理解Object.create()和new SomeFunction()之间的区别

Mat*_*att 372 javascript prototype object-create

我最近Object.create()在JavaScript中偶然发现了这个方法,并试图推断它与创建一个对象的新实例有什么不同new SomeFunction(),当你想要使用另一个时.

请考虑以下示例:

var test = {
  val: 1,
  func: function() {
    return this.val;
  }
};
var testA = Object.create(test);

testA.val = 2;
console.log(test.func()); // 1
console.log(testA.func()); // 2

console.log('other test');
var otherTest = function() {
  this.val = 1;
  this.func = function() {
    return this.val;
  };
};

var otherTestA = new otherTest();
var otherTestB = new otherTest();
otherTestB.val = 2;
console.log(otherTestA.val); // 1 
console.log(otherTestB.val); // 2

console.log(otherTestA.func()); // 1
console.log(otherTestB.func()); // 2
Run Code Online (Sandbox Code Playgroud)

请注意,在两种情况下都观察到相同的行为.在我看来,这两种情况之间的主要区别是:

  • Object.create()实际使用的对象实际上形成了新对象的原型,而在new Function()声明的属性/函数中,不形成原型.
  • 您不能Object.create()像使用函数语法一样使用语法创建闭包.鉴于JavaScript的词法(vs块)类型范围,这是合乎逻辑的.

以上陈述是否正确?我错过了什么吗?你什么时候用一个而不是另一个?

编辑:链接到以上代码示例的jsfiddle版本:http://jsfiddle.net/rZfYL/

Evi*_*ine 397

非常简单地说,new XObject.create(X.prototype)另外运行该constructor功能.(并给出了应该是表达式结果的实际对象constructor的机会,return而不是this.)

而已.:)

其余的答案只是令人困惑,因为显然没有其他人读到其中new任何一个的定义.;)

  • +1简洁明了!(虽然Object.create(null)似乎是个不错的选择 - 也许应该提一下). (22认同)
  • @Qwertie:在JS中,*所有*都是一个对象.:)他们从Java那里复制了那些,他从SmallTalk复制了它,然后用它一直到最后.这是"出现"的一个很好的例子,一般来说生活更轻松. (2认同)
  • @Evi1M4chine 实际上在 Java 中,函数不是对象(就此而言,基元也不是)...并且对象没有原型,因此比较似乎不合适。事实上,JS 的工作方式与其他流行的 OO 语言不同,这是造成混乱的一个主要根源(浏览器不提供一种简单的方法来可视化包括函数和原型在内的对象网络,这也无济于事)。PS我发现这个链接很有帮助:https://davidwalsh.name/javascript-objects-deconstruction (2认同)
  • @Qwertie:我并没有说 Java 完全“确实”遵循了这一理念。他们有_哲学_。他们只是半途而废。:) 但 SmallTalk 确实遵循了它。…并且OOP不等于基于类的OOP..JS是基于原型的OOP,但它们都有OOP的共同点。事实上,JS 的 OOP 哲学比基于类的方法更干净、更优雅、更通用。他们也只是未能很好地实施它。(JavaScript 2 本来应该解决所有这些问题,而且会非常好。WebAssembly 使所有这些都变得毫无意义。:) (2认同)

CMS*_*CMS 240

Object.create中使用的对象实际上形成了新对象的原型,而在新的Function()形式中,声明的属性/函数不构成原型.

是的,Object.create构建一个直接从作为第一个参数传递的对象继承的对象.

使用构造函数,新创建的对象继承自构造函数的原型,例如:

var o = new SomeConstructor();
Run Code Online (Sandbox Code Playgroud)

在上面的例子中,o直接继承自SomeConstructor.prototype.

这里有一个区别,Object.create你可以创建一个不从任何东西继承的对象,Object.create(null);另一方面,如果你设置SomeConstructor.prototype = null;新创建的对象将继承Object.prototype.

