2013-09-30 75 views
0

为了实验的缘故,我最近切换到了Object.create()而不是新的。如何实现多继承,如classA - >classA's parent - >classA's parent's parent等等?Object.create链接继承

例如:

var test = Object.create(null); 
test.prototype = { 
    greet: function() { 
     console.info('hello world'); 
    }, 

    name: 'myName' 
}; 

var test2 = Object.create(test.prototype); 
test2.prototype = { 
    name: 'newName' 
}; 

var test3 = Object.create(test2.prototype); 
test3.prototype = { 
    name: 'another Name' 
}; 

test2依然能够迎接,test3显然不是,因为我们使用不具有信息有关test,因此没有映入眼帘的test2原型。

我读了几篇文章,并且非常沮丧地使用__proto__进行继承。 什么是这样做的正确javascripty方式?

类似下面的但的Object.create

test2.prototype = new test(); 
test2.constructor = test2; 

test3.prototype = new test2(); 
test3.constructor = test3; 

var a = new test3(); 
a.greet(); 
+2

请参阅http://stackoverflow.com/questions/2709612/using-object-create-instead-of-new – mccainz

+1

从技术上讲,测试不是一个对象,因为您从null继承。 JavaScript中的对象预计具有hasOwnProperty,但测试不会。如果其他人使用你的代码,他们可能期望测试是一个JS对象。也许像这样创建它:'test = Object.create(Object.prototype);' – HMR

回答

1

用的Object.create对象继承从另一个直接之一,prototype财产中没有任何作用。在第一个例子中,我用最接近你之前做的事情的形式写了一些东西,但是当你调用Object.create时,你不需要设置对象的属性。你可以在通话之后设置它们,没有任何问题(见第二个例子)。

var test1 = Object.create(null, { 
    greet : {value : function() { 
     console.info('hello world'); 
    }}, 

    name : {value : 'myName'} 
}); 

var test2 = Object.create(test1, { 
    name : {value : 'alteredProperty'}}); 

var test3 = Object.create(test2); 

test3.greet(); // hello world 
console.log(test3.name); // alteredProperty 

简单的例子(无属性描述):

var test1 = Object.create(null); 
test1.greet = function() { 
    console.info('hello world'); 
}; 
test1.name = 'myName'; 

var test2 = Object.create(test1); 
test2.name = 'alteredProperty'; 

var test3 = Object.create(test2); 

test3.greet(); 
console.log(test3.name); 

由于HMR指出,每次你做一个test1对象,您将创建一个新的greet功能和是不可取的。下一个示例通过将方法卸载到原型对象来解决此问题。

// proto object 
var Test1 = { 
    greet : function() { console.info('hello world ' + this.name); }, 
    name : 'Test1' 
}; 

// instance of Test1 
var test1 = Object.create(Test1); 

// proto object inheriting from Test1 
var Test2 = Object.create(Test1) 
Test2.name = 'Test2'; 

// instance of Test2 
var test2 = Object.create(Test2); 

// proto object inheriting from Test2 
var Test3 = Object.create(Test2); 
Test3.size = 'big'; 


// instance of Test3 
var test3 = Object.create(Test3); 

test3.greet(); // hello world Test2 
console.info(test3.name); // Test2 
console.info(test3.size); // big 
test3.name = 'Mike'; 
test3.greet(); // hello world Mike 

正如你所看到的,例如是大同小异的,如上图所示,但不同的是你如何对待某些对象。一些对象(使用大写字母的对象)的行为与具有原型的构造函数类似:它们通常不是直接使用,而是包含构建对象的方法和默认值。这纯粹是常规的,因为“类”的实例和继承“类”的语法具有完全相同的语法。这是由你来执行,proto对象不会被滥用。

奖励:

function isInstanceOf(child, parent) { 
    return Object.prototype.isPrototypeOf.call(parent, child); 
} 

console.info(isInstanceOf(test3, Test3)); // true 
console.info(isInstanceOf(test3, Test1)); // true 
console.info(isInstanceOf(test2, Test3)); // false 
+1

没有理由的Downvotes是无用的。请提供一个理由,所以我可以从我的错误中学习,而不是再次这样做。 – Tibos

+1

不是我已经写了,但是你的代码每次创建一个实例都会创建函数。你没有使用原型。我个人不喜欢它,因为它使用额外的CPU和内存没有任何理由,但'不喜欢新的关键字'我喜欢道格拉斯克罗克福德不得不说的关于JS的很多东西,但我认为他最近已经考虑不了使用'new'来支持原型。如果你有旧的JS,那么它仍然有一些内容,他今天不会同意。 – HMR

+0

