Понимание создания объекта-прототипа с помощью "Object.create()" вместо ключевого слова "новое"
Я пришел к коду, содержащему эти строки
var data = function() {
function Metadata() { /*some initialization here*/ }
Metadata.prototype = Object.create(Backend.prototype);
Metadata.prototype.constructor = Metadata;
return Metadata;
}
Я пытаюсь понять, что происходит на самом деле, и как использовать возвращаемый объект. Если я правильно понял, data
теперь будет объектом, который должен быть инициализирован следующим образом
var d = new data()
Но я не понимаю следующие строки и почему Object.create()
используется вместо ключевого слова new
:
Metadata.prototype = Object.create(Backend.prototype);
Metadata.prototype.constructor = Metadata;
Что они делают? Нужны ли они? И в чем разница между Object.create
и new
?
Ответы
Ответ 1
JavaScript - это язык, основанный на прототипе.
Это означает, что он не использует ключевое слово класса, как на других языках. Вместо этого JavaScript использует функции как классы.
В вашем примере переменная данных может быть ассимилирована классу:
var Data = function() { ... }
Чтобы создать экземпляр этого класса, мы используем новое ключевое слово, назначающее результат объекта типа переменной.
var data = new Data()
Так как ECMA Script 6, мы можем использовать метод создания экземпляров Object.create()
, который создает непосвященный объект с указанным объектом и свойствами прототипа. Он принимает аргумент объекта, который должен быть прототипом вновь созданного объекта. (Он также скопирует конструктор)
Итак, следующие строки - это способ сделать метаданные, расширяющие объект Backend, и сохранить свой собственный конструктор:
// Metadata extends Backend
Metadata.prototype = Object.create(Backend.prototype);
Metadata.prototype.constructor = Metadata;
Но этот код не совсем эквивалентен Metadata.prototype = new Backend();
. См. Этот пример:
//Base class
var Backend = function(){ this.publicProperty='SomeValue'; }
//Extension class 1
var Metadata1 = function() { }
Metadata1.prototype = Object.create(Backend.prototype);
Metadata1.prototype.constructor = Metadata1;
//Extension class 2
var Metadata2 = function() { }
Metadata2.prototype = new Backend();
/*
* Then the results are different (see code snippet at the end of this post)
*/
//result1 = 'undefined'
var data1 = new Metadata1();
var result1 = data1.publicProperty;
//result2 = 'SomeValue'
var data2 = new Metadata2();
var result2 = data2.publicProperty;
Фактически оба очень похожи, основное отличие состоит в том, что ключевое слово new
на самом деле запускает код конструктора, тогда как Object.create
не будет выполнять код.
Еще одно отличие состоит в том, что при Object.create
вы можете создать объект, который не наследует ничего (Object.create(null)
).
Если вы сделаете Metadata.prototype = null
, новый созданный объект наследует от Object.prototype
Примечание. В более раннем браузере (IE8 и ниже) вы можете использовать этот эквивалентный код для Object.create
:
Object.create = function(o){
function F(){}
F.prototype=o;
return new F();
}
Вот фрагмент рабочего кода, который показывает различия между двумя подходами
//Base class
var Backend = function(){ this.publicProperty='SomeValue'; }
//Extension class 1
var Metadata1 = function() { }
Metadata1.prototype = Object.create(Backend.prototype);
Metadata1.prototype.constructor = Metadata1;
//Extension class 2
var Metadata2 = function() { }
Metadata2.prototype = new Backend();
//result: 'undefined'
var data1 = new Metadata1();
$("#result1").text("result1: " + (typeof data1.publicProperty=='undefined' ? 'undefined' : data1.publicProperty));
//result: 'SomeValue'
var data2 = new Metadata2();
$("#result2").text("result2: " + (typeof data2.publicProperty=='undefined' ? 'undefined' : data2.publicProperty));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="result1"></div>
<div id="result2"></div>
Ответ 2
Эти две строки являются прототипом способа расширения Metadata
Backend
.
Классы в JavaScript определяются как функции. Если вы определяете prototype
для функции, вы можете использовать ключевое слово new
(или Object.create
) для создания экземпляра класса. Функции/свойства, которые находятся на прототипе, будут в новом экземпляре создаваемого вами класса.
В приведенном выше коде Metadata.prototype
устанавливается экземпляр Backend
, поэтому Metadata.prototype
наследует вызов методов/свойств на Backend.prototype
.
Вы можете найти более Наследование и цепочка прототипов.
Ответ 3
В JavaScript нет классов, и вместо этого у нас есть конструкторы, которые можно вызвать с помощью ключевого слова new
для создания новых объектов, поэтому мы получим то же поведение, что и классы и instanciation.
И эти две строки используются для выражения наследования и для того, чтобы сделать Metadata
extends Backend
в строке:
Metadata.prototype = Object.create(Backend.prototype);
Мы определяем прототип объекта Metadata
, который должен быть прототипом вновь созданного объекта.
В этой строке:
Metadata.prototype.constructor = Metadata;
Определим конструктор Metadata
, который будет использоваться для создания новых экземпляров Metadata
.
И наследование можно протестировать следующим образом:
var meta = new Metadata();
console.log('Is meta an instance of Metadata? ' + (meta instanceof Metadata)); // true
console.log('Is meta an instance of Backend? ' + (meta instanceof Backend)); // true
И вы можете найти больше об этом с другим примером в Object.create() documentaion здесь.
Ответ 4
Object.create
- это метод ES6, он создает объект с данным объектом в качестве прототипа.
Metadata.prototype = Object.create(Backend.prototype);
Таким образом, указанная выше строка означает Metadata
наследует все свойства и методы из Backend
. Это как-то похоже на следующую строку перед ES6:
Metadata.prototype = new Backend();
Однако Metadata
также наследует свойство constructor
из Backend
:
var metaData = new Metadata();
metaData.constructor; // function Backend()
Это выглядит странно, потому что Metadata
фактически сконструирован с помощью Metadata
, поэтому мы фиксируем его следующим образом:
Metadata.prototype.constructor = Metadata;
var metaData = new Metadata();
metaData.constructor; // function Metadata()
Обратите внимание, что это не путается с оператором instanceof
:
metaData instanceof Metadata // true
metaData instanceof Backend // true