您不能像使用函数语法一样使用Object.create语法创建闭包.鉴于JavaScript的词法(vs块)类型范围,这是合乎逻辑的.

好吧,你可以创建闭包,例如使用属性描述符参数:

var o = Object.create({inherited: 1}, {
  foo: {
    get: (function () { // a closure
      var closured = 'foo';
      return function () {
        return closured+'bar';
      };
    })()
  }
});

o.foo; // "foobar"
Run Code Online (Sandbox Code Playgroud)

请注意,我在谈论ECMAScript第5版Object.create方法,而不是Crockford的垫片.

该方法开始在最新的浏览器上本地实现,请检查此兼容性表.

  • @Matt,1)范围链在这里并不是一个真正的相关概念,范围链与**标识符解析**有关,例如:如何在当前*词汇环境*中解析`foo;`.2)为了提供一种实现继承的简单方法,它是一个非常强大的构造.IMO我会使用它,因为它非常简单和轻量级,但对于生产代码,我们仍需要等待一段时间才能广泛支持ES5.关于缺少的功能,创建一个"pristine"对象的事实,`Object.create(null);`缺失,它实现可靠的类似哈希表的对象真的很有用...... (9认同)
  • @CMS 2个问题.1)Object.create(null)上的作用域链是否仍然终止于全局作用域(例如浏览器中的"窗口"),还是它自身终止?2)我仍然不清楚为什么会引入Object.create(例如,缺少什么功能,这解决了?)以及为什么会使用它而不是新的Function(); (2认同)

Ray*_*lha 198

以下是两个调用内部发生的步骤:(
提示:唯一的区别在于步骤3)


new Test():

  1. 创建new Object()obj
  2. 设置obj.__proto__Test.prototype
  3. return Test.call(obj) || obj; // normally obj is returned but constructors in JS can return a value

Object.create( Test.prototype )

  1. 创建new Object()obj
  2. 设置obj.__proto__Test.prototype
  3. return obj;

所以基本上Object.create不执行构造函数.


Ans*_*hul 61

让我试着解释一下(更多关于博客):

  1. 当你编写Car构造函数时var Car = function(){},这就是内部的事情: 创建javascript对象时的原型链图 我们有一个无法访问的{prototype}隐藏链接,Function.prototype其中一个prototype链接Car.prototype可访问且具有实际constructorCar.Function.prototype和Car.prototype都有隐藏的链接Object.prototype.
  2. 当我们想通过使用new运算符和create方法创建两个等效对象时,我们必须这样做:Honda = new Car();Maruti = Object.create(Car.prototype).用于不同对象创建方法的原型链图 怎么了?

    Honda = new Car();- 当您创建这样的对象时,{prototype}指向隐藏属性Car.prototype.所以在这里,{prototype}本田对象永远是Car.prototype- 我们没有任何选项来改变{prototype}对象的属性.如果我想更改新创建的对象的原型怎么办?
    Maruti = Object.create(Car.prototype)- 当您创建这样的对象时,您有一个额外的选项来选择对象的{prototype}属性.如果你想将Car.prototype作为{prototype}函数传递给它作为参数.如果您不想要任何{prototype}对象,那么您可以这样传递null:Maruti = Object.create(null).

结论 - 通过使用该方法,Object.create您可以自由选择对象{prototype}属性.在new Car();,你没有那种自由.

OO JavaScript中的首选方式:

假设我们有两个对象ab.

var a = new Object();
var b = new Object();
Run Code Online (Sandbox Code Playgroud)

现在,假设a有一些方法b也想要访问.为此,我们需要对象继承(仅当我们想要访问这些方法时才a应该是原型b).如果我们检查的原型ab那么我们会发现,它们共享原型Object.prototype.

Object.prototype.isPrototypeOf(b); //true
a.isPrototypeOf(b); //false (the problem comes into the picture here).
Run Code Online (Sandbox Code Playgroud)

