2010-11-12 6 views
308

Недавно я наткнулся на метод Object.create() в JavaScript и пытаюсь определить, как он отличается от создания нового экземпляра объекта с помощью new SomeFunction(), и когда вы хотели бы использовать один над другим.Понимание различий между Object.create() и новым 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

Обратите внимание, что такое же поведение наблюдается в обоих случаях. Мне кажется, что основные различия между этими двумя сценариями:

  • Объектом, используемым в Object.create() фактически формирует прототип нового объекта, в то время как в new Function() от заявленных свойств/функции не образуют прототип.
  • Вы не можете создавать замыкания с синтаксисом Object.create(), как с функциональным синтаксисом. Это логично, учитывая лексический (vs block) тип JavaScript.

Правильность приведенных выше утверждений? И я что-то упускаю? Когда вы будете использовать один над другим?

EDIT: ссылка на jsfiddle версию выше образца коду: http://jsfiddle.net/rZfYL/

+2

См. Также [Использование «Object.create» вместо «new»] (http: // stackoverflow.com/q/2709612/1048572) – Bergi

ответ

205

Объект, используемый в Object.create, фактически формирует прототип нового объекта, где, как и в новой форме Function(), объявленные свойства/функции не образуют прототип.

Да, Object.create создает объект, который наследуется непосредственно от переданного в качестве первого аргумента.

С функциями конструктора, вновь созданный объект наследует от прототипа конструктора, например .:

var o = new SomeConstructor(); 

В приведенном выше примере, o наследует непосредственно от SomeConstructor.prototype.

Там разница здесь, с Object.create вы можете создать объект, который не наследует от чего-либо, Object.create(null);, с другой стороны, если вы установите SomeConstructor.prototype = null; вновь созданный объект наследует от Object.prototype.

Вы не можете создавать замыкания с синтаксисом Object.create, как с функциональным синтаксисом. Это логично, учитывая лексический (vs block) тип JavaScript.

Ну, вы можете создать затворы, например.используя дескрипторы собственности аргумент:

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

o.foo; // "foobar" 

Обратите внимание, что я говорю о ECMAScript 5-е издание Object.create метода, а не прокладки в Крокфорд в.

Метод запускается изначально в последних браузерах, проверьте это compatibility table.

+1

@ CMS 2 вопроса. 1) До сих пор прекращается ли цепочка областей действия Object.create (null) в глобальной области (например, «окно» в браузере) или завершается ли она сама по себе? 2) Мне все еще не ясно, почему Object.create был введен (например, какая функция отсутствовала, что это касается?), И почему ее можно использовать вместо новой функции(); – Matt

+8

@Matt, 1) цепь видимости на самом деле не является связанной концепцией здесь, цепочка видимости связана с ** разрешением идентификатора **, например: как 'foo;' разрешается в текущей * лексической среде *. 2) Чтобы обеспечить простой способ реализации наследования, это действительно мощная конструкция. IMO я бы использовал его, потому что это действительно простой и легкий, но для производственного кода нам все равно нужно подождать некоторое время, пока ES5 не будет широко поддерживаться. О недостающих функциях, о факте создания «нетронутого» объекта, 'Object.create (null);' отсутствует, действительно полезно реализовать надежные объекты с хэш-таблицами ... – CMS

+0

@CMS Спасибо! – Matt

7

Внутренне Object.create делает это:

Object.create = function (o) { 
    function F() {} 
    F.prototype = o; 
    return new F(); 
}; 

Синтаксис просто убирает иллюзию, что JavaScript использует классическое наследование.

+24

Метод 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) – CMS

+0

Хмм, не знал. – xj9

+0

Согласитесь с @CMS, но в целом, это просто полипол для 'Object.create'. –

166

Вот шаги, которые происходят внутри для обоих вызовов:
(Подсказка: единственное отличие заключается в шаге 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 не выполняет конструктор.

+4

Очень полезное сравнение. –

+0

@ Ray так, используя object.create we font имеют свойства функции, упомянутые в функции конструктора? – 2017-02-21 12:28:10

+0

@sortednoun, если свойства являются частными и не указаны на прототипе **, да, они не будут унаследованы, и вы не будете иметь их в новом объекте ** (и, я бы добавил, вы можете ожидать для получения возможных прототипированных свойств от родителя, только когда родительский конструктор выполнил хотя бы один раз). – Kamafeather

16

Разница заключается в так называемом «псевдоклассическом и прототипном наследовании». Предполагается использовать только один тип кода, а не смешивать два.

В псевдоклассическом наследовании (с «новым» оператором) представьте, что вы сначала определяете псевдокласс, а затем создаете объекты из этого класса. Например, определите псевдокласс «Человек», а затем создайте «Алису» и «Боб» из «Лица».

В прототипальном наследовании (с использованием Object.create) вы непосредственно создаете конкретного человека «Алиса», а затем создаете другого человека «Боб», используя «Алису» в качестве прототипа. Здесь нет «класса»; все объекты.

Внутренне, JavaScript использует «прототипное наследование»; «псевдоклассический» способ - это просто немного сахара.

См. this link для сравнения двух способов.

+0

Этот ответ также хорош. +1 – Bento

297

Очень просто сказано, new X - Object.create(X.prototype) с дополнительным запуском функции constructor. (И давая constructor шанс return фактическим объектом, который должен быть результатом выражения, а не this.)

Вот и все. :)

