2012-04-28 79 views
6

我觉得差已经点击了我的头,但我只是想确信的差异。在Javascript中,间“的Object.create”和“新”

在道格拉斯克罗克福德Prototypal Inheritance in JavaScript页,他说

在原型系统中,对象从对象继承。但是,JavaScript, 缺少执行该操作的操作员。相反,它 有一个新的操作者,以使得新的f()的产生一个新的对象,该对象从f.prototype 继承。

我真的不明白他想说的那句话,所以我进行了一些测试。在我看来,关键的区别是,如果我创建一个基于纯原型系统的另一个对象的对象,那么所有的父父成员应在新对象的原型,而不是新的对象本身。

这里的测试:

var Person = function(name, age) { 
     this.name = name; 
     this.age = age; 
} 
Person.prototype.toString = function(){return this.name + ', ' + this.age}; 

// The old way... 
var jim = new Person("Jim",13); 
for (n in jim) { 
    if (jim.hasOwnProperty(n)) { 
     console.log(n); 
    } 
} 
// This will output 'name' and 'age'. 

// The pure way... 
var tim = Object.create(new Person("Tim",14)); 
for (n in tim) { 
    if (tim.hasOwnProperty(n)) { 
     console.log(n); 
    } 
} 
// This will output nothing because all the members belong to the prototype. 
// If I remove the hasOwnProperty check then 'name' and 'age' will be output. 

我的理解是正确的,对象本身就为大家测试时的区别仅变得明显?

+3

请参阅http://stackoverflow.com/questions/4166616/understanding-the-difference-between-object-create-and-new-somefunction-in-j – 2012-04-28 08:14:57

+0

我昨天看过那个问题,我想,但是答案是我不清楚。现在,我已经完成我的测试,并输入了我的问题,很明显! – Jules 2012-04-28 08:21:16

回答

3

你的假设是正确的,但道格拉斯没有多谈另一种模式 - 原型可用于性能以及。你的个人类可能被写为:

var Person = function(name, age) { 
    this.name = name; 
    this.age = age; 
} 
Person.prototype.name = null; //default value if you don't init in ctor 
Person.prototype.age = null; 
Person.prototype.gender = "male"; 
Person.prototype.toString = function(){return this.name + ', ' + this.age;}; 

在这种情况下,遍历这个类的一个实例的属性,正如你在做例子,将不产生输出的“性别”属性。

编辑1: 在构造的名字和年龄的分配做使性能可见的hasOwnProperty(感谢@马特提醒的这个我)。除非有人将其设置在实例上,否则未分配的性别属性将不可见。

编辑2: 为了进一步增加这一点,我提出了一个替代的遗传模式 - 一个我亲身用于非常大的项目:

var inherits = function(childCtor, parentCtor) { 
    function tempCtor() {}; 
    tempCtor.prototype = parentCtor.prototype; 
    childCtor.superclass = parentCtor.prototype; 
    childCtor.prototype = new tempCtor(); 
    childCtor.prototype.constructor = childCtor; 
}; 

var Person = function(name){ 
    this.name = name; 
} 
Person.prototype.name = ""; 
Person.prototype.toString = function(){ 
    return "My name is " + this.name; 
} 

var OldPerson = function(name, age){ 
    OldPerson.superclass.constructor.call(this); 
    this.age = age 
}; 
inherits(OldPerson, Person); 
OldPerson.prototype.age = 0; 
OldPerson.prototype.toString = function(){ 
    var oldString = OldPerson.superclass.toString.call(this); 
    return oldString + " and my age is " + this.age; 
} 

这是一小搓一个相当普遍的模式 - 父类通过“超类”属性附加到孩子,允许您访问孩子覆盖的方法/属性。从技术上讲,你可以用Person取代OldPerson.superclass,但是这是不理想。如果您将OldPerson更改为从Person以外的类继承,那么您必须更新对Person的所有引用。

编辑3: 只是把这个完整的圆,这里是“继承”功能的一个版本,它利用的Object.create和功能完全一样,我前面描述:

var inherits = function(childCtor, parentCtor) { 
    childCtor.prototype = Object.create(parentCtor.prototype); 
    childCtor.superclass = parentCtor.prototype; 
}; 
3

编辑:这个答案最初是对@ jordancpaul的答案的回应,他已经纠正了。我将留下我的答案部分,这有助于解释原型属性和实例属性之间的重要区别:

在某些情况下,属性在所有实例之间共享,每当您声明属性时都需要非常小心在原型上。考虑下面这个例子:

Person.prototype.favoriteColors = []; //Do not do this!

现在,如果您选择使用Object.createnew创建一个新的Person实例,它不工作,你可能会想到...

var jim = new Person("Jim",13); 
jim.favoriteColors.push('red'); 
var tim = new Person("Tim",14); 
tim.favoriteColors.push('blue'); 

console.log(tim.favoriteColors); //outputs an array containing red AND blue! 

这没有按”这意味着你不能在原型上声明属性,但是如果你这样做了,那么你和每个开发你的代码的开发人员都需要知道这个陷阱。在这样的情况下,如果你喜欢的原型是什么原因声明属性,你可以这样做:

Person.prototype.favoriteColors = null

并初始化为在构造一个空数组:

var Person = function(name, age) { 
    ... 
    this.favoriteColors = []; 
} 

的使用此方法的一般规则是可以直接在原型上设置简单文字属性(字符串,数字,布尔值)的默认值,但是从Object继承的任何属性(包括数组和日期)应该设置为null,然后进行初始化在构造函数中。

更安全的方法是仅在原型上声明方法,并且始终在构造函数中声明属性。

无论如何,问题是关于的Object.create ...

传递的Object.create的第一个参数被设置为新实例的原型。更好的用法是:

var person = { 
    initialize: function(name, age) { 
     this.name = name; 
     this.age = age; 
     return this; 
    }, 

    toString: function() { 
     return this.name + ', ' + this.age; 
    } 
}; 

var tim = Object.create(person).initialize("Tim",14); 

现在输出将与第一个示例中的输出相同。如你所见,这是一种与Javascript中更古典的OOP风格不同的哲学方法。使用Object.create,重点是从现有对象创建新对象,而不是构造函数。初始化然后成为一个单独的步骤。我个人对Object.create方法有着复杂的感受;继承是非常好的,因为第二个参数可以用来向现有原型添加其他属性,但它也更加冗长,并且使得实例检查不再有效(在此示例中,替代方法是检查person.isPrototypeOf(tim)) 。

主要的原因,我说的Object.create是冗长的,因为第二个参数,但也有一些有用的库,在那里,地址:

https://github.com/Gozala/selfish

https://github.com/Raynos/pd

(和其他人)

我希望比混乱更有启发性!

+1

我只是写了*非常*简单的库来帮助遵循更传统的原型继承方法的继承,但延长“类”,当它使用的Object.create: https://github.com/mbrowne/simpleoo。 JS – 2012-11-11 04:29:08

+0

啊,牺牲品'Class.prototype.property = []'陷阱自己。我已经更新了我原来的职位多一点信息 - 或许你已经看过了这种模式。 – jordancpaul 2012-11-22 18:36:45