在我们聊JavaScript的对象与继承之前,先来认识一个别的东西:构造函数。构造函数是对象和继承的基础,所以理解它至关重要。

1: JavaScript的构造函数是什么?

 1: 它是一个普通的函数
 2: 它内部用了this关键字
 3: 它有一个prototype属性,这个prototype是一个对象(其实每一个函数都有prototype属性)
 4: 这个prototype包含的是可以被继承的属性

接下来用代码来解释一下上面的文字:

function Apple(price, colour){
    this.price = price;
    this.colour = colour;
}
Apple.prototype.type = 'fruit'; //可被继承的属性

var appleRed = new Apple(10, 'red');
var appleGreen = new Apple(20, 'green');

appleRed.colour;//red
appleGreen.colour;//green
appleRed.type; //fruit
appleGreen.type;//fruit

以上是一个从原型对象(Apple)生成实例对象(appleGreen, appleRed)的例子。接下来看看基于构造函数继承:
首先假设有两个构造函数:

function Fruit(){
    this.taste = 'sweet';
}

function Apple(color){
    this.color = color;
}

我们希望Apple能继承Fruit,怎么做?总共有5种继承方法:
1: 构造函数绑定
在子构造函数中,在父构造器身上调用call或者apply方法

function Fruit(){
    this.taste = 'sweet';
}

function Apple(color){
    Fruit.apply(this, arguments);
    this.color = color;
}

var appleRed = new Apple('red');
appleRed.taste;// 'sweet'

2: prototype赋值
实例化一个父对象,然后把它赋给子构造函数的prototype属性

function Fruit(){
    this.taste = 'sweet';
}

function Apple(color){
    this.color = 'red'
}

Apple.prototype = new Fruit();
Apple.prototype.constructor = Apple;//特别注意这一行

var appleRed = new Apple('red');
appleRed.taste;//'sweet'
appleRed.constructor === Apple;//true

每一个实例都有一个constructor,继承自构造函数的prototype的constructor属性。在执行了

Apple.prototype = new Fruit();

之后,相当于是把Apple的prototype这个对象整个覆盖了,这个时候 Apple.prototype.constructor是Fruit这个方法。这样子就意味这appleRed.constructor也是Fruit,这样继承就混乱了,因为appleRed明明是由Apple这个构造方法实例化出来的。所以我们要把Apple正确的constructor重新设置回去:

Apple.prototype.constructor = Apple;

3: 把父构造函数的prototype直接赋给子构造函数的prototype

function Fruit(){}
Fruit.prototype.taste = 'sweet';

function Apple(color){
    this.color = 'red'
}

Apple.prototype = Fruit.prototype;
Apple.prototype.constructor = Apple;

var appleRed = new Apple('red');
appleRed.taste;//'sweet'
Fruit.prototype.constructor === Apple;//true

虽然实现了继承,但是现在Apple和Fruit的prototype指向了同一段内存,任何对Apple.prototype的修改都会影响到Fruit.prototype,所以代码最后一行的这种结果,是我们不愿意看到的。
4: 利用一个空的中介构造函数
其实第四种是第二种和第三种的结合

function Fruit(){}
Fruit.prototype.taste = 'sweet';

function Apple(color){
    this.color = 'red'
}

var F = function(){};
F.prototype = Fruit.prototype;
Apple.prototype = new F();
Apple.prototype.constructor = Apple;

var appleRed = new Apple('red');
appleRed.taste;//'sweet'
appleRed.constructor === Apple;//true

1: 中介构造函数的prototype=父构造函数的prototype;
2: 子构造函数的prototype=中介构造函数的一个实例;
3: 把子构造函数的constructor复原为自己
5: copy继承
就是把父构造函数的prototype上的所有属性和方法拷贝进子构造方法。

function Fruit(){}
Fruit.prototype.taste = 'sweet';

function Apple(color){
    this.color = color;
}

function extend(child, parent){
    var c = child.prototype;
    var p = parent.prototype;
    for(var i in p){
        c[i] = p[i];
    }
}
extend(Apple, Fruit);//调用继承方法
var appleRed = new Apple('red');
appleRed.taste;//'sweet'

但是以上实现的是一个浅拷贝,浅拷贝存在一些问题。在实现继承的时候,到底要使用浅拷贝还是深拷贝,要根据具体的需求,具体可以看这一篇关于浅拷贝和深拷贝的对比


nanaistaken
586 声望43 粉丝