问题 -我们希望将对象a作为原型b,但在这里我们b用原型创建了对象Object.prototype. 解决方案 -介绍ECMAScript 5 Object.create(),轻松实现这种继承.如果我们创建这样的对象b:

var b = Object.create(a);
Run Code Online (Sandbox Code Playgroud)

然后,

a.isPrototypeOf(b);// true (problem solved, you included object a in the prototype chain of object b.)
Run Code Online (Sandbox Code Playgroud)

因此,如果您正在进行面向对象的脚本编写,那么Object.create()对继承非常有用.


Leo*_*opd 43

这个:

var foo = new Foo();
Run Code Online (Sandbox Code Playgroud)

var foo = Object.create(Foo.prototype);
Run Code Online (Sandbox Code Playgroud)

非常相似.一个重要的区别是new Foo实际运行构造函数代码,而Object.create不会执行诸如的代码

function Foo() {
    alert("This constructor does not run with Object.create");
}
Run Code Online (Sandbox Code Playgroud)

请注意,如果您使用双参数版本,Object.create()则可以执行更强大的功能.


小智 23

不同之处在于所谓的"伪经典与原型继承".建议是在代码中只使用一种类型,而不是将两者混合使用.

在伪经典继承(使用"new"运算符)中,假设您首先定义一个伪类,然后从该类创建对象.例如,定义一个伪类"Person",然后从"Person"创建"Alice"和"Bob".

在原型继承(使用Object.create)中,您直接创建特定的人"Alice",然后使用"Alice"作为原型创建另一个人"Bob".这里没有"班级"; 都是对象.

在内部,JavaScript使用"原型继承"; "伪经典"的方式只是一些糖.

请参阅此链接以比较两种方式.


小智 21

function Test(){
    this.prop1 = 'prop1';
    this.prop2 = 'prop2';
    this.func1 = function(){
        return this.prop1 + this.prop2;
    }
};

Test.prototype.protoProp1 = 'protoProp1';
Test.prototype.protoProp2 = 'protoProp2';
var newKeywordTest = new Test();
var objectCreateTest = Object.create(Test.prototype);

/* Object.create   */
console.log(objectCreateTest.prop1); // undefined
console.log(objectCreateTest.protoProp1); // protoProp1 
console.log(objectCreateTest.__proto__.protoProp1); // protoProp1

/* new    */
console.log(newKeywordTest.prop1); // prop1
console.log(newKeywordTest.__proto__.protoProp1); // protoProp1
Run Code Online (Sandbox Code Playgroud)

摘要:

1)new关键字有两点需要注意;

a)函数用作构造函数

b)将function.prototype对象传递给__proto__属性...或者__proto__不支持的位置,它是新对象查找属性的第二个位置

2)与Object.create(obj.prototype)你正在构建一个object(obj.prototype)并将其传递给目标对象..不同之处在于,现在新对象__proto__也指向obj.prototype(请为xj9引用)


xj9*_*xj9 10

内部Object.create做到这一点:

Object.create = function (o) {
    function F() {}
    F.prototype = o;
    return new F();
};
Run Code Online (Sandbox Code Playgroud)

