JavaScript的原型继承和ES6的class语法
回顾一下JavaScript的原型链和原型继承及ES6的Class语法。
原型链
JavaScript中的每个对象,都有一个特殊内置的_proto_
属性。这个属性是编程不可见的(虽然ES6标准中开放了这个属性,然而浏览器对这个属性的可见性的支持不同),它实际上是对另一个对象或者null
的引用。几乎所有的对象在创建时_proto_
属性都会被赋予一个非空的值 。
当一个对象需要引用一个属性时,JavaScript引擎首先会从这个对象自身的属性表中寻找这个属性标识,如果找到则进行相应读写操作,若没有在自身的属性表中找到,则在_proto_
属性引用的对象的属性表中查找,如此往复,直到找到这个属性或者_proto_
属性指向null
为止。最后会找到Object对象上,如果还没有,那么就返回undefined 。
这个_proto_
的引用链,被称作原型链。
注意,此处有一个性能优化的问题:往原型链越深处搜索,耗费的时间越多。
原型继承实现的几种方式
在javaScript中,可以说一切皆对象。它并没有‘’类‘’这个概念,它跟Java等语言不同,在ES6标准出炉之前,它是没有类的定义的。对于在javaScript中提出类的概念,只是那些熟悉Java强类型语言的使用者,希望JavaScript中能通过类生成实例,通过子类复用代码。那在ES6中class的语法糖提出前,是怎么做到的???
原型链继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| function Parent () { this.name = 'kevin'; }
Parent.prototype.getName = function () { console.log(this.name); }
function Child () {
}
Child.prototype = new Parent();
var child1 = new Child();
console.log(child1.getName())
function Parent () { this.names = ['kevin', 'daisy']; }
function Child () {
}
Child.prototype = new Parent();
var child1 = new Child();
child1.names.push('yayu');
console.log(child1.names);
var child2 = new Child();
console.log(child2.names);
|
这种继承会产生一些问题:
1.引用类型的属性被所有实例共享
2.在创建 Child 的实例时,不能向Parent传参
原型式继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| function Foo(name) { this.name = name; } Foo.prototype.myName = function() { return this.name; }; function Bar(name,label) { Foo.call( this, name ); this.label = label; } Bar.prototype = Foo.prototype; Bar.prototype.myLabel = function() { return this.label; };
var a = new Bar( "a", "obj a" ); console.log(Foo.prototype.hasOwnProperty('myLabel')) console.log(a.myName()); console.log(a.myLabel());
|
这种继承是有缺点的,由于Bar.prototype引用了Foo.prototype对象,当执行Bar.prototype.myLabel 的赋值语句会直接修改Foo.prototype对象本身,同时导致包含引用类型的属性值始终都会共享相应的值
组合继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| function Parent (name) { this.name = name; consoel.log('a') }
Parent.prototype.getName = function () { console.log(this.name) }
function Child (name, age) { Parent.call(this, name); this.age = age; }
Child.prototype = new Parent();
var child1 = new Child('koa', '18');
|
缺点:两次调用两次父构造函数。那需要怎么样去让子类调用父类的构造函数一次
改进:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| function Parent (name) { this.name = name; }
Parent.prototype.getName = function () { console.log(this.name) }
function Child (name, age) { Parent.call(this, name); this.age = age; }
var F = function () {};
F.prototype = Parent.prototype;
Child.prototype = new F();
var child1 = new Child('koa', '18');
|
但是这次改进还是存在对象的属性共享的问题:
1 2 3 4 5 6 7 8 9
| Parent.prototype.a = { x: 1};
.......
var child1 = new Child('kevin', '18');
console.log(Parent.prototype.a ) child1.a.x = 2; console.log(Parent.prototype.a )
|
在ES5中,还可通过Object.create()来实现继承:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| function Parent (name) { this.name = name; }
Parent.prototype.getName = function () { console.log(this.name) }
function Child (name, age) { Parent.call(this, name); this.age = age; }
Child.prototype = Object.create( Parent.prototype );
var child1 = new Child('koa', '18');
|
ES6的Class
到此让我们来迎接ES6吧!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class Parent { constructor(name) { this.name = name; } getName() { console.log(this.name) } }
class Children extends Parent { constructor(name) { super(name); this.age = age; } }
var child1 = new Child('koa', '18');
|
本文由 Abert 创作,采用 知识共享署名 4.0 国际许可协议。
本站文章除注明转载/出处外,均为本站原创或翻译,转载请务必署名。