Name: Password: Sign in
Javascript 中的文章

本文在 署名-非商业性使用-相同方式共享 3.0 版权协议下发布, 转载请注明出自 kyleslight.net

Javascript 的原型继承

1 构造函数

先来看两个语言的例子:

//Java
Foo foo = new Foo();
//C++
Foo * foo = new Foo(para);

观察到其相似性,对象声明的时候其实就是在调用构造函数,由于考虑到Javascript是一种轻型的(误)脚本语言,因此它做得更加极端,只有构造函数:

function Foo(name){
    this.name = name;
}

var foo = new Foo("foo_name");

这样就完成了所谓的通过类(构造函数)来构造实例(新生成的对象)的想法。

2 Prototype 的引入

不过用这样的方法有一个弊端,就是每个不同的对象都携带一整套通过构造函数得到的属性,这是一种极大的浪费,因为我们获得的对象相当于一种拷贝。而实际上多数情况下我们构造的时候只有少数属于这个对象独有的特征,简单一点可以看下面这个例子:

function Dog(name, age){
    this.name = name;
    this.age = age;
    this.can_swim = true;
    this.species = "Dog";
    this.bark = function(){
        console.log("Wow");
    }
}

实际上每个Dog对象能控制的就只有nameage两个属性(特征量),但却都获得了全部的属性拷贝。为了改变这种状况,Javascript引入了prototype,这是一个(属于该构造函数的)特殊的对象,将这个构造函数所创造的共有的属性存放在这里(这是一个对象,但是在外界看来相当于一个属于该类的公用属性集合),每个由该构造函数创造出来的对象都可以像引用自己的属性一样引用prototype的属性。

这样我们从Dog构造函数中可以提取出所有的公用部分,放入它的prototype对象:

function Dog(name, age){
    this.name = name;
    this.age = age;
}

Dog.prototype = {
    can_swim : true,
    species : "Dog",
    bark : function(){
        console.log("Wow");
    }
}

var doge = new Dog("Doge", 3);
console.log(doge.species);
doge.bark();

控制台输出:

Dog
Wow

3 Prototype 继承

即使是Javascript这样的语言也依然逃脱不了继承的需求。既然需求存在,那么我们一定能想到一种方法找到解决方案。

但是,我们的对象都是通过构造函数生成的,如何复用一个对象的属性,换句话说,如何继承这个对象的全部属性呢?

我想答案你应该也已经猜到了,那就是让构造函数产生继承构造函数(是不是和类十分相似?其实说到底Javascript还是绕不开类)。

例如下面这个例子:

function Animal(){
}
Animal.prototype.species = "Animal";

function Dog(){
}
Dog.prototype.bark = function(){
    console.log("Wow");
}

function extend(child, parent){
    var o = {};
    for(fea in parent.prototype){
        o[fea] = parent.prototype[fea];
    }

    for(fea in child.prototype){
        o[fea] = child.prototype[fea];
    }

    return o;
}

这里我们使用了一个extend作为拷贝函数,将父构造函数的prototype全部拷贝至一个空对象,再用子构造函数的 prototype覆盖这个对象原有的属性(如果存在)并加入自己特有的属性。

Dog继承Animal,从而获得 species属性,我们可以这样做:

Dog.prototype = extend(Dog, Animal);
var doge = new Dog();
console.log(doge.species);
doge.bark();

如我们所想输出:

Animal
Wow

(上面这种方式有一个没有提到的好处,我们在使用extend时并没有直接将父构造函数的prototype赋予子构造函数而只是拷贝,这样即使之后我们修改了子构造函数的prototype对父构造函数依然没有影响,显然这是我们想要的:)

(好长时间没精力写字,算是对接下来一段更加苦逼生活的预热吧:)

Copyright (c) 2014-2016 Kyles Light.
Powered by Tornado.
鄂 ICP 备 15003296 号