语法只是消除了JavaScript使用经典继承的错觉.

  • ECMAScript 5 [`Object.create`](http://sideshowbarker.github.com/es5-spec/#x15.2.3.5)方法做的远不止于此,您可以通过*属性描述符定义属性*并且你可以创建一个不从任何东西继承的对象(`Object.create(null);`),应避免使用这种类型的填充程序,因为你无法在ES3上真正模拟这种行为.[更多信息](http://stackoverflow.com/questions/3830800/object-defineproperty-in-es5/3844768#3844768) (25认同)

Vla*_*pak 10

根据这个答案这个视频 new关键字做下一件事:

  1. 创建新对象.

  2. 将新对象链接到构造函数(prototype).

  3. 使this变量指向新对象.

  4. 使用新对象执行构造函数并隐式执行return this;

  5. 将构造函数名称分配给新对象的属性constructor.

Object.create只执行1st2nd步骤!!!


Ted*_*Ted 10

对象创建变体.


变体1:' new Object() ' - >没有参数的Object构造函数.

var p1 = new Object(); // 'new Object()' create and return empty object -> {}

var p2 = new Object(); // 'new Object()' create and return empty object -> {}

console.log(p1); // empty object -> {}

console.log(p2); // empty object -> {}

// p1 and p2 are pointers to different objects
console.log(p1 === p2); // false

console.log(p1.prototype); // undefined

// empty object which is in fact Object.prototype
console.log(p1.__proto__); // {}

// empty object to which p1.__proto__ points
console.log(Object.prototype); // {}

console.log(p1.__proto__ === Object.prototype); // true

// null, which is in fact Object.prototype.__proto__
console.log(p1.__proto__.__proto__); // null

console.log(Object.prototype.__proto__); // null
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述


变体2:' new Object(person) ' - >带有参数的Object构造函数.

const person = {
    name: 'no name',
    lastName: 'no lastName',
    age: -1
}

// 'new Object(person)' return 'person', which is pointer to the object ->
//  -> { name: 'no name', lastName: 'no lastName', age: -1 }
var p1 = new Object(person);

// 'new Object(person)' return 'person', which is pointer to the object ->
//  -> { name: 'no name', lastName: 'no lastName', age: -1 }
var p2 = new Object(person);

// person, p1 and p2 are pointers to the same object
console.log(p1 === p2); // true
console.log(p1 === person); // true
console.log(p2 === person); // true

p1.name = 'John'; // change 'name' by 'p1'
p2.lastName = 'Doe'; // change 'lastName' by 'p2'
person.age = 25; // change 'age' by 'person'

// when print 'p1', 'p2' and 'person', it's the same result,
// because the object they points is the same
console.log(p1); // { name: 'John', lastName: 'Doe', age: 25 }
console.log(p2); // { name: 'John', lastName: 'Doe', age: 25 }
console.log(person); // { name: 'John', lastName: 'Doe', age: 25 }
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述


变体3.1:' Object.create(person) '.将Object.create与简单对象'person'一起使用.'Object.create(person)'将创建(并返回)新的空对象,并将属性'__proto__'添加到同一个新的空对象中.此属性"__proto__"将指向对象"person".

const person = {
        name: 'no name',
        lastName: 'no lastName',
        age: -1,
        getInfo: function getName() {
           return `${this.name} ${this.lastName}, ${this.age}!`;
    }
}

var p1 = Object.create(person);

var p2 = Object.create(person);

// 'p1.__proto__' and 'p2.__proto__' points to
// the same object -> 'person'
// { name: 'no name', lastName: 'no lastName', age: -1, getInfo: [Function: getName] }
console.log(p1.__proto__);
console.log(p2.__proto__);
console.log(p1.__proto__ === p2.__proto__); // true

console.log(person.__proto__); // {}(which is the Object.prototype)

// 'person', 'p1' and 'p2' are different
console.log(p1 === person); // false
console.log(p1 === p2); // false
console.log(p2 === person); // false

// { name: 'no name', lastName: 'no lastName', age: -1, getInfo: [Function: getName] }
console.log(person);

console.log(p1); // empty object - {}

console.log(p2); // empty object - {}

// add properties to object 'p1'
// (properties with the same names like in object 'person')
p1.name = 'John';
p1.lastName = 'Doe';
p1.age = 25;

// add properties to object 'p2'
// (properties with the same names like in object 'person')
p2.name = 'Tom';
p2.lastName = 'Harrison';
p2.age = 38;

// { name: 'no name', lastName: 'no lastName', age: -1, getInfo: [Function: getName] }
console.log(person);

// { name: 'John', lastName: 'Doe', age: 25 }
console.log(p1);

// { name: 'Tom', lastName: 'Harrison', age: 38 }
console.log(p2);

// use by '__proto__'(link from 'p1' to 'person'),
// person's function 'getInfo'
console.log(p1.getInfo()); // John Doe, 25!

// use by '__proto__'(link from 'p2' to 'person'),
// person's function 'getInfo'
console.log(p2.getInfo()); // Tom Harrison, 38!
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述


变体3.2:' Object.create(Object.prototype) '.将Object.create与内置对象一起使用 - >'Object.prototype'.'Object.create(Object.prototype)'将创建(并返回)新的空对象,并将属性'__proto__'添加到同一个新的空对象中.此属性"__proto__"将指向对象"Object.prototype".

// 'Object.create(Object.prototype)' :
// 1. create and return empty object -> {}.
// 2. add to 'p1' property '__proto__', which is link to 'Object.prototype'
var p1 = Object.create(Object.prototype);

// 'Object.create(Object.prototype)' :
// 1. create and return empty object -> {}.
// 2. add to 'p2' property '__proto__', which is link to 'Object.prototype'
var p2 = Object.create(Object.prototype);

console.log(p1); // {}

console.log(p2); // {}

console.log(p1 === p2); // false

console.log(p1.prototype); // undefined

console.log(p2.prototype); // undefined

console.log(p1.__proto__ === Object.prototype); // true

console.log(p2.__proto__ === Object.prototype); // true
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述


变体4:' new SomeFunction() '

// 'this' in constructor-function 'Person'
// represents a new instace,
// that will be created by 'new Person(...)'
// and returned implicitly
function Person(name, lastName, age) {

    this.name = name;
    this.lastName = lastName;
    this.age = age;

    //-----------------------------------------------------------------
    // !--- only for demonstration ---
    // if add function 'getInfo' into
    // constructor-function 'Person',
    // then all instances will have a copy of the function 'getInfo'!
    //
    // this.getInfo: function getInfo() {
    //  return this.name + " " + this.lastName + ", " + this.age + "!";
    // }
    //-----------------------------------------------------------------
}

// 'Person.prototype' is an empty object
// (before add function 'getInfo')
console.log(Person.prototype); // Person {}

// With 'getInfo' added to 'Person.prototype',
// instances by their properties '__proto__',
// will have access to the function 'getInfo'.
// With this approach, instances not need
// a copy of the function 'getInfo' for every instance.
Person.prototype.getInfo = function getInfo() {
    return this.name + " " + this.lastName + ", " + this.age + "!";
}

// after function 'getInfo' is added to 'Person.prototype'
console.log(Person.prototype); // Person { getInfo: [Function: getInfo] }

// create instance 'p1'
var p1 = new Person('John', 'Doe', 25);

// create instance 'p2'
var p2 = new Person('Tom', 'Harrison', 38);

// Person { name: 'John', lastName: 'Doe', age: 25 }
console.log(p1);

// Person { name: 'Tom', lastName: 'Harrison', age: 38 }
console.log(p2);

// 'p1.__proto__' points to 'Person.prototype'
console.log(p1.__proto__); // Person { getInfo: [Function: getInfo] }

// 'p2.__proto__' points to 'Person.prototype'
console.log(p2.__proto__); // Person { getInfo: [Function: getInfo] }

console.log(p1.__proto__ === p2.__proto__); // true

// 'p1' and 'p2' points to different objects(instaces of 'Person')
console.log(p1 === p2); // false

// 'p1' by its property '__proto__' reaches 'Person.prototype.getInfo' 
// and use 'getInfo' with 'p1'-instance's data
console.log(p1.getInfo()); // John Doe, 25!

// 'p2' by its property '__proto__' reaches 'Person.prototype.getInfo' 
// and use 'getInfo' with 'p2'-instance's data
console.log(p2.getInfo()); // Tom Harrison, 38!
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述