6种继承方式的复习(深入了解2)

1.原型链式继承
让子原型对象指向父的实例,构建原型链。

Father.prototype.LastName = "kang"
function Father(){

}
var father = new Father();
Son.prototype = father;
(Son,prototype = new Father)
function Son(){

}
var son = new Son()
console.log(son.LastName)

儿子继承了父亲原型上的方法LastName.

那么如果说我要给儿子添加一个新方法,或者给父亲重写方法呢?

Father.prototype.LastName = "kang"
function Father(){

}
var father = new Father();
Son.prototype = father;
function Son(){

}
给Son添加新的方法
Son.prototype.name = "jiahao";
var son = new Son();
console.log(son.name)  //"jiahao"
给Father重写方法
Father.prototype.LastName = "wang"
var son = new Son();  
console.log(son.LastName)  //"kang"

阅读红宝书你会发现,这两种修改方法都放在了最后边,也就是说在放在把儿子原型指向父亲实例的后面。
如果说放在前面呢?

Father.prototype.LastName = "kang"
function Father(){

}
var father = new Father();
Son.prototype.name = "jiahao";
Son.prototype = father;
function Son(){

}
// 给Son添加新的方法

var son = new Son();
console.log(son.name)  //"undefined"

这个时候访问son.name返回undefined。
为什么呢?

Father.prototype.LastName = "kang"
function Father(){

}
var father = new Father();
Son.prototype.name = "jiahao";
console.log(Son.prototype)
Son.prototype = father;
console.log(Son.prototype)
function Son(){

}
// 给Son添加新的方法

var son = new Son();
console.log(son.name)  //"undefined"

分别打印两个son.prototype可以看出:
在这里插入图片描述
两个Son.prototype 返回的并不一样,指向空间发生改变。
因此为了不出现这种错误,添加新方法,和重写方法,都要放在最后面来进行。

不能用对象字面量的方法来添加方法:

对象字面量的写法相当于重写原型,为形成对比
1.普遍写法:

Cat.prototype.name = "jerry"
function Cat(){

}
var littleCat = new Cat();
Cat.prototype.name = "tom"
console.log(Cat.prototype.constructor == Cat)  //true
console.log(Cat.prototype.isPrototypeOf(littleCat))   //  true   原型对象在littleCat对象原型链上

2.对象字面量写法

Cat.prototype.name = "jerry"
function Cat(){

}
var littleCat = new Cat();
 Cat.prototype = {
     name:"tom"
 }
console.log(Cat.prototype.constructor == Cat)  //false
console.log(Cat.prototype.isPrototypeOf(littleCat)) //false  //原型对象不在littleCat对象原型链上

再举个例子你很容易明白:

Cat.prototype.name = "jerry"
function Cat(){

}
var littleCat = new Cat();
Cat2.prototype = littleCat;

function Cat2(){

}
Cat2.prototype = {
    age:"tom2"
}
var littleCat2 = new Cat2()
console.log(littleCat2.name)  //undefined

对象字面量的写法相当于重写原型链,重写后意味着Cat.prototype指向了一个新的原型对象,切断了Cat 和 Cat2的联系。像本例一种一样,father和son的联系被切断了。
当然你可以在重写的过程中显示的修改constructor的指向。

原型链式继承的缺点:
1.过多继承了没有用的方法:

//假如说我只想继承Father的lastname,在原型链式继承上会自动继承了Father的sex方法;
Father.prototype.lastname = "kang"
function Father(){
    this.sex = "man"
}
Son.prototype = new Father();
function Son(){

}
var son = new Son();

2.包含引用值类型的原型属性会称为所有实例的共享属性。

Father.prototype.jobs = ["tencent","alibaba","baidu"] 
function Father(){
    
}
Son.prototype = new Father();
function Son(){

}
var son = new Son();
son.jobs.push('meituan');
console.log(son.jobs);  //(4) ["tencent", "alibaba", "baidu", "meituan"]  没有问题
var son2 = new Son();
console.log(son2.jobs);  //(4) ["tencent", "alibaba", "baidu", "meituan"]  这里son2直接共享了jobs属性。

