Javascript高级程序设计读书笔记——继承

3. 继承

     对于继承,其实我是拒绝的!!看的时候懂了,用的时候懵逼。。。表示go die。。这次按照自己的理解把继承的几种方式记录下来,再忘记的话我就可以直接回家养老了。

3.1 原型链

主要思想: 利用prototype属性重写原型对象。使一个引用类型集成另一个引用类型的属性和方法。

function Supertype(){
    this.name = 'supertype';
}
Supertype.prototype.getName = function(){
    console.log(this.name);
}
function Subtype(){
    this.subname = 'subtype';
}
Subtype.prototype = new Supertype();

Subtype.prototype.getSubName = function(){
    console.log(this.subname);
}
var instance = new Subtype();

console.log(instance.getName());  // supertype
console.log(instance.getSubName()); // subtype

 以上可以看到,instance 继承了 subtype 和 supertype 的方法和属性。

思考题来啦~

 1. 我把这两个语句换一下位置,会出现什么情况?

Subtype.prototype.getSubName = function(){
    console.log(this.subname);
}

Subtype.prototype = new Supertype();

 2. 如果我通过这种方式添加 getSubName 方法会出现什么情况?

Subtype.prototype = {
    getSubName: function(){
        console.log(this.subname);
    }
}

 3. 试着自己画一个以上例子的原型链~
 

答案来啦:

 1. 没错,更换位置后会报错~这是因为在重写 subtype 的原型之前, Subtype.prototype 指向默认原型,那么 getSubName 自然是定义在默认原型上。接下来我们重写了原型,Subtype.prototype 指向了 Supertype 的实例 ,而在其上我们并没有定义 getSubName(),所以在调用该方法的时候就会报错啦。

 2. 在我们使用字面量的方式赋值的时候,Subtype.prototype就不在是按照我们设想的是指向Supertype了,也就是说它会切断原型链。那么instance可以说和Supertype完全无关~

  3.

 

    实践中很少单独使用原型链。主要是因为原型链继承存在两个缺点: 1. 数据共享。原型中的引用类型值被其中一个实例改变的时候,其他所有实例的该属性值也会随之改变。 2. 没有办法在不影响其他实例的情况下,给超类型的构造函数传参。

function Supertype(){
    this.color = ['red', 'blur', 'green']; // 引用类型值哦~
}
function Subtype(){
}
Subtype.prototype = new Supertype(); // 重写subtype的原型~

var instance1 = new Subtype();
var instance2 = new Subtype();

instance1.color.push('yellow');

console.log(instance1.color); // ['red', 'blur', 'green', 'yellow']
console.log(instance2.color); // ['red', 'blur', 'green', 'yellow']

 
    再来思考一下,超类型的color属性被所有实例共享,如果我们通过 Subtype 实现对超类型的构造函数传参,能做到不影响哪个其他实例嘛?在来想一下,为什么color是引用类型值的时候出现这种情况,是字符串可以吗?自己去试一下哦~

3.2 借用构造函数

主要思想: 在Subtype函数的内部调用Supertype构造函数

function Supertype(){
    this.color = ['red', 'blue', 'green'];
}
function Subtype(){
    Supertype.call(this);
}

var instance1 = new Subtype();
var instance2 = new Subtype();

instance1.color.push('yellow');

console.log(instanc1.color);  // "red", "blue", "green", "yellow"
console.log(instance2.color); // "red", "blue", "green"

 
    实际上,我们这次是在Subtype函数内部调用了 Supertype,从而实现每次初始化 Subtype函数,实例都会拥有一个自己的color。
    
    借用构造函数继承虽然解决了原型链继承的问题: 参数传递和数据共享。但它实际上只是一个伪对象继承。

    缺点: 函数无法实现复用。

3.3 组合继承
   
    又叫做伪经典继承。它将原型链继承和借生构造函数结合起来。通过借生构造函数进行属性的继承,而通过原型链实现方法的复用。

function Supertype(name){
    this.name = name;
    this.color = ['red', 'blue', 'yellow'];
}
function Subtype(name){
    Supertype.call(this, name);
}
Subtype.prototype = new Supertype();

Subtype.prototype.getColor = function(){
    console.log(this.color);
}

    
    哈~我们终于实现了传参,属性不共享,方法复用~ 在实践中,组合继承是最常用的继承方法。但它也有不足,想想看能不能想到呢?

     它的缺点就是无论什么情况,它都会调用两次超类型构造函数。一次是在重写 Subtype 原型的时候,一次是在调用 Subtype 构造函数的时候。

3.4 原型式继承

主要思想: 基于已经存在的对象创建新对象

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

var baseObj = {
    name: 'colors',
    color: ['red', 'blue'], 
}

var instance1 = object(baseObj);
var instance2 = object(baseObj);

instance1.color.push('green');

console.log(instance1.color); // "red", "blue", "green"
console.log(instance2.color); // "red", "blue", "green"

    好啦,这就是原型式继承。看到这里有没有想到 ES5 里的一个方法呢——Object.create()。就是这个方法规范化了原型式继承。

    当我们在只是简单地想要构造两个比较相似的对象的时候,使用原型式继承还是很方便的。

    实际上,原型式继承只是创建了两个baseObj的副本。如果把这个过程看做是复制的话,那么它执行的只是简单的浅复制~毕竟引用类型值依旧是共享的。

 
3.5 寄生式继承
 
    主要思想:创建一个用于封装继承过程的函数,并在函数内部对对象进行增强。思路有些类似工厂模式和寄生构造函数模式

function createObj(o) {
    var clone = object(o); // 创建一个新对象
    clone.sayHi = function(){
        console.log('hi');
    };
    return clone;
}

var instance1 = createObj(baseObj);

instance1.sayHi(); // hi

    通过以上方式,可以使返回的新对象不仅有baseObj的属性和方法,自己也拥有方法。但是类似于构造函数模式,它无法实现方法复用。

3.6 寄生组合式继承

    我们经常用的继承方式是组合继承,但它也有缺点,就是会调用两次超类型构造函数。现在寄生组合式继承弥补了该缺点。

function inheritPrototype(subtype, supertype){
    var ob = object(supertype);
    ob.constructor = subtype;
    subtype.prototype = ob;
}

function Supertype(name){
    this.name = name;
    this.color = ['red', 'blue'];
}
Supertype.prototype.getName = function(){
    console.log(this.name);
}
function Subtype(){
    Supertype.call(this, name);
}

inheritPrototype(Subtype, Supertype);  // 与组合继承的不同点
// Subtype.prototype = new Supertype();  // 组合继承中的语句

Subtype.prototype.sayHi = function(){
    console.log('hi');
};

    看到了嘛?不同点就在于组合继承中,subtype指向Supertype的实例,在这个过程中调用了一次构造函数。而在寄生组合式继承中,使用 object() 方法创建了 Supertype 的一个副本,通过该副本实现继承。没有调用Supertype构造函数~

有兴趣的话可以把几种继承方式的原型链都画一下。反正我是在纸上画了。。电脑画图好慢。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值