new
Ключове слово в JavaScript може бути досить заплутаним , коли він вперше зустрічається, так як люди схильні думати , що JavaScript не є об'єктно-орієнтована мова програмування.
- Що це?
- Які проблеми вона вирішує?
- Коли це доречно і коли ні?
new
Ключове слово в JavaScript може бути досить заплутаним , коли він вперше зустрічається, так як люди схильні думати , що JavaScript не є об'єктно-орієнтована мова програмування.
Відповіді:
Це робить 5 речей:
this
змінну точку на новостворений об’єкт.this
згадується.null
посилання на об'єкт. У цьому випадку замість цього повертається посилання на об'єкт.Примітка: функція конструктора відноситься до функції після new
ключового слова, як у
new ConstructorFunction(arg1, arg2)
Після цього, якщо буде запропоновано невизначене властивість нового об’єкта, скрипт замість цього буде перевіряти [[прототип]] об'єкта. Так ви можете отримати щось схоже на традиційне успадкування класу в JavaScript.
Найскладніша частина цього питання - точка № 2. Кожен об'єкт (включаючи функції) має цю внутрішню властивість під назвою [[прототип]] . Його можна встановити лише під час створення об'єкта, або з новим , за допомогою Object.create , або на основі літералу (функції за замовчуванням для Function.prototype, числа до Number.prototype тощо). Його можна читати лише за допомогою Object.getPrototypeOf (someObject) . Там немає ні іншого шляху , щоб встановити або прочитати це значення.
Функції, крім прихованого [[прототипу]] властивості, також мають властивість, звану прототип , і саме це ви можете отримати доступ та змінити для надання успадкованих властивостей та методів для створених вами об'єктів.
Ось приклад:
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
елемент нового об'єкта, щоб непрозоро інкапсулювати успадковані члени, імітуючи класичні ланцюжки успадкування OO (які не змінюються під час виконання). Ви можете імітувати це без цього new
, але успадкування буде видозміненим. Добре? Погано? До вас.
Notice that this pattern is deprecated!
. Який правильний сучасний зразок для встановлення прототипу класу?
Припустимо, у вас є ця функція:
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()
це призведе до об'єкта, що внутрішній прототип вказує на Object
об'єкт: jsfiddle.net/Mk42Z
Object(ret) === ret
.
typeof
тест просто полегшує розуміння того, що відбувається за лаштунками.
спробуйте наступний код на консолі браузера.
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;
дає такий же вихід.
тому, ймовірно, це не для створення примірників об'єкта
Він використовується саме для цього. Ви визначаєте конструктор функцій так:
function Person(name) {
this.name = name;
}
var john = new Person('John');
Однак додатковою перевагою, яку має ECMAScript, є те, що ви можете розширити .prototype
власність, тому ми можемо зробити щось на кшталт ...
Person.prototype.getName = function() { return this.name; }
Усі об’єкти, створені з цього конструктора, тепер матимуть свою getName
силу через прототип ланцюга, до якого вони мають доступ.
class
ключового слова немає, але ви можете зробити те саме.
JavaScript - об'єктно-орієнтована мова програмування, яка використовується саме для створення екземплярів. Це на прототипі, а не на основі класів, але це не означає, що воно не орієнтоване на об'єкти.
Вже є кілька дуже чудових відповідей, але я розміщую новий, щоб наголосити на моєму спостереженні щодо випадку 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
станьте властивості на новоствореному об'єкті (замість об'єкта вікна). Оскільки у вас немає явного твердження про повернення, то інтерпретатор 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
станьте властивості на новоствореному об’єкті. Але цього разу у вас є явна заява про повернення, тому інтерпретатор JavaScript нічого не зробить.
Що слід зазначити у випадку III, це те, що об’єкт, який створюється завдяки new
ключовому слову, загубився з вашого радара. bar
насправді вказує на зовсім інший об'єкт, який не є тим, який інтерпретатор JavaScript створив завдяки new
ключовому слову.
Цитуючи Девіда Фланагана з JavaScripit: Постійний посібник (6-е видання), гл. 4, сторінка № 62:
Коли оцінюється вираз створення об'єкта, JavaScript спочатку створює новий порожній об'єкт, як і той, який створюється ініціалізатором об'єктів {}. Далі він викликає вказану функцію із вказаними аргументами, передаючи новий об’єкт як значення цього ключового слова. Потім функція може використовувати це для ініціалізації властивостей новоствореного об'єкта. Функції, написані для використання в якості конструкторів, не повертають значення, а значенням виразу створення об'єкта є новостворений та ініціалізований об'єкт. Якщо конструктор повертає значення об'єкта, це значення стає значенням виразу створення об'єкта, а новостворений об'єкт відкидається.
--- Додаткова інформація ---
Функції, які використовуються в фрагменті коду вищезазначених випадків, мають спеціальні назви у світі JS, як показано нижче:
Випадок I та II - Функція конструктора
Випадок III - Заводська функція. Фабричні функції не слід використовувати з new
ключовим словом, яке я робив для пояснення концепції в поточній темі.
Про різницю між ними ви можете прочитати в цій темі.
іноді код простіше, ніж слова:
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
?
new
Ключове слово змінює контекст , в якому функція часу запуску і повертає покажчик на цей контекст.
Якщо ви не використовуєте new
ключове слово, контекст, під яким Vehicle()
запускається функція, є тим самим контекстом, з якого ви викликаєте Vehicle
функцію. this
Ключове слово буде посилатися на той же контекст. При використанні new Vehicle()
створюється новий контекст, тому ключове слово this
всередині функції посилається на новий контекст. Що ви отримуєте взамін - це новостворений контекст.
new
Ключове слово для створення нових екземплярів об'єктів. І так, JavaScript - це динамічна мова програмування, яка підтримує об'єктно-орієнтовану парадигму програмування. Конвенція про іменування об'єктів полягає в тому, що завжди використовуйте велику літеру для об'єктів, які повинні бути створені новим ключовим словом.
obj = new Element();
new
Ключове слово використовується в JavaScript для створення об'єкта з функції конструктора. new
Ключове слово повинно бути перед викликом функції конструктори і буде робити такі речі:
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.prototypethis
значенням, встановленим для нового об’єкта. На цьому кроці є те, де вік призначається новоствореному об’єкту собачки.new
Ключові слова створюють екземпляри об'єктів з використанням функції в якості конструктора. Наприклад:
var Foo = function() {};
Foo.prototype.bar = 'bar';
var foo = new Foo();
foo instanceof Foo; // true
Екземпляри успадковуються від prototype
функції конструктора. Отже, наведений приклад вище ...
foo.bar; // 'bar'
Ну, JavaScript може сильно відрізнятися від платформи до платформи, оскільки це завжди реалізація оригінальної специфікації EcmaScript.
У будь-якому випадку незалежно від реалізації всі реалізації JavaScript, які відповідають правилу специфікації EcmaScript, дадуть вам об'єктно-орієнтовану мову. Відповідно до стандарту ES:
ECMAScript - це об'єктно-орієнтована мова програмування для виконання обчислень та маніпулювання обчислювальними об'єктами в хост-середовищі.
Отже, тепер ми погодилися, що JavaScript є реалізацією EcmaScript, і тому це об'єктно-орієнтована мова. Визначення new
операції будь-якою об'єктно-орієнтованою мовою говорить про те, що таке ключове слово використовується для створення об’єктного об'єкта з класу певного типу (включаючи анонімні типи, у випадках, таких як C #).
У EcmaScript ми не використовуємо класи, як ви можете прочитати із специфікацій:
ECMAScript не використовує класи, такі як у C ++, Smalltalk або Java. Натомість об'єкти можуть створюватися різними способами, в тому числі за допомогою буквальної позначення або за допомогою конструкторів, які створюють об'єкти, а потім виконують код, який ініціалізує всі або їх частини, присвоюючи початкові значення їх властивостям. Кожен конструктор - це функція, яка має властивість з назвою - прототип ‖, яка використовується для реалізації спадкування на основі прототипу та спільних властивостей. Об'єкти створюються за
допомогою конструкторів у нових виразах; наприклад, новий Date (2009,11) створює новий об'єкт Date. Викликати конструктор без використання нового має наслідки, які залежать від конструктора. Наприклад, Date () створює рядкове подання поточної дати та часу, а не об'єкта.