在Javascript中,';对象。创建';和';新';

在Javascript中,';对象。创建';和';新';,javascript,inheritance,prototype,prototypal-inheritance,Javascript,Inheritance,Prototype,Prototypal Inheritance,我想这种差异已经在我脑海中闪现,但我只是想确定一下 在道格拉斯·克罗克福德的页面上,他说 在原型系统中,对象从对象继承。JavaScript, 但是,缺少执行该操作的运算符。相反,它 有一个新的操作符,使得new f()生成一个新的 继承自f.prototype 我真的不明白他在那句话里想说什么,所以我做了一些测试。在我看来,关键的区别在于,如果我基于纯原型系统中的另一个对象创建一个对象,那么所有父成员都应该在新对象的原型上,而不是在新对象本身上 以下是测试: var Person = func

我想这种差异已经在我脑海中闪现,但我只是想确定一下

在道格拉斯·克罗克福德的页面上,他说

在原型系统中,对象从对象继承。JavaScript, 但是,缺少执行该操作的运算符。相反,它 有一个新的操作符,使得new 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.

我的理解正确吗?只有在对对象本身的成员进行测试时,差异才会变得明显?

您的假设是正确的,但道格拉斯没有谈论太多的另一种模式-原型也可以用于属性。您的person类可以写成:

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;};
在本例中,像您在示例中所做的那样,迭代该类实例的属性将不会生成“gender”属性的输出

编辑1: 构造函数中的名称和年龄分配确实使hasOwnProperty可以看到属性(感谢@matt提醒我这一点)。除非有人在实例上设置“未分配性别”属性,否则该属性将不可见

编辑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;
}
这是一种相当常见的模式,但有一点小小的变化——父类通过“superclass”属性附加到子类,允许您访问子类重写的方法/属性。从技术上讲,您可以用
Person
替换
OldPerson.superclass
,但这并不理想。如果将OldPerson更改为从Person以外的类继承,则还必须更新对Person的所有引用

编辑3: 下面是一个完整的“继承”函数版本,它利用了Object.create和函数,与我前面描述的完全相同:

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

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

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

Person.prototype.favoriteColors=[]//不要这样做

现在,如果您使用
Object.create
new
创建一个新的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 = [];
}
使用此方法时的一般规则是,可以直接在原型上设置简单文字属性(字符串、数字、布尔值)的默认值,但从对象继承的任何属性(包括数组和日期)都应设置为null,然后在构造函数中初始化

更安全的方法是只在原型上声明方法,并且总是在构造函数中声明属性

不管怎样,问题是关于对象的。创建

传递给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,重点是从现有对象创建新对象,而不是构造函数。然后,初始化成为一个单独的步骤

就我个人而言,我对这个对象有着复杂的感受;这对于继承来说非常好,因为第二个参数可以用于向现有原型添加其他属性,但它也更加冗长,使得instanceof检查不再有效(本例中的替代方法是检查
person.isPrototypeOf(tim)

我之所以说Object.create是verbose,主要是因为第二个参数,但是有一些有用的库可以解决这个问题:

(及其他)


我希望这更具启发性,而不是令人困惑

我想我昨天看到了那个问题,但我不清楚答案。现在我已经完成了我的测试并输入了我的问题,很清楚了!我刚刚编写了一个非常简单的库来帮助继承,它遵循了更多传统的原型继承方法,但它在扩展“类”时使用Object.create:是的,我自己成了
Class.prototype.property=[]
陷阱的牺牲品。我用更多的信息更新了我原来的帖子——也许你以前见过这种模式。