We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
JavaScript 语言最初只是一门脚本语言,是没有类class和继承extends的概念的。不过这些都可以依靠 JavaScript 基于 原型 这一特性来模拟实现。
class
extends
ES6中引入了class和extends关键字,但那只是语法糖,JavaScript 仍然是基于原型的。
本文介绍几种 JavaScript 继承的实现方式。首先我们来创建一个"父类"。
function Animal(name = 'Animal', voice){ this.name = name; this.voice = voice; this.action = ['say']; } Animal.prototype.isAnimal = true; Animal.prototype.say = function (){ console.log(`The ${this.name} saying: ${this.voice}!`) }
function Dog(name){ this.name = name; } Dog.prototype = new Animal(); Dog.prototype.constructor = Dog; //修复子类构造函数 var tom = new Dog('tom'); console.log(tom.name); // "tom" console.log(tom.voice); // undefined console.log(tom.action); // ["say"] tom.say(); // The tom saying: undefined! var bob = new Dog('bob'); bob.action.push('sleep'); console.log(bob.action); // ["say", "sleep"] console.log(tom.action); // ["say", "sleep"]
function Dog(name){ Animal.call(this, name, 'WONNG') this.name = name; } var tom = new Dog('tom'); console.log(tom.name); // "tom" console.log(tom.voice); // "WONNG" console.log(tom.action); // ["say"] console.log(tom.say); // undefined var bob = new Dog('bob'); bob.action.push('sleep'); console.log(bob.action); // ["say", "sleep"] console.log(tom.action); // ["say"]
function Dog(name){ Animal.call(this, name, 'WONNG') this.name = name; } Dog.prototype = new Animal(); Dog.prototype.constructor = Dog; //修复子类构造函数 var tom = new Dog('tom'); console.log(tom.name); // "tom" console.log(tom.voice); // "WONNG" console.log(tom.action); // ["say"] tom.say(); // The tom saying: WONNG! var bob = new Dog('bob'); bob.action.push('sleep'); console.log(bob.action); // ["say", "sleep"] console.log(tom.action); // ["say"]
至此组合继承已经近乎完美的实现了 JavaScript 的继承,但是还是不够优雅,因为在组合继承的实现中,子类原型上的父类实例是多余的。
console.log(bob); // {name: "bob", voice: "WONNG", action: ["say", "sleep"]} console.log(bob.__proto__); // {name: "Animal", voice: undefined, action: ["say"], constructor: ƒ Dog(name)}
可以看到bob.__proto__上的name voice action属性都是多余的,甚至当我们删除bob的某个属性时,可能会从子类原型上得到意料之外的父类实例属性。
bob.__proto__
name
voice
action
bob
delete bob.name console.log(bob.name); // "Animal"
所以下面介绍另一个继承方式来解决这个问题。
function Dog(name){ Animal.call(this, name, 'WONNG') this.name = name; } function Mid(){} // 没有实例属性的中间类 Mid.prototype = Animal.prototype; Dog.prototype = new Mid(); Dog.prototype.constructor = Dog; // 修复子类构造函数 var tom = new Dog('tom'); console.log(tom.name); // "tom" console.log(tom.voice); // "WONNG" console.log(tom.action); // ["say"] tom.say(); // The tom saying: WONNG! var bob = new Dog('bob'); bob.action.push('sleep'); console.log(bob.action); // ["say", "sleep"] console.log(tom.action); // ["say"] console.log(bob); // {name: "bob", voice: "WONNG", action: ["say", "sleep"]} console.log(bob.__proto__); // {constructor: ƒ Dog(name)}
这种方式就是现阶段很完美,也是最常用的继承方式了。代码上还可以根据各种情况优化一下,比如将 call 改成 apply,我们就可以用原生的 arguments 数组更方便的向父类的构造函数传参, 定义中间类的时候也可以用一个立即执行的匿名函数包裹一下,让中间类处在函数的局部作用域,防止全局作用域下的同名变量或者函数污染。
The text was updated successfully, but these errors were encountered:
No branches or pull requests
JavaScript 继承
JavaScript 语言最初只是一门脚本语言,是没有类
class
和继承extends
的概念的。不过这些都可以依靠 JavaScript 基于 原型 这一特性来模拟实现。本文介绍几种 JavaScript 继承的实现方式。首先我们来创建一个"父类"。
1.原型继承
2.构造函数继承
3.组合继承
至此组合继承已经近乎完美的实现了 JavaScript 的继承,但是还是不够优雅,因为在组合继承的实现中,子类原型上的父类实例是多余的。
可以看到
bob.__proto__
上的name
voice
action
属性都是多余的,甚至当我们删除bob
的某个属性时,可能会从子类原型上得到意料之外的父类实例属性。所以下面介绍另一个继承方式来解决这个问题。
4.寄生组合继承
这种方式就是现阶段很完美,也是最常用的继承方式了。代码上还可以根据各种情况优化一下,比如将 call 改成 apply,我们就可以用原生的 arguments 数组更方便的向父类的构造函数传参,
定义中间类的时候也可以用一个立即执行的匿名函数包裹一下,让中间类处在函数的局部作用域,防止全局作用域下的同名变量或者函数污染。
The text was updated successfully, but these errors were encountered: