Что делает следующий код:
WeatherWidget.prototype = new Widget;
где Widget
- конструктор, и я хочу расширить класс Widget с помощью новой функции WeatherWidget
.
Что такое ключевое слово new
, и что произойдет, если его не учитывать?
Что делает следующий код:
WeatherWidget.prototype = new Widget;
где Widget
- конструктор, и я хочу расширить класс Widget с помощью новой функции WeatherWidget
.
Что такое ключевое слово new
, и что произойдет, если его не учитывать?
WeatherWidget.prototype = new Widget;
Ключевое слово new
вызывает Widget
как конструктор, а возвращаемое значение присваивается свойству prototype
. (Если вы опустите new
, вы не будете называть Widget
, если вы не добавили список аргументов, ()
. Однако вызов этого метода был бы невозможен. Это, безусловно, потенциально может испортить глобальный пространство имен, если это не строгий режимный код, и реализация соответствует ECMAScript Ed. 5.x, потому что тогда this
в конструкторе будет ссылаться на глобальный объект ECMAScripts.)
Но этот подход на самом деле происходит от действительно вирусного плохой пример в старом Netscape JavaScript 1.3 Guide (отражается в Oracle, ранее Sun).
Таким образом, ваши экземпляры WeatherWidget
будут наследоваться от одного и того же экземпляра Widget
. Цепочка прототипа будет:
[new WeatherWidget()] → [new Widget()] → [Widget.prototype] → …
Это может быть полезно, но большую часть времени вы не хотите, чтобы это произошло. Вы не должны делать это здесь, если не хотите, чтобы все ваши экземпляры WeatherWidget
делили между собой значения свойств, которые они наследуют от этого экземпляра Widget
, и только через него, от Widget.prototype
. Другая проблема заключается в том, что вам нужно вызвать родительский конструктор таким образом, что может не позволить вызываться без аргументов, как вы, или не будет правильно инициализироваться. Это, безусловно, не имеет никакого отношения к эмуляции наследования на основе классов, как известно, например, от Java.
Правильный способ реализации наследования на основе классов на этих языках, основанных на прототипах (первоначально был разработан Lasse Reichstein Nielsen в comp.lang.javascript
в 2003 году, для клонирование объектов):
function Dummy () {}
Dummy.prototype = Widget.prototype;
WeatherWidget.prototype = new Dummy();
WeatherWidget.prototype.constructor = WeatherWidget;
Свойство prototype constructor
также должно быть исправлено, так что ваши экземпляры WeatherWidget
w
имели бы w.constructor === WeatherWidget
, как ожидалось, а не w.constructor === Widget
. Однако имейте в виду, что он перечислим впоследствии.
Таким образом, экземпляры WeatherWidget
наследуют свойства через цепочку прототипов, но не будут делиться значениями свойств между ними, поскольку они наследуют от Widget.prototype
до Dummy
, который не имеет собственных свойств:
[new WeatherWidget()] → [new Dummy()] → [Widget.prototype] → …
В реализациях ECMAScript Ed. 5 и более поздних версий, вы можете и должны использовать
WeatherWidget.prototype = Object.create(Widget.prototype, {
constructor: {value: WeatherWidget}
});
вместо этого. Это имеет дополнительное преимущество в том, что полученное свойство constructor
не доступно для записи, перечислимо или настраивается.
Родительский конструктор вызывается только в том случае, если вы вызываете его явно, из WeatherWidget
, например, с помощью
function WeatherWidget (…)
{
Widget.apply(this, arguments);
}
См. также Function.prototype.extend()
в моем JSX: object. js, чтобы обобщить это. Используя этот код, он станет
WeatherWidget.extend(Widget);
My Function.prototype.extend()
принимает необязательный второй аргумент, с помощью которого вы можете легко увеличить прототип экземпляров WeatherWidget
:
WeatherWidget.extend(Widget, {
foo: 42,
bar: "baz"
});
будет эквивалентно
WeatherWidget.extend(Widget);
WeatherWidget.prototype.foo = 42;
WeatherWidget.prototype.bar = "baz";
Вам все равно нужно вызвать родительский конструктор явно в дочернем конструкторе; эта часть не может быть автоматизирована. Но мой Function.prototype.extend()
добавляет свойство _super
к экземпляру Function
, что упрощает его:
function WeatherWidget (…)
{
WeatherWidget._super.apply(this, arguments);
}
Другие люди реализовали аналогичные расширения.
В соответствии с некоторыми нечетными правилами Javascript new Widget
фактически вызывает конструктор, а не возвращает ссылку на конструктор. Этот вопрос фактически отвечает на вопрос о различии между var a = new Widget()
и var a = Widget()
.
Простыми словами ключевое слово new
сообщает Javascript вызывать функцию Widget
под другим набором правил, чем обычный вызов функции. Уходя с головы, я помню:
Widget
может использовать ключевое слово this
для ссылки на этот объект.Widget
ничего не возвращает, этот новый объект будет создан.Widget
, которые используются для отслеживания цепочек свойств.Без ключевого слова new
вызов виджета будет
this
будет установлено значение undefined.
this
будет ссылаться на глобальный объект. (Вызывается window
браузером.)undefined
.Ссылка:
new
ключевое слово
WeatherWidget.prototype = new Widget;
создает новый экземпляр конструктора Widget
и использует его как объект WeatherWidget
prototype. Используя new
keyword создает новый объект, наследует цепочку наследования до Widget.prototype
и применяет на нем функцию конструктора (где вы можете настроить методы отдельных свойств или создавать переменные с частной областью).
Без ключевого слова new
это будет назначение функции Widget
для свойства prototype
, что не имеет никакого смысла. Если вы добавите необязательные скобки (т.е. Widget()
), он будет вызывать функцию нормально, но не как конструктор в новом экземпляре, а глобальный объект как контекст. См. Также ссылка для ключевого слова this
.
Обратите внимание, что вы не должны действительно использовать этот код. Как сказано, он создает новый экземпляр, вызывая функцию конструктора. Но целью является создание только пустого объекта, который наследуется от объекта-прототипа Widget
, а не для создания какого-либо экземпляра (что может нанести вред, в зависимости от кода). Вместо этого вы должны использовать Object.create
(или его популярную прокладку):
WeatherWidget.prototype = Object.create(Widget.prototype);
см. также базовое наследование Javascript против прототипного наследования Crockford
В простом английском языке вы расширяете один класс другим. Прототипом может быть только объект, поэтому вы устанавливаете прототип WeatherWidget
в новый экземпляр Widget
. Если вы удалили ключевое слово new
, вы должны установить прототип функции литерала-конструктора, которая ничего не делает.
var Appendages = function(){
this.legs = 2
};
var Features = function() {
this.ears = 4;
this.eyes = 1;
}
// Extend Features class with Appendages class.
Features.prototype = new Appendages;
var sara = new Features();
sara.legs;
// Returns 2.
Понимая, что прототипом может быть любой объект, что-то вроде этого также будет работать:
var appendages = {
legs : 2
};
var Features = function() {
this.ears = 4;
this.eyes = 1;
}
// Extend Features class with Appendages class.
Features.prototype = appendages;
var sara = new Features();
sara.legs;
// Returns 2.
В JavaScript, если ключ не найден на объекте, он проверяет объект-родитель, из которого вы его расширили. Следовательно, вы можете менять элементы на родительском объекте на лету так:
var appendages = {
legs : 2
};
var Features = function() {
this.ears = 4;
this.eyes = 1;
}
// Extend Features class with Appendages class.
Features.prototype = appendages;
var sara = new Features();
sara.legs;
// Returns 2.
appendages.hair = true;
sara.hair;
// Returns true.
Обратите внимание, что это происходит во время создания экземпляра, что означает, что вы не можете просто отключить прототип после создания объекта:
var foo = {name : 'bob'};
var bar = {nachos : 'cheese'};
foo.prototype = bar;
foo.nachos;
// undefined
Однако, все современные браузеры поставляются с этим новым __proto__
методом, который позволяет это сделать:
var foo = {name : 'bob'};
var bar = {nachos : 'cheese'};
foo.__proto__ = bar;
foo.nachos
// "cheese"
Читайте больше о понимании прототипов JavaScript здесь. Этот article из Pivotal Labs также очень хорош.
new
важно для наследования прототипов; т.е.
Создайте конструктор с помощью метода
var Obj = function(){};
Obj.prototype = {};
Obj.prototype.foo = function(){console.log('foo');};
Создайте второй конструктор для продолжения первого с помощью
var ExObj = function(){};
Теперь, если мы прототипируем без new
,
ExObj.prototype = Obj;
(new ExObj).foo(); // TypeError: Object #<Object> has no method 'foo'
Это означает, что мы не унаследовали от прототипа Obj
, однако, если прототип с new
ExObj.prototype = new Obj();
(new ExObj).foo(); // console logs 'foo'
Кроме того, добавление новых вещей в прототип ExObj
не вносит никаких изменений в его базу, Obj
.
Функции JavaScript "МНОЖЕСТВО (2) ЛИЧНОСТИ"!!!
Они являются регулярными функциями с вводом и выводом, которые мы называем function()
.
Также они являются конструкторами JS-объектов, когда мы используем ключевое слово new
. → > НО < < < новые созданные объекты НЕ ДОПУСКАЮТ конструкторов (например, объекты классов в наследовании на основе классов). Новые объекты - это экземпляры объекта свойства prototype
конструктора.
Затем в WeatherWidget.prototype =
вы помещаете объект, который вы хотите наследовать его свойства, к объектам, которые создаст конструктор, который обычно является new function()
, а не функцией.
JavaScript создал HUGE путаницу в сообществе программирования, назвав объекты, созданные конструкторами, INSTANCES из них с ключевым словом instanceof
. > function f(){}
undefined
> new f() instanceof f
true