3.在创建儿子类型的实例时,不能向父亲类型的构造函数传递参数。

2.借用构造函数实现继承。(伪造对象或者经典继承)

function Father(name,age,hobbit){
    this.name = name;
    this.age = age;
    this.hobbit = hobbit;
}
function Son(name,age,hobbit,sex){
    this.name = name;
    this.age = age;
    this.hobbit = hobbit;
    this.sex = sex;
}
var son = new Son("kang","19","bianchen","man");

现在我想让儿子继承父亲的name,age,和hobbit。

function Father(name,age,hobbit){
    this.name = name;
    this.age = age;
    this.hobbit = hobbit;
}
function Son(name,age,hobbit,sex){
    Father.call(this,name,age,hobbit)   //使用call()改变了this指向
    this.sex = sex;
}
var son = new Son("kang","19","bianchen","man");

但是父亲的原型链上的属性,儿子继承不了。

Father.prototype.lastname = "kang";
function Father(name,age,hobbit){
    this.name = name;
    this.age = age;
    this.hobbit = hobbit;
}
function Son(name,age,hobbit,sex){
    // this.name = name;
    // this.age = age;
    // this.hobbit = hobbit;
    Father.call(this,name,age,hobbit)
    this.sex = sex;
}
var son = new Son("kang","19","bianchen","man");
console.log(son.lastname)  //undefined

这种继承模式也实现了传递参数的功能。
缺点:相同方法在不同对象的构造函数中都要定义一遍,无法实现函数复用。在超类型原型中定义的方法,对子类型是不可见的,因此所有的超类型的原型属性都不能被继承。

3.组合继承
通过上面两种继承方式,我们可以发现,原型链式继承不能传递参数,而经典继承不能继承父类原型链上的方法,所以组合继承就时由这两种继承方式组成。

//组合继承
Father.prototype.sayName  = function (){
    return this.name
}
function Father(name){
    this.name = name;
    this.colors = ["red","black","white"]
}
Son.prototype = new Father();
Son.prototype.constructor = Son;
function Son(name,age){
    Father.call(this,name);
    this.age = age;
}
var son = new Son("dangyanlin",3);
son.colors.push('yellow');
console.log(son.colors)    //  (4) ["red", "black", "white", "yellow"]
console.log(son.sayName())  //  dangyanlin
console.log(son.age)  // 3

var son2 = new Son();  //没有传进去参数
console.log(son2.colors);  //(3)["red", "black", "white"]

先使用原型继承方式,使得子类可以继承到父类原型上的方法,之后使用经典继承使得子类可以继承父亲身上的方法。为保证 son和 son2 拥有各自的父类属性副本,我们在 Son 构造函数中,还是使用了 Person.call ( this ) 方法,这样他们可以有属于自己的属性,也可以使用相同的方法了。
如此,结合原型链继承和借用构造函数继承,就完美地解决了之前这二者各自表现出来的缺点。
组合继承解决原型链继承的引用类型原型属性被实例共享问题。(call())
缺点:两次调用构造函数,效率低。

4.原型式继承

function Object(o){
    function F();
    F.prototype = o;
    return new F();
}

原型式继承本质其实就是个浅拷贝,以一个对象为模板复制出新的对象

function Object(o){
    function F(){}
    F.prototype = o;
    return new F();
}
var obj = {
    name:'kang',
    sex:'man',
    saymymessage:function(){
        return this.name + this.sex;
    }
}
var obj2 = Object(obj);
console.log(obj2.name)  //"kang"
console.log(obj2.sex)   //"man"
console.log(obj2.saymymessage())  //"kangman"

因为是个浅拷贝的原因,这种继承模式的实例会共享引用数据。