@HMR感谢您的建议。答案的重点是“你可以做什么”而不是“你应该怎么做”。我将添加一个应该解决“每个实例的功能”问题的推荐。 – Tibos

0

使用的Object.create方式Tibos不会看起来像它会把传入的对象的所有成员返回的对象的prototype

// in firefox firebug running 
// an empty page 
var Definition = { 
    name : 'Test1' 
}; 
//doesn't matter where it's defined 
Definition.greet=function() { 
    console.log(this);//<-what is this in Chrome? 
}; 
Definition.arr=[]; 
// instance of Test1 
var test1 = Object.create(Definition); 
var test2 = Object.create(Definition); 
console.log(test1.greet===test2.greet);//true 
delete test2.greet 
delete test2.greet 
delete test2.greet 
delete test2.greet//can't delete it 
test2.greet(); 
console.log(test1.greet===test2.greet);//true 
console.log(test1.arr===test2.arr);//true 
test1.arr.push(1); 
console.log(test2.arr);//=[1] 
var things=[]; 
for(thing in test1){ 
    things.push(thing); 
} 
console.log("all things in test1:",things); 
things=[]; 
for(thing in test1){ 
    if(test1.hasOwnProperty(thing)){ 
    things.push(thing); 
    } 
} 
console.log("instance things in test1:",things);//nothing, no instance variables 

[更新]

正如我们从上面的代码看到的Object.create返回具有第一参数的所有成员在其上的原型和第二参数,因为它是实例成员的对象。 (在mccainz的评论中有很长一段时间的回答)增加好处(忽略IE8和多年未更新的浏览器)是可以指定实例成员的枚举,可写和可配置,并且可以创建getter和setter像分配一样工作(instance.someprop = 22实际上可以是instance.someprop(22))。

要指定特定于实例的成员,可以使用多种模式。参数是,当使用这些模式时,你的代码看起来就像使用new关键字一样“丑陋”,甚至更糟糕,但这将是一种个人喜好,并且不会从创建getter和setters或具有额外控制中获益(可枚举,可写和可配置的)。

一个模式将被使用的初始化函数:

var userB = { 
    init: function(nameParam) { 
     this.id = MY_GLOBAL.nextId(); 
     this.name = nameParam; 
    }, 
    sayHello: function() { 
     console.log('Hello '+ this.name); 
    } 
}; 
var bob = Object.create(userB).init("Bob"); 

一个更复杂的一个需要额外控制的优点是这样的:

var Person={ 
    talk:function(){console.log("I'm "+this.name);} 
    //,other prototype stuff related to Person 
}; 
var userCreator={ 
    processInstanceMembers:function(o,initObj){ 
    this.createName(o,initObj.name); 
    Object.defineProperty(o,"_name",{writable:true}); 
    o.name=initObj.name; 
    }, 
    get:function(initObj,inheritFrom){ 
    var ret=Object.create(inheritFrom||Person); 
    this.processInstanceMembers(ret,initObj); 
    return ret; 
    }, 
    createName:function(o){//minimalise closure scope 
    Object.defineProperty(o,"name",{ 
     get:function(){ 
     return this._name; 
     }, 
     set:function(val){ 
     if(val.replace(/\s*/gm,"")===""){ 
      throw new Error("Name can't be empty, or only whitespaces"); 
     } 
     this._name=val; 
     }, 
     enumerable : true 
    }); 
    } 
}; 

//when creating an instance you can choose what to inherit from 
//leave it out to inherit from Person 
var u=userCreator.get({name:"Ben"}); 
u.talk(); 
u.name="Benji"; 
u.talk(); 
u.name=" ";//error, name can't be empty 

创建父的新实例来设置的继承孩子是不需要的,你可以使用Object.create这个或帮助函数:

var Child =function(){ 
    //get Parent's INSTANCE members defined in the 
    //parent function body with this.parentInstance=... 
    Parent.apply(this,arguments); 
} 
Child.prototype=Object.create(Parent.prototype); 
Child.prototype.constructor=Child; 
Child.prototype.otherFn=function(){}; 

你可能会发现this有趣,它有一个辅助函数,所以如果你不想使用Object.create,你不需要使用Object.create。

+0

'this'是该方法被调用的对象(除非它被绑定)。在你的例子'Definition.greet()'=>这个===定义,'test2.greet()'=>这个=== test2 – Tibos

+0

@Tibos我的意思是Chrome分别显示实例和原始继承的成员。由于我的代码显示没有实例成员,可能会有问题。看看'arr'的例子。在test1实例中推送一个值会更改test2中的'arr'成员。 – HMR

+0

@Tibos是的,Chrome显示一个空对象,其中所有成员都在__proto__中 – HMR