关于javascript:了解Object.create()和new SomeFunction()之间的区别

Understanding the difference between Object.create() and new SomeFunction()

我最近偶然发现了javascript中的Object.create()方法,并试图推断它与使用new SomeFunction()创建对象的新实例有何不同,以及何时您希望使用一个实例来替代另一个实例。

请考虑以下示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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

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

  • Object.create()中使用的对象实际上构成了新对象的原型,而在new Function()中,来自声明的属性/函数的对象并不构成原型。
  • 不能像使用函数语法那样使用Object.create()语法创建闭包。这是合乎逻辑的,给定了javascript的词汇(vs块)类型范围。

以上说法正确吗?我错过什么了吗?你什么时候用一个比另一个?

编辑:链接到上述代码的jsFiddle版本示例:http://jsFiddle.net/rzfyl/


很简单地说,new XObject.create(X.prototype),另外运行constructor功能。(并给予constructor机会,使return有可能成为表达的结果,而不是this的结果。)

就是这样。:)

其余的答案都是令人困惑的,因为显然没有其他人读过new的定义。;)


The object used in Object.create actually forms the prototype of the new object, where as in the new Function() form the declared properties/functions do not form the prototype.

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

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

1
var o = new SomeConstructor();

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

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

You cannot create closures with the Object.create syntax as you would with the functional syntax. This is logical given the lexical (vs block) type scope of JavaScript.

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

1
2
3
4
5
6
7
8
9
10
11
12
var o = Object.create({inherited: 1}, {
  foo: {
    get: (function () { // a closure
      var closured = 'foo';
      return function () {
        return closured+'bar';
      };
    })()
  }
});

o.foo; //"foobar"

注意,我所说的是ECMAScript第五版Object.create方法,而不是Crockford的垫片。

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


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

new Test()

  • 创建new Object()对象
  • obj.__proto__设为Test.prototype
  • return Test.call(obj) || obj;
    // normally obj is returned but constructors in JS can return a value
  • Object.create( Test.prototype )

  • 创建new Object()对象
  • obj.__proto__设为Test.prototype
  • return obj;
  • 所以基本上Object.create不执行构造函数。


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

  • 编写Car构造函数var Car = function(){}时,内部情况如下:A diagram of prototypal chains when creating javascript objects我们有一个{prototype}隐藏链接到Function.prototype不可访问,还有一个prototype链接到Car.prototype可访问,实际constructorCar可访问。function.prototype和car.prototype都有到Object.prototype的隐藏链接。
  • 当我们想使用new操作符和create方法创建两个等价对象时,我们必须这样做:Honda = new Car();Maruti = Object.create(Car.prototype).A diagram of prototypal chains for differing object creation methods。发生什么事了?

    Honda = new Car();—创建这样的对象时,隐藏的{prototype}属性指向Car.prototype。因此,在这里,本田对象的{prototype}将始终是Car.prototype—我们没有任何选择来更改对象的{prototype}属性。如果我想改变我们新创建的对象的原型呢?Maruti = Object.create(Car.prototype)—当您创建这样的对象时,您有一个额外的选项来选择对象的{prototype}属性。如果您希望car.prototype作为{prototype},那么将其作为函数中的参数传递。如果你不想让任何一个{prototype}作为你的目标,那么你可以这样通过nullMaruti = Object.create(null)

  • 结论:通过使用Object.create方法,您可以自由选择对象{prototype}属性。在江户记1〔27〕中,你没有那种自由。

    OO javascript中的首选方法:

    假设我们有两个对象:ab

    1
    2
    var a = new Object();
    var b = new Object();

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

    1
    2
    Object.prototype.isPrototypeOf(b); //true
    a.isPrototypeOf(b); //false (the problem comes into the picture here).

    问题—我们希望对象a作为b的原型,但这里我们使用原型Object.prototype创建了对象b。解决方案—ecmascript 5引入了Object.create(),以轻松实现这种继承。如果我们创建这样的对象b

    1
    var b = Object.create(a);

    然后,

    1
    a.isPrototypeOf(b);// true (problem solved, you included object a in the prototype chain of object b.)

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


    这是:

    1
    var foo = new Foo();

    1
    var foo = Object.create(Foo.prototype);

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

    1
    2
    3
    function Foo() {
        alert("This constructor does not run with Object.create");
    }

    注意,如果您使用两参数版本的Object.create(),那么您可以做更强大的事情。


    区别在于所谓的"伪经典与原型继承"。建议在代码中只使用一种类型,而不要混合使用这两种类型。

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

    在原型继承(使用object.create)中,直接创建一个特定的人"alice",然后使用"alice"作为原型创建另一个人"bob"。这里没有"类";所有的都是对象。

    在内部,JavaScript使用"原型继承";"伪经典"方法只是一些甜头。

    请参阅此链接以了解这两种方法的比较。


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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

    总结:

    1)对于new关键字,有两件事需要注意;

    a)函数用作构造函数

    b)将function.prototype对象传递给__proto__财产…或者在不支持__proto__的情况下,它是新对象寻找属性的第二个地方。

    2)使用Object.create(obj.prototype)时,您正在构造一个对象(obj.prototype并将其传递给预期的对象……不同的是,现在新对象的__proto__也指向obj.prototype(请参考xj9中的an s)。


    在内部,Object.create是这样做的:

    1
    2
    3
    4
    5
    Object.create = function (o) {
        function F() {}
        F.prototype = o;
        return new F();
    };

    语法消除了JavaScript使用经典继承的假象。


    因此,对于这个答案和这个视频,new关键字将执行以下操作:

  • 创建新对象。

  • 将新对象链接到构造函数函数(prototype)。

  • 使this变量指向新对象。

  • 使用新对象执行构造器函数,隐式执行return this

  • 将构造函数函数名赋给新对象的属性constructor

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


    对象创建变量。

    变量1:"new object()"->不带参数的对象构造函数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    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

    enter image description here

    变量2:"new object(person)"->带参数的对象构造函数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    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 }

    enter image description here

    变量3.1:"object.create(person)"。使用object.create和简单对象"person"。对象。create(person)'将创建(并返回)新的空对象,并将属性"uu proto_uuuuuu"添加到同一个新的空对象。此属性"proto"将指向对象"person"。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    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!

    enter image description here

    变量3.2:"object.create(object.prototype)"。使用object.create和内置对象->"object.prototype".object.create(object.prototype)将创建(并返回)新的空对象,并将属性"uu proto_uuuuuuuu"添加到同一个新的空对象。此属性"proto"将指向对象"object.prototype"。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // '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

    enter image description here

    变量4:"new someFunction()"

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    // '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!

    enter image description here