Что такое «новое» ключевое слово в JavaScript? - PullRequest
1647 голосов
/ 30 октября 2009

Ключевое слово new в JavaScript может быть довольно запутанным, когда оно встречается впервые, поскольку люди склонны считать, что JavaScript не является объектно-ориентированным языком программирования.

  • Что это?
  • Какие проблемы это решает?
  • Когда это уместно, а когда нет?

Ответы [ 14 ]

2041 голосов
/ 07 сентября 2010

Это делает 5 вещей:

  1. Создает новый объект. Тип этого объекта просто object .
  2. Устанавливает внутреннее, недоступное для этого нового объекта свойство [[prototype]] (т.е. __ proto __ ) как внешнее, доступное, prototype функции конструктора объект (каждый объект функции автоматически имеет свойство prototype ).
  3. Переменная this указывает на вновь созданный объект.
  4. Выполняет функцию конструктора, используя вновь созданный объект всякий раз, когда упоминается this.
  5. Возвращает вновь созданный объект, если только функция конструктора не возвращает ссылку на объект, отличную от null. В этом случае вместо этого возвращается ссылка на объект.

Примечание: функция конструктора относится к функции после ключевого слова new, как в

new ConstructorFunction(arg1, arg2)

Как только это будет сделано, если запрошено неопределенное свойство нового объекта, сценарий проверит объект [[prototype]] для этого свойства. Вот как вы можете получить нечто похожее на традиционное наследование классов в JavaScript.

Самым сложным в этом является пункт номер 2. Каждый объект (включая функции) имеет это внутреннее свойство, называемое [[prototype]] . Он может быть установлен только во время создания объекта, либо с помощью new , с 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

Я прочитал тонну мусора на эту тему, прежде чем наконец нашел эту страницу , где это очень хорошо объясняется с помощью хороших диаграмм.

377 голосов
/ 30 октября 2009

Предположим, у вас есть эта функция:

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. Любая функция может быть конструктором, просто она не всегда имеет смысл.

157 голосов
/ 21 июня 2013

В дополнение к ответу Дэниела Ховарда, вот что 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);
100 голосов
/ 27 мая 2015

Для начинающих, чтобы понять это лучше

попробуйте следующий код в консоли браузера.

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

Теперь вы можете прочитать вики-ответ сообщества :)

34 голосов
/ 30 октября 2009

так что это, вероятно, не для создания экземпляры объекта

Он используется именно для этого. Вы определяете конструктор функции так:

function Person(name) {
    this.name = name;
}

var john = new Person('John');

Однако дополнительное преимущество, которое имеет ECMAScript, заключается в том, что вы можете расширить его с помощью свойства .prototype, чтобы мы могли сделать что-то вроде ...

Person.prototype.getName = function() { return this.name; }

Все объекты, созданные из этого конструктора, теперь будут иметь getName из-за цепочки прототипов, к которым они имеют доступ.

27 голосов
/ 30 октября 2009

JavaScript - это объектно-ориентированный язык программирования, и он используется именно для создания экземпляров. Он основан на прототипах, а не на классах, но это не значит, что он не объектно-ориентированный.

14 голосов
/ 30 октября 2009

Javascript - это динамический язык программирования, который поддерживает парадигму объектно-ориентированного программирования и используется для создания новых экземпляров объекта.

Классы не нужны для объектов - Javascript - это язык на основе прототипов .

7 голосов
/ 05 октября 2017

Уже есть несколько очень хороших ответов, но я выкладываю новый, чтобы подчеркнуть мое наблюдение по случаю 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; в конец функции. Здесь window - объект вызова (контекстный 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.

--- Дополнительная информация ---

Функции, используемые в фрагменте кода вышеупомянутых случаев, имеют специальные имена в мире JS, как показано ниже:

Случай I и II - Функция конструктора

Случай III - Заводская функция. Фабричные функции не следует использовать с ключевым словом new, которое я сделал для объяснения концепции в текущем потоке.

Вы можете прочитать о разнице между ними в этой теме.

5 голосов
/ 16 мая 2015

иногда код легче слов:

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, поскольку он дает мне немного больше гибкости внутри и вне функции.

4 голосов
/ 27 ноября 2017

Ключевое слово new изменяет контекст, в котором выполняется функция, и возвращает указатель на этот контекст.

Если вы не используете ключевое слово new, контекст, в котором выполняется функция Vehicle(), является тем же контекстом, из которого вы вызываете функцию Vehicle. Ключевое слово this будет ссылаться на тот же контекст. Когда вы используете new Vehicle(), создается новый контекст, поэтому ключевое слово this внутри функции относится к новому контексту. В ответ вы получаете только что созданный контекст.

...