Что такое «новое» ключевое слово в JavaScript?

1572

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

  • Что это такое?
  • Какие проблемы он разрешает?
  • Когда это подходит, а когда нет?
Теги:
new-operator

14 ответов

2067
Лучший ответ

Он делает 5 вещей:

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

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

new ConstructorFunction(arg1, arg2)

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

Самая сложная часть этого вопроса - это номер 2. Каждый объект (включая функции) имеет это внутреннее свойство, называемое [[prototype]]. Он может быть установлен только при создании объекта, либо с новым, с Object.create, либо на основе литерала (функции по умолчанию - Function.prototype, числа - Number.prototype и т.д.). Его можно прочитать только с Object.getPrototypeOf(someObject). Нет другого способа установить или прочитать это значение.

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


Вот пример:

ObjMaker = function() {this.a = 'first';};
// ObjMaker is just a function, there 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 [[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() вами с использованием 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

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

  • 1
    @daniel, +1 за добро. Как вы узнали, что нового?
  • 44
    Просто хотел добавить: на самом деле есть способ получить доступ к внутреннему [[prototype]] с помощью __proto__. Однако это нестандартно и поддерживается только относительно новыми браузерами (и не всеми). Существует стандартизированный способ, а именно Object.getPrototypeOf (obj), но это Ecmascript3.1, и он сам по себе поддерживается только в новых браузерах - опять же. Как правило, рекомендуется не использовать это свойство, но там все очень быстро усложняется.
Показать ещё 27 комментариев
343

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

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

  • 5
    Зависит от контекста исполнения. В моем случае (Qt scripting) это просто глобальный объект.
  • 1
    это вызовет больше использования памяти?
Показать ещё 8 комментариев
148

В дополнение к ответу Даниэля Говарда, вот что делает 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);
  • 66
    Я обнаружил, что JavaScript легче понять, чем английский: V
  • 0
    Отличный ответ. У меня есть один крошечный вопрос: как для func.prototype может быть значение null ? Не могли бы вы рассказать подробнее об этом?
Показать ещё 3 комментария
89

Для новичков это лучше понять

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

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

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

  • 4
    Хороший ответ. Также - пропуская, return this; дает тот же результат.
  • 4
    Было бы неплохо указать, почему это происходит.
32

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

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

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

var john = new Person('John');

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

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

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

  • 6
    конструкторы функций используются как классы, здесь нет ключевого слова class но вы можете сделать то же самое.
  • 0
    Здесь kindof является ключевым словом класса - класс зарезервирован для будущего использования
Показать ещё 2 комментария
27

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

  • 6
    Мне нравится говорить, что JavaScript кажется даже более объектно-ориентированным, чем все эти языки на основе классов. В JavaScript все, что вы пишете, немедленно становится объектом, но в языках на основе классов вы сначала пишете объявления, а только потом вы создаете конкретные экземпляры (объекты) классов. И прототип JavaScript, кажется, смутно напоминает все эти вещи VTABLE для языков на основе классов.
13

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

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

3

иногда код проще, чем слова:

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

  • 3
    B1 = new func2(2); <- Почему это не будет иметь B1.y ?
  • 0
    @sunny_dev Я не эксперт по JS, но, вероятно, потому что func2 возвращает непосредственно значение (объект z) вместо того, чтобы работать / возвращать с внутренними значениями (это)
2

Есть уже некоторые очень большие ответы, но я публикую новый, чтобы подчеркнуть мое наблюдение на примере III ниже о том, что происходит, когда у вас есть явный оператор return в функции, которой вы являетесь new вверх. Взгляните ниже:

Случай I:

var Foo = function(){
  this.A = 1; 
  this.B = 2;
};
console.log(Foo()); //prints undefined
console.log(window.A); //prints 1

Выше - простой случай вызова анонимной функции, на которую указывает Foo. Когда вы вызываете эту функцию, она возвращает undefined. Поскольку нет явного оператора return, поэтому интерпретатор 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.

2

Ключевое слово new предназначено для создания новых экземпляров объектов. И да, javascript - это язык динамического программирования, который поддерживает парадигму объектно-ориентированного программирования. Соглашение об именовании объектов всегда использует заглавную букву для объектов, которые должны быть созданы новым ключевым словом.

obj = new Element();
1

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

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

1

Ну JavaScript на si может сильно отличаться от платформы к платформе, поскольку она всегда является реализацией исходной спецификации EcmaScript.

В любом случае независимо от реализации все реализации JavaScript, соответствующие спецификации спецификации EcmaScript, предоставят вам объектно-ориентированный язык. Согласно стандарту ES:

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

Итак, теперь, когда мы согласились, что JavaScript является реализацией EcmaScript и поэтому является объектно-ориентированным языком. Определение операции new на любом объектно-ориентированном языке говорит о том, что такое ключевое слово используется для создания экземпляра объекта из класса определенного типа (включая анонимные типы, в случаях, подобных С#).

В EcmaScript мы не используем классы, как вы можете прочитать из спецификаций:

ECMAScript не использует классы, например, в С++, Smalltalk или Java. Вместо этого объекты могут быть созданы различными способами, включая буквальное обозначение или через конструкторы, которые создают объекты, а затем выполняют код, который инициализирует все или часть из них, назначая начальные значения к их свойствам. Каждый конструктор является функцией, которая имеет свойство -     прототип ‖, который используется для реализации наследования на основе прототипов и общих свойств. Объекты создаются с помощью использование конструкторов в новых выражениях; например, новых Дата (2009,11) создает новый объект Date. Вызов конструктора без использования новых имеет последствия, которые зависят от конструктора. Например, Date() создает строковое представление текущая дата и время, а не объект.

0

Резюме:

new ключевое слово используется в javascript для создания объекта из функции-конструктора. new ключевое слово должно быть помещено перед вызовом функции конструктора и будет выполнять следующие действия:

  1. Создает новый объект
  2. Устанавливает прототип этого объекта в свойство прототипа функции конструктора
  3. Привязывает this ключевое слово к вновь созданному объекту и выполняет функцию конструктора
  4. Возвращает вновь созданный объект

Пример:

function Dog (age) {
  this.age = age;
}

const doggie = new Dog(12);

console.log(doggie);
console.log(doggie.__proto__ === Dog.prototype) // true

Что именно происходит:

  1. const doggie говорит: Нам нужна память для объявления переменной.
  2. Оператор assigment = говорит: мы собираемся инициализировать эту переменную с выражением после =
  3. Выражение - new Dog(12). Механизм JS видит новое ключевое слово, создает новый объект и задает прототип для Dog.prototype
  4. Функция конструктора выполняется с this значением, установленным для нового объекта. На этом этапе возраст назначается новому созданному объекту doggie.
  5. Созданный объект возвращается и присваивается переменной doggie.
0

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

var Foo = function() {};
Foo.prototype.bar = 'bar';

var foo = new Foo();
foo instanceof Foo; // true

Экземпляры наследуют от prototype функции-конструктора. Итак, приведенный выше пример...

foo.bar; // 'bar'
  • 2
    Ключевое слово new в основном ассоциирует функцию как конструктор; вам не нужно ничего возвращать. Вы можете просто сделать: function foo (x) {this.bar = x; } var obj = new foo (10); оповещения (obj.bar);
  • 0
    Вам не нужно возвращать объекты из функции конструктора, если вы не хотите специально для этой цели. Например, если вам нужно возвращать конкретный экземпляр объекта вместо создания нового объекта каждый раз (по какой-либо причине). В вашем примере, однако, это совершенно не нужно.
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню