JavaScript里面的坑(二)——读《JavaScript高级程序设计》有感

上一次写blog写了JavaScript里面对象这个坑,今天就来讲一下JavaScript里面的继承。
众所周知,许多OO语言都支持两种继承方式:接口继承和实现继承。接口继承只继承方法签名,实现继承则继承实际的方法。而由于js里面函数没有签名,所以ECMAScript里面只能支持实现继承,而且主要是依靠原型链来实现的。

  • 原型链实现继承:
    构造函数有一个原型对象,而原型对象都有一个指针指向构造函数,实例也有一个指针指向原型对象。那么如果原型对象是另一个类型的实例,然后那个类型的原型对象又是另一个类型的实例……这样层层递进下去,不就实现继承了吗?
function Super() {
    this.property = true;
};
Super.prototype.getSuperValue = function() {
    return this.property;
};

function Sub() {
    this.subproperty = false;
};
Sub.prototype = new Super();
Sub.prototype.getSubValue = function() {
    return this.subproperty;
};

var instance = new Sub();
console.log(instance.getSuperValue());//true

在这里,Sub继承了Super。这个继承实现的本质其实是我们重写了Sub的原型对象,代之以一个Super的实例。也正是因此,我们实现了继承。
【使用这种方式的时候一定要注意:因为这个继承的本质是重写,因此一定要在替换原型之后再给原型添加方法。】
但是这种方式实现的继承有两个问题:第一个就是引用类型的原型属性会被所有的实例共享【←还记得上篇文章里面的数组吗】,于是你修改了一个实例中的引用类型的属性,就会发现其他的实例也受到了影响。
第二个问题就是你没法在不影响所有对象实例的情况下,向Super传递参数。
因此我们想出了第二种方法:

  • 借用构造函数
    首先,我们得介绍一下call()和apply()这两个函数——它们的作用是扩充函数的作用域,比如这段代码:
window.color = 1;
var o = {this.color = 2};

function sayColor() {
    console.log(this.color);
};
sayColor.call(this);//1
sayColor.call(window);//1
sayColor.call(o);//2

如上所示,call()接受的第一个参数就是函数将要在其中运行的作用域。正是因为这个方法,我们可以使引用类型的数据在继承中不受影响:

function Super() {
    this.color = [1, 2, 3];
}

function Sub() {
    Super.call(this);
}

var test1 = new Sub();
test1.color.push(4);
console.log(test1.color);//1,2,3,4

var test2 = new Sub();
console.log(test2.color);//1,2,3

在这里,通过call(),我们实际上是在(未来将要)新创建的Sub实例的环境下调用了Super构造函数,这样就会在新实例上指向Super()函数中定义的所有对象初始化代码,于是每个实例都有一个属于自己的color副本。
而且借助这个方法,我们也可以传递参数了。
但是和构造对象方法里面的构造函数模式一样,如果这样做的话,方法在构造函数中定义的话就不存在函数复用了,每个不同实例中的同名函数都是不同的函数……而且Super的原型对象中定义的方法对于Sub也是不可见的。基于这两个缺点,我们有了第三个方法:

  • 组合继承
    顾名思义,就是将原型链继承和构造函数继承组合在一起:原型链实现对原型属性和方法的继承,构造函数实现对实例属性的继承:
function Super(a) {
    this.a = a;
    this.color = [1, 2, 3];
};
Super.prototype.sayHi = function() {
    console.log("hi");
}

function Sub(a, b) {
    Super.call(this, a);
    this.b = b
}
Sub.prototype = new Super();
Sub.prototype.constructor = Sub;
Sub.prototype.sayB = function() {
    console.log(this.b);
}

var test1 = new Sub(1, 2), test2 = new Sub(3, 4);
test1.color.push(4);
console.log(test1.color);//[1, 2, 3, 4]
test1.sayHi();//hi
test1.sayB();//2

console.log(test2.color);//[1, 2, 3]
test2.sayHi();//hi
test2.sayB();//4
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值