function Object(o){
    function F(){}
    F.prototype = o;
    return new F();
}
var obj = {
    name:'kang',
    sex:'man',
    skills:['唱','跳','rap'],
    saymymessage:function(){
        return this.name + this.sex;
    }
}
var obj2 = Object(obj);
var obj3 = Object(obj);
obj2.skills.push('篮球');
console.log(obj2.skills);   //  (4) ["唱", "跳", "rap", "篮球"]
console.log(obj3.skills)    //  (4) ["唱", "跳", "rap", "篮球"]

ES5中新增了Object.create()方法规范了原型式继承。

var obj1 = {
    skills:['唱','跳','rap']
}
var obj2 = Object.create(obj1);
obj2.skills.push('篮球')
console.log(obj2.skills);

Object.create()有两个参数,第一个继承别人的方法,第二个设置自己的方法;第二种方法设置的属性会覆盖原型上的属性。

var obj1 = {
    skills:['唱','跳','rap'],
    name:“wang”
}
var obj2 = Object.create(obj1,{
    name:{
        value:'kang'
    }
});
obj2.skills.push('篮球')
console.log(obj2.skills);
console.log(obj2.name)  //"kang"

5.寄生式继承。
寄生式继承就是把原型式继承再次封装,然后在对象上扩展新的方法,再把新对象返回

//寄生式继承
function object(o){
    function F(){}    //创建了一个对象
    F.prototype = o;  //将这个对象原型指向传入的函数 
    return new F();   //返回一个对象的新实例  其实就是 传入对象的一个实例
}
function createAnother(original){
    var clone = object(original);  // clone是original的一个实例
    clone.sayHi = function(){      //增强这个对象
         alert("Hi");
    }
    return clone;                  //返回这个对象
}

var obj = {
    name:'kang',
    age:'19'
}
var obj2 = createAnother(obj);

缺点:类似于构造函数继承,不能实现函数复用。
6.寄生组合式继承
组合继承的缺点:

Father.prototype.sex = "man"
function Father(name){
    this.name = name
}
Son.prototype = new Father();   //-->调用Father构造函数
Son.prototype.constructor = Son;
function Son(name,age){
    Father.call(this,name)   //-->调用Father构造函数
    this.age = age
}
var son1 = new Son("kang",12);

两次调用父类构造函数,效率低。

寄生组合式:通过构造函数继承属性,通过原型链的混成形式来继承方法,不必为了指定子类型的原型而调用父类构造函数,本质上是使用寄生式继承来继承夫类型的原型。

//父类构造函数
function Super(name){
    this.name = name;
    this.funcName = "Super";
    this.func = "super";
}
Super.prototype.sayName = function (){ alert(this.name); }
Super.prototype.sayFuncName = function (){ alert(this.funcName); }

//子类构造函数
function Sub(name,age){
    Super.call(this,name);
    this.age = age;
    this.funcName = "Sub";
}

//继承函数
function inheritPrototype(Sub,Super){
    //中间实例化对象
    var a = Object(Super.prototype);
    //重写该对象的构造函数指针,指向Sub函数
    a.constructor = Sub;
    //Sub的原型指向实例化对象a
    Sub.prototype = a;
}
//调用继承函数,实现原型链构造
inheritPrototype(Sub,Super);

/*
----------一定要在调用继承函数之后添加,如果放在继承函数之前,随着原型的替换,函数会undefined。----------
*/
//添加子类方法
Sub.prototype.sayAge = function (){ alert(this.age); }
Sub.prototype.sayFuncName = function (){ alert(this.funcName); }

//测试代码
var subTest = new Sub("lulu",20);
subTest.sayName(); //lulu
subTest.sayAge(); //20
subTest.sayFuncName(); //Sub
alert(subTest.func); //super




里面有部分博主的个人理解,博主也是个萌新,错了希望大家指出来,我再接再厉继续改进。
thanks!



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值