js 中的继承

继承就是让一个对象拥有另一个对象的属性和方法

一、原型链继承(两种)

  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]
// 说明不同实例之间数据不相通,可复用

 

 

 

 

    

 

  

posted @ 2020-05-01 10:27  只争朝夕,不负韶华  阅读(216)  评论(0编辑  收藏  举报