new
Ключевое слово в JavaScript может быть довольно запутанным , когда он впервые встречается, так как люди склонны думать , что JavaScript не является объектно-ориентированный язык программирования.
- Что это?
- Какие проблемы это решает?
- Когда это уместно, а когда нет?
Это делает 5 вещей:
- Это создает новый объект. Тип этого объекта - просто объект .
- Он устанавливает внутреннее недоступное свойство [[prototype]] (т. Е. __Proto__ ) этого нового объекта как внешний, доступный объект- прототип функции конструктора (каждый объект функции автоматически имеет свойство prototype ).
- Это делает
this
точку переменной для вновь созданного объекта. - Он выполняет функцию конструктора, используя вновь созданный объект всякий раз, когда
this
упоминается. - Он возвращает вновь созданный объект, если только функция конструктора не возвращает
null
ссылку на не объект. В этом случае вместо этого возвращается ссылка на объект.
Примечание: функция конструктора ссылается на функцию после new
ключевого слова, как в
new ConstructorFunction(arg1, arg2)
Как только это будет сделано, если запрошено неопределенное свойство нового объекта, сценарий проверит объект объекта [[prototype]] вместо этого свойства. Вот как вы можете получить нечто похожее на традиционное наследование классов в JavaScript.
Самая сложная часть этого - точка № 2. Каждый объект (включая функции) имеет это внутреннее свойство, называемое [[prototype]] . Он может быть установлен только во время создания объекта, либо с новым , с Object.create , либо на основе литерала (функции по умолчанию равны Function.prototype, числа - Number.prototype и т. Д.). Его можно прочитать только с Object.getPrototypeOf (someObject) . Там нет нет другого пути , чтобы установить или прочитать это значение.
Функции, в дополнение к скрытому свойству [[prototype]] , также имеют свойство, называемое prototype , и именно этим вы можете обращаться и изменять, чтобы предоставлять унаследованные свойства и методы для создаваемых вами объектов.
Вот пример:
ObjMaker = function() {this.a = 'first';};
// ObjMaker is just a function, there's nothing special about it that makes
// it a constructor.
ObjMaker.prototype.b = 'second';
// like all functions, ObjMaker has an accessible prototype property that
// we can alter. I just added a property called 'b' to it. Like
// all objects, ObjMaker also has an inaccessible [[prototype]] property
// that we can't do anything with
obj1 = new ObjMaker();
// 3 things just happened.
// A new, empty object was created called obj1. At first obj1 was the same
// as {}. The [[prototype]] property of obj1 was then set to the current
// object value of the ObjMaker.prototype (if ObjMaker.prototype is later
// assigned a new object value, obj1's [[prototype]] will not change, but you
// can alter the properties of ObjMaker.prototype to add to both the
// prototype and [[prototype]]). The ObjMaker function was executed, with
// obj1 in place of this... so obj1.a was set to 'first'.
obj1.a;
// returns 'first'
obj1.b;
// obj1 doesn't have a property called 'b', so JavaScript checks
// its [[prototype]]. Its [[prototype]] is the same as ObjMaker.prototype
// ObjMaker.prototype has a property called 'b' with value 'second'
// returns 'second'
Это похоже на наследование классов, потому что теперь все объекты, которые вы используете new ObjMaker()
, также будут наследовать свойство 'b'.
Если вы хотите что-то вроде подкласса, то вы делаете это:
SubObjMaker = function () {};
SubObjMaker.prototype = new ObjMaker(); // note: this pattern is deprecated!
// Because we used 'new', the [[prototype]] property of SubObjMaker.prototype
// is now set to the object value of ObjMaker.prototype.
// The modern way to do this is with Object.create(), which was added in ECMAScript 5:
// SubObjMaker.prototype = Object.create(ObjMaker.prototype);
SubObjMaker.prototype.c = 'third';
obj2 = new SubObjMaker();
// [[prototype]] property of obj2 is now set to SubObjMaker.prototype
// Remember that the [[prototype]] property of SubObjMaker.prototype
// is ObjMaker.prototype. So now obj2 has a prototype chain!
// obj2 ---> SubObjMaker.prototype ---> ObjMaker.prototype
obj2.c;
// returns 'third', from SubObjMaker.prototype
obj2.b;
// returns 'second', from ObjMaker.prototype
obj2.a;
// returns 'first', from SubObjMaker.prototype, because SubObjMaker.prototype
// was created with the ObjMaker function, which assigned a for us
Я прочитал кучу мусора на эту тему, прежде чем наконец нашел эту страницу , где это очень хорошо объясняется с помощью хороших диаграмм.
ObjMaker
определяется как функция, которая возвращает значение? new
существует, так что вам не нужно писать фабричные методы для конструирования / копирования функций / объектов. Это означает: «Скопируйте это, сделав его таким же, как его родительский« класс »; делайте это эффективно и правильно; и храните информацию о наследовании, которая доступна только мне, JS, внутри». Для этого он изменяет недоступный в противном случае внутренний prototype
объект нового объекта, чтобы непрозрачно инкапсулировать унаследованные элементы, имитируя классические цепочки наследования ОО (которые нельзя изменить во время выполнения). Вы можете смоделировать это без new
, но наследование будет изменяемым во время выполнения. Хорошо? Плохой? Вам решать. Notice that this pattern is deprecated!
. Каков правильный современный шаблон для установки прототипа класса? так что это, вероятно, не для создания экземпляров объекта
Это используется именно для этого. Вы определяете конструктор функции так:
function Person(name) {
this.name = name;
}
var john = new Person('John');
Однако дополнительное преимущество, которое имеет ECMAScript, заключается в том, что вы можете расширять его с помощью .prototype
свойства, поэтому мы можем сделать что-то вроде ...
Person.prototype.getName = function() { return this.name; }
Все объекты, созданные из этого конструктора, теперь будут иметь getName
цепочку прототипов, к которой они имеют доступ.
class
ключевого слова, но вы можете сделать то же самое. JavaScript является объектно-ориентированным языком программирования и используется именно для создания экземпляров. Он основан на прототипах, а не на классах, но это не значит, что он не объектно-ориентирован.
Javascript - это динамический язык программирования, который поддерживает парадигму объектно-ориентированного программирования и использует его для создания новых экземпляров объекта.
Классы не нужны для объектов - Javascript - это язык, основанный на прототипах .
new
Ключевые слова создают экземпляры объектов с использованием функции в качестве конструктора. Например:
var Foo = function() {};
Foo.prototype.bar = 'bar';
var foo = new Foo();
foo instanceof Foo; // true
Экземпляры наследуются от prototype
функции конструктора. Итак, приведенный выше пример ...
foo.bar; // 'bar'
new
Ключевое слово для создания новых экземпляров объектов. И да, javascript - это динамический язык программирования, который поддерживает парадигму объектно-ориентированного программирования. Соглашение о присвоении имен объектам заключается в том, чтобы всегда использовать заглавную букву для объектов, для которых предполагается создание нового ключевого слова.
obj = new Element();
Предположим, у вас есть эта функция:
var Foo = function(){
this.A = 1;
this.B = 2;
};
Если вы вызываете это как отдельную функцию, например:
Foo();
Выполнение этой функции добавит два свойства к window
объекту ( A
и B
). Он добавляет его к тому, window
потому что window
это объект, который вызвал функцию, когда вы выполняете ее таким образом, а this
в функции это объект, который вызвал функцию. По крайней мере в Javascript.
Теперь назовите это так new
:
var bar = new Foo();
Что происходит, когда вы добавляете new
к вызову функции то, что новый объект создается (просто var bar = new Object()
) и что this
внутри функции указывается на Object
только что созданный вами объект, а не на объект, который вызвал функцию. Так bar
что теперь объект со свойствами A
и B
. Любая функция может быть конструктором, просто она не всегда имеет смысл.
window
неявная функция будет методом неявно. Даже в закрытии, даже если анонимус. Однако в этом примере это простой вызов метода для окна: Foo();
=> [default context].Foo();
=> window.Foo();
. В этом выражении window
есть контекст (не только вызывающий , что не имеет значения). В дополнение к ответу Дэниела Ховарда, вот что new
делает (или, по крайней мере, кажется, делает):
function New(func) {
var res = {};
if (func.prototype !== null) {
res.__proto__ = func.prototype;
}
var ret = func.apply(res, Array.prototype.slice.call(arguments, 1));
if ((typeof ret === "object" || typeof ret === "function") && ret !== null) {
return ret;
}
return res;
}
Пока
var obj = New(A, 1, 2);
эквивалентно
var obj = new A(1, 2);
func.prototype
чтобы быть null
? Не могли бы вы рассказать подробнее об этом? A.prototype = null;
В этом случае вы new A()
получите объект on, то есть внутренний прототип указывает на Object
объект: jsfiddle.net/Mk42ZObject(ret) === ret
. typeof
тест просто облегчает понимание того, что происходит за кулисами. Ну, JavaScript на Си может сильно отличаться от платформы к платформе, поскольку это всегда реализация оригинальной спецификации EcmaScript.
В любом случае, независимо от реализации, все реализации JavaScript, которые соответствуют спецификации EcmaScript, дадут вам объектно-ориентированный язык. Согласно стандарту ES:
ECMAScript - это объектно-ориентированный язык программирования для выполнения вычислений и манипулирования вычислительными объектами в среде хоста.
Итак, теперь, когда мы договорились, что JavaScript - это реализация EcmaScript и, следовательно, это объектно-ориентированный язык. Определение new
операции в любом объектно-ориентированном языке говорит, что такое ключевое слово используется для создания экземпляра объекта из класса определенного типа (включая анонимные типы, в случаях, подобных C #).
В EcmaScript мы не используем классы, как вы можете прочитать из спецификации:
ECMAScript не использует классы, такие как в C ++, Smalltalk или Java. Вместо этого объекты могут создаваться различными способами, в том числе посредством буквенной нотации или с помощью конструкторов, которые создают объекты и затем выполняют код, который инициализирует все или часть из них, назначая начальные значения их свойствам. Каждый конструктор - это функция, которая имеет свойство с именем - prototype ‖, которое используется для реализации наследования на основе прототипа и общих свойств. Объекты создаются с
помощью конструкторов в новых выражениях; например, new Date (2009,11) создает новый объект Date. Вызов конструктора без использования new имеет последствия, которые зависят от конструктора. Например, Date () создает строковое представление текущей даты и времени, а не объекта.
иногда код легче слов:
var func1 = function (x) { this.x = x; } // used with 'new' only
var func2 = function (x) { var z={}; z.x = x; return z; } // used both ways
func1.prototype.y = 11;
func2.prototype.y = 12;
A1 = new func1(1); // has A1.x AND A1.y
A2 = func1(1); // undefined ('this' refers to 'window')
B1 = new func2(2); // has B1.x ONLY
B2 = func2(2); // has B2.x ONLY
для меня, пока я не являюсь прототипом, я использую стиль func2, поскольку он дает мне немного больше гибкости внутри и снаружи функции.
B1 = new func2(2);
<- Почему этого не будет B1.y
? Для начинающих, чтобы понять это лучше
Попробуйте следующий код в консоли браузера.
function Foo() {
return this;
}
var a = Foo(); //returns window object
var b = new Foo(); //returns empty object of foo
a instanceof Window; // true
a instanceof Foo; // false
b instanceof Window; // false
b instanceof Foo; // true
Теперь вы можете прочитать ответ сообщества вики :)
return this;
опускание дает тот же результат. Уже есть несколько очень хороших ответов, но я публикую новый, чтобы подчеркнуть мое наблюдение в случае III ниже о том, что происходит, когда у вас есть явный оператор возврата в функции, которую вы new
вызываете. Посмотрите на следующие случаи:
Случай I :
var Foo = function(){
this.A = 1;
this.B = 2;
};
console.log(Foo()); //prints undefined
console.log(window.A); //prints 1
Выше приведен простой пример вызова анонимной функции, на которую указывает Foo
. Когда вы вызываете эту функцию, она возвращается undefined
. Поскольку явного оператора возврата нет, интерпретатор JavaScript принудительно вставляет return undefined;
оператор в конец функции. Здесь окно призывания объект (контекстный this
) , который получает новый A
и B
свойство.
Случай II :
var Foo = function(){
this.A = 1;
this.B = 2;
};
var bar = new Foo();
console.log(bar()); //illegal isn't pointing to a function but an object
console.log(bar.A); //prints 1
Здесь интерпретатор JavaScript, увидев new
ключевое слово, создает новый объект, который действует как объект вызова (контекстуальный this
) анонимной функции, на которую указывает указатель Foo
. В этом случае A
и B
становятся свойства вновь создаваемого объекта (вместо оконного объекта). Поскольку у вас нет явного оператора return, интерпретатор JavaScript принудительно вставляет оператор return, чтобы вернуть новый объект, созданный из-за использования new
ключевого слова.
Случай III :
var Foo = function(){
this.A = 1;
this.B = 2;
return {C:20,D:30};
};
var bar = new Foo();
console.log(bar.C);//prints 20
console.log(bar.A); //prints undefined. bar is not pointing to the object which got created due to new keyword.
Здесь снова интерпретатор JavaScript, увидев new
ключевое слово, создает новый объект, который действует как объект вызова (контекстуальный this
) анонимной функции, на которую указывает указатель Foo
. Снова A
и B
становитесь свойствами вновь созданного объекта. Но на этот раз у вас есть явное выражение return, поэтому интерпретатор JavaScript не будет ничего делать самостоятельно.
В случае III следует отметить, что объект, созданный из-за new
ключевого слова, был потерян с вашего радара. bar
на самом деле указывает на совершенно другой объект, который не тот, который интерпретатор JavaScript создал из-за new
ключевого слова.
Цитирование Дэвида Фланагана из JavaScripit: Полное руководство (6-е издание), гл. 4, страница № 62:
Когда выражение создания объекта оценивается, JavaScript сначала создает новый пустой объект, такой же, как тот, который создан инициализатором объекта {}. Затем он вызывает указанную функцию с указанными аргументами, передавая новый объект в качестве значения ключевого слова this. Затем функция может использовать это для инициализации свойств вновь созданного объекта. Функции, написанные для использования в качестве конструкторов, не возвращают значение, а значением выражения создания объекта является вновь созданный и инициализированный объект. Если конструктор возвращает значение объекта, это значение становится значением выражения создания объекта, и вновь созданный объект отбрасывается.
--- Дополнительная информация ---
Функции, используемые в фрагменте кода вышеупомянутых случаев, имеют специальные имена в мире JS, как показано ниже:
Случай I и II - функция конструктора
Случай III - Заводская функция. Фабричные функции не должны использоваться с new
ключевым словом, которое я сделал, чтобы объяснить концепцию в текущем потоке.
Вы можете прочитать о разнице между ними в этой теме.
new
Ключевое слово изменяет контекст , в котором функция времени запуска и возвращает указатель на этот контекст.
Если вы не используете new
ключевое слово, контекст, в котором Vehicle()
выполняется функция, совпадает с контекстом, из которого вы вызываете Vehicle
функцию. this
Ключевое слово будет ссылаться на тот же контекст. Когда вы используете new Vehicle()
, создается новый контекст, поэтому ключевое слово this
внутри функции ссылается на новый контекст. В ответ вы получаете только что созданный контекст.
Резюме:
new
Ключевое слово используется в JavaScript для создания объекта из функции конструктора. new
Ключевое слово должно быть перед вызовом функции конструкторы и будет делать следующие вещи:
- Создает новый объект
- Устанавливает прототип этого объекта в свойство prototype функции конструктора
- Привязывает
this
ключевое слово к вновь созданному объекту и выполняет функцию конструктора - Возвращает вновь созданный объект
Пример:
function Dog (age) {
this.age = age;
}
const doggie = new Dog(12);
console.log(doggie);
console.log(doggie.__proto__ === Dog.prototype) // true
Что именно происходит:
const doggie
говорит: нам нужна память для объявления переменной.- Оператор присвоения
=
говорит: мы собираемся инициализировать эту переменную с помощью выражения после=
- Выражение есть
new Dog(12)
. Движок JS видит новое ключевое слово, создает новый объект и устанавливает прототип в Dog.prototype - Функция конструктора выполняется со
this
значением, установленным для нового объекта. На этом этапе возраст назначается вновь созданному объекту собачка. - Вновь созданный объект возвращается и присваивается переменной doggie.