继承方式四:原型式继承

道格拉斯·克罗克福德在一篇文章中介绍了一种实现继承的方法,这种方法并没有使用严格意义上的构造函数。它的想法是借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型。为了达到这个目的,他给出了如下函数。

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

在object()函数内部,先创建了一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回了这个临时类型的一个新实例从本质上讲,object()对传入其中的对象执行了一次浅复制。来看下面的例子:

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

var person = {
    name : "李白",
    friends : ["杜甫","杜牧"]
};

var anotherPerson = object(person);
anotherPerson.name = "陶渊明";
anotherPerson.friends.push("李清照");

var yetAnotherPerson = object(person);
yetAnotherPerson.name = "曹操";
yetAnotherPerson.friends.push("陆游");

console.log(person.friends);//["杜甫", "杜牧", "李清照", "陆游"]

克罗克福德主张的这种原型式继承要求你必须有一个对象可以作为另一个对象的基础。如果有这么一个对象的话,可以把它传递给object()函数,然后再根据具体需求对得到的对象加以修改即可。在这个例子中,可以作为另一个对象基础的是person对象,于是我们把它传入到object()函数中,然后该函数就会返回一个新对象。新对象将person作为原型,所以它的原型中就包含一个基本类型值属性和一个引用类型值属性。这意味着person.friends不仅仅属于person所有,而且也会被anotherPerson 以及yetAnotherPerson共享。实际上,这就相当于又创建了person对象的两个副本。

ECMAScript5通过新增Object.create()方法规范化了原形式继承。这个方法接收两个参数:一个用作新对象原型的对象和(可选的)一个为新对象定义额外属性的对象。在传入一个参数的情况下,Object.create()和object()方法的行为相同。

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

var person = {
    name : "李白",
    friends : ["杜甫","杜牧"]
};

var anotherPerson = Object.create(person);
anotherPerson.name = "陶渊明";
anotherPerson.friends.push("李清照");

var yetAnotherPerson = Object.create(person);
yetAnotherPerson.name = "曹操";
yetAnotherPerson.friends.push("陆游");

console.log(person.friends);//["杜甫", "杜牧", "李清照", "陆游"]
console.log(anotherPerson.friends);//["杜甫", "杜牧", "李清照", "陆游"]
console.log(yetAnotherPerson.friends);//["杜甫", "杜牧", "李清照", "陆游"]

Object.create()方法的第二个参数与Object.defineProperties()方法的第二个参数格式相同:每个属性都是通过自己的描述符定义的。以这种方式指定的任何属性都会覆盖原型对象上的同名属性。例如:

var person = {
    name : "李白",
    friends : ["杜甫","杜牧"]
};

var anotherPerson = Object.create(person,{
    name:{
    	value:"杜甫"
    }
});
console.log(anotherPerson.name);//杜甫

支持Object.create()方法的浏览器有IE9+、Firefox4+、Safari5+、Opera 12+和Chrome。

在没有必要兴师动众地创建构造函数,而只想让一个对象和另一个对象保持类似的情况下,原型式继承是完全可以胜任的。不过别忘了,包含引用类型值的属性始终都会共享相应的值,就像使用原型模式一样

你可能感兴趣的:(实现继承的方式)