我最近为了试验而切换到了Object.create()而不是new。如何实现多重继承,例如classA
- > classA's parent
- > classA's parent's parent
等等?
示例:
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
显然不是因为我们使用的test2
原型没有关于test
的信息,因此没有问候。
我阅读了一些文章,强烈建议不要使用__proto__
进行继承。
这样做的正确javascripty方式是什么?
以下内容,但使用Object.create
test2.prototype = new test();
test2.constructor = test2;
test3.prototype = new test2();
test3.constructor = test3;
var a = new test3();
a.greet();
答案 0 :(得分: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 :(得分: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))。
要指定特定于实例的成员,可以使用多种模式。争论是,当使用这些模式时,您的代码看起来就像&#34;丑陋&#34;甚至比使用new关键字更糟糕,但这将是个人偏好,并且不会从创建getter和setter或具有额外控制(可枚举,可写和可配置)中获益。
一种模式是使用init函数:
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
创建一个新的Parent实例来设置Child的继承是不需要的,你可以使用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。