Понимание разницы между Object.create() и новым SomeFunction()
Я недавно наткнулся на 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
Обратите внимание, что одинаковое поведение наблюдается в обоих случаях. Мне кажется, что основными различиями между этими двумя сценариями являются:
- Объект, используемый в
Object.create()
фактически формирует прототип нового объекта, тогда как вnew Function()
Из заявленных свойств / функций не формируется прототип. - Вы не можете создавать замыкания с
Object.create()
синтаксис, как вы бы с функциональным синтаксисом. Это логично, учитывая объем лексического (против блочного) типа JavaScript.
Являются ли приведенные выше утверждения правильными? И я что-то упустил? Когда бы вы использовали один поверх другого?
РЕДАКТИРОВАТЬ: ссылка на jsfiddle версию приведенного выше примера кода: http://jsfiddle.net/rZfYL/
10 ответов
Объект, используемый в Object.create, фактически формирует прототип нового объекта, где, как и в новой функции (), объявленные свойства / функции не образуют прототип.
Да, Object.create
создает объект, который наследуется непосредственно от объекта, переданного в качестве первого аргумента.
С помощью функций конструктора вновь созданный объект наследует от прототипа конструктора, например:
var o = new SomeConstructor();
В приведенном выше примере o
наследуется напрямую от SomeConstructor.prototype
,
Здесь есть разница, с Object.create
Вы можете создать объект, который не наследует ни от чего, Object.create(null);
с другой стороны, если вы установите SomeConstructor.prototype = null;
вновь созданный объект будет наследоваться от Object.prototype
,
Вы не можете создавать замыкания с синтаксисом Object.create, как с функциональным синтаксисом. Это логично, учитывая объем лексического (против блочного) типа JavaScript.
Ну, вы можете создавать замыкания, например, используя аргумент дескрипторов свойств:
var o = Object.create({inherited: 1}, {
foo: {
get: (function () { // a closure
var closured = 'foo';
return function () {
return closured+'bar';
};
})()
}
});
o.foo; // "foobar"
Обратите внимание, что я говорю о 5-й редакции ECMAScript Object.create
метод, а не прокладка Крокфорда.
Этот метод начинает реализовываться в последних браузерах, проверьте эту таблицу совместимости.
Очень просто сказал: new X
является Object.create(X.prototype)
с дополнительным управлением constructor
функция. (И давая constructor
шанс return
фактический объект, который должен быть результатом выражения вместо this
.)
Вот и все.:)
Остальные ответы просто сбивают с толку, потому что, очевидно, никто не читает определение new
или.;)
Вот шаги, которые происходят внутри для обоих вызовов:
(Подсказка: единственная разница в шаге 3)
new Test()
:
- Создайте
new Object()
OBJ - задавать
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 - задавать
obj.__proto__
вTest.prototype
return obj;
Так в основном Object.create
не выполняет конструктор
Позвольте мне попытаться объяснить (больше на блоге):
- Когда ты пишешь
Car
конструкторvar Car = function(){}
Вот как это происходит внутри: У нас есть один{prototype}
скрытая ссылка наFunction.prototype
который не доступен и одинprototype
ссылка наCar.prototype
который доступен и имеет актуальныйconstructor
изCar
, И Function.prototype, и Car.prototype имеют скрытые ссылки наObject.prototype
, Когда мы хотим создать два эквивалентных объекта, используя
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:
Предположим, у нас есть два объекта 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()
очень полезно для наследования.
Это:
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()
тогда вы можете делать гораздо более сильные вещи.
Разница заключается в так называемом "псевдоклассическом и прототипном наследовании". Предлагаем использовать только один тип в вашем коде, не смешивая их.
В псевдоклассическом наследовании (с оператором "new") представьте, что вы сначала определяете псевдокласс, а затем создаете объекты из этого класса. Например, определите псевдокласс "Person", а затем создайте "Alice" и "Bob" из "Person".
В наследовании прототипа (используя Object.create) вы непосредственно создаете определенного человека "Алису", а затем создаете другого человека "Боб", используя "Алису" в качестве прототипа. Здесь нет "класса"; все объекты.
Внутренне JavaScript использует "прототипное наследование"; "псевдоклассический" путь - это просто немного сахара.
Смотрите эту ссылку для сравнения двух способов.
Варианты создания объекта.
Вариант 1: 'new 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
Вариант 2: "новый объект (персона)" -> конструктор объекта с аргументом.
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 }
Вариант 3.1: "Объект.создание (персона)". Используйте 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!
Вариант 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
Вариант 4: "новая функция 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!
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)
Соответственно этому ответу и этому видео new
Ключевое слово делает следующие вещи:
Создает новый объект.
Связывает новый объект с функцией конструктора (
prototype
).делает
this
переменная указывает на новый объект.Выполняет функцию конструктора, используя новый объект и неявное выполнение
return this
;Назначает имя функции конструктора для свойства нового объекта
constructor
,
Object.create
выполняет только 1st
а также 2nd
шаги!!!
Внутренне Object.create
Является ли это:
Object.create = function (o) {
function F() {}
F.prototype = o;
return new F();
};
Синтаксис просто устраняет иллюзию, что JavaScript использует классическое наследование.
Object.create(Constructor.prototype)
является частью
new Constructor
это
new Constructor
реализация
// 1. define constructor function
function myConstructor(name, age) {
this.name = name;
this.age = age;
}
myConstructor.prototype.greet = function(){
console.log(this.name, this.age)
};
// 2. new operator implementation
let newOperatorWithConstructor = function(name, age) {
const newInstance = new Object(); // empty object
Object.setPrototypeOf(newInstance, myConstructor.prototype); // set prototype
const bindedConstructor = myConstructor.bind(newInstance); // this binding
bindedConstructor(name, age); // execute binded constructor function
return newInstance; // return instance
};
// 3. produce new instance
const instance = new myConstructor("jun", 28);
const instance2 = newOperatorWithConstructor("jun", 28);
console.log(instance);
console.log(instance2);
new Constructor
реализация содержит
Object.create
метод
newOperatorWithConstructor = function(name, age) {
const newInstance = Object.create(myConstructor.prototype); // empty object, prototype chaining
const bindedConstructor = myConstructor.bind(newInstance); // this binding
bindedConstructor(name, age); // execute binded constructor function
return newInstance; // return instance
};
console.log(newOperatorWithConstructor("jun", 28));