预备知识 构造函数的属性 1 2 3 4 5 6 7 function A (name ) { this .name = name; this .arr = [1 ]; this .say = function ( ) { console .log('hello' ); } };
原型对象 每个函数都有prototype属性,它就是原型对象,它存储了实例共享的方法和属性,同时有一个构造函数指向函数本身
通过函数实例化出来的对象有–proto–属性指向原型对象
1 2 3 4 5 6 7 let a = new A(); a.__proto__===A.prototype;
原型对象的作用 原型对象的用途是为每个实例对象存储共享的方法和属性,它仅仅是一个普通对象而已,并且所有的实例共享同一个原型对象,因此有别于实例方法或属性,原型对象仅有一份,而实例有很多份,且实例属性和方法是独立的。
在构造函数中,为了属性(实例基本属性)的私有性,以及方法(实例引用属性)的复用、共享,我们提倡:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function A (name ) { this .name = name; this .arr = [1 ]; }; A.prototype.say = function ( ) { console .log('hello' ); }
五种JS继承方式 原型链继承
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 function Parent (name ) { this .name = name||'父亲' ; this .arr = [1 ]; } Parent.prototype.say = function ( ) { console .log('hello' ); }function Child (like ) { this .like = like; } Child.prototype = new Parent; Child.prototype.constructor = Child; let boy1 = new Child();let boy2 = new Child();console .log(boy1.say===boy2.say);console .log(boy1.name,boy2.name,boy1.name===boy2.name) boy1.arr.push(2 );console .log(boy2.arr);
借用构造函数
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 function Parent (name ) { this .name = name||'父亲' ; this .arr = [1 ]; this .say = function ( ) { console .log('hello' ); } }function Child (name,like ) { Parent.call(this ,name); this .like = like; }let boy1 = new Child('小红' ,'apple' );let boy2 = new Child('小明' ,'orange' );console .log(boy1.name,boy2.name); boy1.arr.push(2 );console .log(boy2.arr);console .log(boy1.arr);console .log(boy1.say===boy2.say) Parent.prototype.walk= function ( ) { console .log('我会走路' ); }console .log(boy1.walk)
组合继承
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 38 39 function Parent (name ) { this .name = name||'父亲' ; this .arr = [1 ]; } Parent.prototype.say = function ( ) { console .log('hello' ); }function Child (name,like ) { Parent.call(this ,name); this .like = like; } Child.prototype = new Parent() Child.prototype.constructor = Child let boy1 = new Child('小红' ,'apple' );let boy2 = new Child('小明' ,'orange' );console .log(boy1.name,boy1.like);console .log(boy2.name,boy2.like); boy1.arr.push(2 ); boy1.arr.push(2 );console .log(boy1.arr);console .log(boy2.arr);console .log(boy1.say===boy2.say) Parent.prototype.walk= function ( ) { console .log('我会走路' ); } boy1.walk();
组合继承优化1
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 38 39 function Parent (name ) { this .name = name||'父亲' ; this .arr = [1 ]; } Parent.prototype.say = function ( ) { console .log('hello' ); }function Child (name,like ) { Parent.call(this ,name); this .like = like; } Child.prototype = Parent.prototype Child.prototype.constructor = Child let boy1 = new Child('小红' ,'apple' );let boy2 = new Child('小明' ,'orange' );console .log(boy1.name,boy1.like);console .log(boy2.name,boy2.like); boy1.arr.push(2 ); boy1.arr.push(2 );console .log(boy1.arr);console .log(boy2.arr);console .log(boy1.say===boy2.say) Parent.prototype.walk= function ( ) { console .log('我会走路' ); } boy1.walk();console .log(Parent.prototype.constructor);
组合继承优化2(寄生组合继承) 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 38 39 40 41 42 43 function Parent (name ) { this .name = name||'父亲' ; this .arr = [1 ]; } Parent.prototype.say = function ( ) { console .log('hello' ); }function Child (name,like ) { Parent.call(this ,name); this .like = like; } Child.prototype = Object .create(Parent.prototype) Child.prototype.constructor = Child let boy1 = new Child('小红' ,'apple' );let boy2 = new Child('小明' ,'orange' );let p1 = new Parent('father' );console .log(boy1.name,boy1.like);console .log(boy2.name,boy2.like); boy1.arr.push(2 ); boy1.arr.push(2 );console .log(boy1.arr);console .log(boy2.arr);console .log(boy1.say===boy2.say) Parent.prototype.walk= function ( ) { console .log('我会走路' ); } boy1.walk();console .log(p1.constructor); console .log(boy1.constructor)
总结 仅仅通过原型链继承的话,子类会共享父类的引用属性,而且不能向父类的构造函数传参
而仅仅通过构造函数继承的话,实例不能共享父类相同的方法、不能继承父类原型上的属性
通过组合继承可以规避上面的缺点,但是如果把实例赋值给原型,会创建两份父类实例
而如果把父类原型直接赋给子类原型,会改变父类构造函数的指向
于是想到用Object.create创建一个新的原型再赋值,就避免了两份实例和改变指向的问题