Остальные ответы просто сбивают с толку, потому что, по-видимому, никто не читает определение new.;)

+18

+1 Простота и ясность! (Хотя Object.create (null) кажется хорошим вариантом - возможно, стоит упомянуть об этом). – user949300

+1

Лучший ответ, который я видел. Очень полезно! – m0meni

+0

Держите это просто, это путь, чтобы пойти – Bruce

20
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 ключевого слова есть две вещи, чтобы отметить;

а) функция используется в качестве конструктора

б) function.prototype объект передается в собственность __proto__ ... или где __proto__ не поддерживается, это второе место, где новый объект выглядит, чтобы найти свойства

2) с Object.create(obj.prototype) сконструированный объект (obj.prototype), и передавая ее предполагаемого объекта ..с той разницей, что в настоящее время нового объекта __proto__ также указывая на obj.prototype (пожалуйста, реф анс по XJ9 для этого)

31

Это:

var foo = new Foo(); 

и

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

очень похожи. Одно важное отличие заключается в том, что на самом деле работает new Foo код конструктора, в то время как Object.create не будет выполнять код, такой как

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

Обратите внимание, что если вы используете версию двухпараметрического Object.create(), то вы можете сделать гораздо более мощные вещи.

+1

Отличное объяснение. Могу ли я добавить, используя 'Object.create' в его простейшей форме, как это позволяет вам опустить функции конструктора из вашего кода, используя преимущества наследования прототипов. –

51

Позвольте мне попытаться объяснить (больше на Blog):

  1. Когда вы пишете Car конструктор var Car = function(){}, это то, как вещи внутри: A diagram of prototypal chains when creating javascript objects Мы одна {prototype} скрытая ссылка на Function.prototype, который не доступен и один prototype ссылка на Car.prototype, которая доступна и имеет фактическое constructor от Car. И Function.prototype, и Car.prototype имеют скрытые ссылки на Object.prototype.
  2. Когда мы хотим создать два эквивалентных объекта с помощью оператора 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} объекта Honda всегда будет Car.prototype — у нас нет возможности изменить свойство объекта {prototype}. Что делать, если я хочу изменить прототип нашего вновь созданного объекта?
    Maruti = Object.create(Car.prototype) — При создании объекта, подобного этому, у вас есть дополнительная опция для выбора свойства объекта {prototype}. Если вы хотите Car.prototype как {prototype}, тогда передайте его как параметр в функции.Если вы не хотите {prototype} для своего объекта, то вылетете null следующим образом: Maruti = Object.create(null).

Заключение — Используя метод Object.create у вас есть свобода выбора свойства объекта {prototype}. В new Car(); у вас нет этой свободы.

Предпочтительный способ ОО JavaScript:

Предположим, у нас есть два объекта a и b.

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

Теперь предположим, что a есть некоторые методы, которые b также хочет получить доступ. Для этого нам требуется наследование объектов (a должен быть прототипом b, только если мы хотим получить доступ к этим методам). Если мы проверим прототипы a и b, то мы узнаем, что они делят прототип Object.prototype.

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

Проблема — мы хотим, чтобы объект a как прототип b, но здесь мы создали объект b с прототипом Object.prototype. Решение — ECMAScript 5 представил Object.create(), чтобы достичь такого наследования легко. Если мы создаем объект b так:

var b = Object.create(a); 

тогда

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

Так что, если вы делаете объектно-ориентированный был тогда Object.create() очень полезно для наследования.

+0

Хорошо. Я думаю, что это ответ, поражающий глаз быка – Bento

+0

Итак, он несколько похож на создание объекта без вызова конструктора? Мы будем наслаждаться всеми преимуществами этого класса. Объект obj instanceof Class также будет правдой. Но мы не вызываем функцию класса через new. – Praveen

+0

@Anshul Вы сказали, что 'a.isPrototypeOf (b);' вернет 'false', что правильно, потому что оба объекта различны и указывают на другую память. Правильный способ сделать это с помощью оператора 'new' здесь. - https://jsfiddle.net/167onunp/. –

3

Соответственно к this answer и this videonew ключевого слова делает следующие вещи:

  1. Создает новый объект.

  2. Ссылки new object to constructor function (prototype).

  3. Делает this переменную точку на новый объект.

  4. Выполняет функцию конструктора с использованием нового объекта и неявного выполнения return this;

  5. Назначает имя функции конструктора новому объекту объекта constructor.

Object.create выполняет только 1st и 2nd шаги !!!

Смежные вопросы