继承就是让一个对象拥有另一个对象的属性和方法
一、原型链继承(两种)
1、Son.prototype = Father.prototype (原型继承)
(1)Son的实例对象只能继承Father实例中原型的方法,无法继承Father本身的属性。
function Father(){ this.age = 50; this.h = 15 } Father.prototype.eat = function(){ console.log(this.age); // 25 console.log(this.h); // undefined } function Son(){ this.age = 25; } Son.prototype = Father.prototype; var son = new Son() son.eat() // son继承Father原型中的eat函数,所以this.age指向Son,故 25 // this.h 弹出undefined 说明无法继承Father本身的属性
2、Son.prototype = new Father() (实例化对象继承)
原理:就是把构造函数Father()的实例化对象father的属性和方法继承到构造函数Son()的原型上。修改的属性是实例化对象father的属性,每次实例化一个构造函数Son(),都是从father上继承,故属性相通。
(1)优先查找自身属性,如果有的话优先使用,如果没有则从继承中查找
(2)继承的构造函数的不同实例化对象的属性不会互相影响;不同的Son实例对象会共享继承Father的实例化对象father的属性。这样的话修改一个Son实例化对象son的arr,Son实例化对象son1的arr也会改变,修改被继承Fathe实例化对象father的属性时会影响继承者Son继承的属性
function Father(){ this.age = 50; this.arr = [1,2,3] this.h = 15; } Father.prototype.eat = function(){ console.log(this.age); console.log(this.h); } function Son(){ this.age = 25; } var father = new Father(); Son.prototype = father; var son = new Son(); son.eat(); // 25 说明当son优先查找自身属性 // 15 说明son继承Father实例对象的属性 son.arr.push(4) console.log(son.arr); // [1,2,3,4] console.log(father.arr) // [1,2,3] var son1 = new Son() console.log(son1.arr);// [1,2,3,4] 说明不同的Son实例对象会共享继承Father的实例化对象father的属性 father.arr.push(5) console.log(father.arr)// [1,2,3,5] console.log(son.arr);// [1,2,3,4,5] 说明修改被继承Fathe实例化对象father的属性时会影响继承者Son继承的属性
对于继承后 Son.prototype.constructor指向的问题,是否需要修改可以查看 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor
二、构造函数继承
在继承构造函数里通过call或者apply或bind修改被继承函数的this。
1、继承属性会覆盖自身属性,
2、继承的构造函数的不同实例化对象的属性不会互相影响
3、无法继承原型上的方法,
function Father(){ this.age = 50; this.hello = "你好"; this.arr = [1,2,3]; } Father.prototype.cat = function(){ console.log(cat); } function Son(){ // 三种方式 this.age = 25; Father.call(this); // Father.bind(this)(); // Father.apply(this); } var son = new Son(15) console.log(son.age); // 50 // 说明继承属性会覆盖自身属性 console.log(son.hello); // 你好 try{ son.cat() }catch(e){ console.log(e); // TypeError: son.cat is not a function } son.arr.push(4); console.log(son.arr); // [1,2,3,4] var son1 = new Son(25); console.log(son1.arr); // [1,2,3] // 这说明构造函数Son的不同实例化对象的属性是不相通,修改实例化对象
三、组合式继承 (原型链与构造函数)
在原型链继承中Son.prototype = Father.prototype只能继承Fanther的原型上的方法,在构造函数继承中,Fathe.call(this)只能继承构造函数的属性,两者结合。
1、继承属性会覆盖自身属性
2、继承的构造函数的不同实例化对象的属性不会互相影响
function Father(){ this.age = 50; this.hello = "你好"; this.arr = [1,2,3]; }; Father.prototype.eat=function(){ console.log("这是Father构造函数原型上方法"); } function Son(){ this.age = 25; Father.call(this) }; Son.prototype = Father.prototype; var son = new Son(); console.log(son.age); // 50 说明继承的属性会覆盖自身的属性 console.log(son.hello); //你好 son.arr.push(4) son.eat() var son1 = new Son(); console.log(son.arr); //[1,2,3,4] console.log(son1.arr); // [1,2,3] // 说明同一构造函数的不同实例化对象之间属性不相通;
弊端:通过Father.call() 和Father.prototype ,父类构造函数Father被调用了两次。继承属性会覆盖原属性
四、原型式继承 (个人认为这种方式和原型链Son.prototype = new Father()的原理是一致的,各种表现也是一致)
1、优先查找自身属性,如果有的话优先使用,如果没有则从继承中查找
2、继承的构造函数的不同实例化对象的属性不会互相影响;
function Father(){ this.age = 50; this.hello = "你好"; this.arr = [1,2,3] }; Father.prototype.eat = function(){ console.log("这是Father原型上的方法"); } function content(obj){ function Son(){ this.age = 25; }; Son.prototype = obj; // 传入继承构造函数Father的实例化对象father return new Son(); } var father = new Father(); var son = content(father); console.log(son.hello); //你好 说明继承构造函数Father实例化对象father上的属性。 console.log(son.age); // 25 // 说明优先查找自身是否有某属性,有的话优先调用自身属性 son.eat() // 这是Father原型上的方法 说明继承了构造函数Father实例化对象father上的方法 son.arr.push(4) var son1 = content(father) console.log(son.arr); // [1,2,3,4] console.log(son1.arr); // [1,2,3,4] 说明这个不同实例间继承的属性是相通的
五、寄生式继承(在原型式继承的基础上,对Son实例化的外面添加函数封装而已,表现一致)
1、优先查找自身属性,如果有的话优先使用,如果没有则从继承中查找
2、继承的构造函数的不同实例化对象的属性不会互相影响;
function Father(){ this.age = 50; this.hello = "你好"; this.arr = [1,2,4]; }; Father.prototype.eat = function(){ console.log("这是Father构造函数原型上的方法"); } function content(obj){ function Son(){ this.age = 25; }; Son.prototype = obj; return new Son(); } function subObject(obj){ var son = content(obj) son.name = 'son 名字' // 可直接对son添加属性 return son } var father = new Father() var son1 = subObject(father) console.log(son1.age);//25 console.log(son1.hello);//你好 console.log(son1.name); // son 名字 son1.eat(); // 这是构造函数原型上的方法 son1.arr.push(4) var son2 = content(father) console.log(son1.arr); // [1,2,3,4] console.log(son2.arr); // [1,2,3,4] 说明这个不同实例间继承的属性是相通的
六、寄生组合式继承 (构造函数继承与寄生式继承组合)
1、寄生属性会覆盖原属性
2、继承的构造函数的不同实例化对象的属性不会互相影响
3、与组合式继承相比效果相同,但是更加复杂。
function Father(){ this.age = 50; this.hello = "你好"; this.arr = [1,2,3]; }; function content(obj){ function Son(){ this.age = 25; }; Son.prototype = obj; return new Son(); }; var son = content(Father.prototype) // var son = content(new Father()) 效果一致 function Sub(){ Father.call(this); // Father.apple(this); // Father.bind(this)(); }; Sub.prototype = son; son.constructor = Sub; var sub = new Sub(); console.log(sub.age); // 50 说明继承的属性会覆盖自身的属性 console.log(sub.hello); // 你好 sub.arr.push(4) console.log(sub.arr); // [1,2,3,4] var sub1 = new Sub(); console.log(sub1.arr); // [1,2,3] // 说明同一构造函数的不同实例化对象之间属性不相通;
七、Object.assign()
与组合式继承基本相同,除了Son.prototype = Object.create(Father.prototype),对原型继承的方式不一样
1、寄生属性会覆盖原属性
2、继承的构造函数的不同实例化对象的属性不会互相影响
// Father - 父类(superclass) function Father() { this.age = 50; this.arr = [1,2,3] } // 父类的方法 Father.prototype.move = function(x, y) { console.info('Father moved.'); }; // Son - 子类(subclass) function Son() { this.age = 25; Father.call(this); // call super constructor. } // 子类续承父类 Son.prototype = Object.create(Father.prototype); // Son.prototype.constructor = Son; var son = new Son(); console.log(son.age); // 50 说明继承属性覆盖原属性 son.move(); // Father moved son.arr.push(4) console.log(son.arr); // [1,2,3,4] var son1 = new Son(); console.log(son1.arr); // [1,2,3] // 说明不同实例之间数据不相通,可复用