По-перше, пам’ятайте, що JavaScript - це передусім прототипна мова , а не мова на основі класу 1 . Foo
це не клас, це функція, яка є об'єктом. Ви можете створити об'єкт із цієї функції за допомогою new
ключового слова, яке дозволить вам створити щось подібне до класу на стандартній мові OOP.
Я б запропонував ігнорувати __proto__
більшу частину часу, тому що він має погану підтримку перехресного веб-переглядача, а замість цього зосередитись на вивченні того, як prototype
працює.
Якщо у вас є примірник об'єкта, створеного з функції 2, і ви будь-яким чином отримуєте доступ до одного з його членів (методів, атрибутів, властивостей, констант тощо), доступ буде протікати вниз по ієрархії прототипу, поки він (a) не знайде член, або (b) не знайде іншого прототипу.
Ієрархія запускається на об'єкт, який був викликаний, а потім здійснює пошук його об’єкта-прототипу. Якщо об'єкт прототипу має прототип, він повторюється, якщо прототип не існує, undefined
повертається.
Наприклад:
foo = {bar: 'baz'};
console.log(foo.bar); // logs "baz"
foo = {};
console.log(foo.bar); // logs undefined
function Foo(){}
Foo.prototype = {bar: 'baz'};
f = new Foo();
console.log(f.bar);
// logs "baz" because the object f doesn't have an attribute "bar"
// so it checks the prototype
f.bar = 'buzz';
console.log( f.bar ); // logs "buzz" because f has an attribute "bar" set
Мені здається, ви вже хоча б дещо зрозуміли ці "основні" частини, але мені потрібно зробити їх явними лише для того, щоб бути впевненим.
У JavaScript все є об'єктом 3 .
все є об’єктом.
function Foo(){}
не просто визначає нову функцію, вона визначає новий об’єкт функції, до якого можна отримати доступ Foo
.
Ось чому ви можете отримати доступ Foo
до прототипу за допомогою Foo.prototype
.
Те , що ви також можете зробити , це встановити додаткові функції по Foo
:
Foo.talk = function () {
alert('hello world!');
};
Доступ до цієї нової функції можна за допомогою:
Foo.talk();
Я сподіваюся, що зараз ви помітили подібність між функціями на об’єкті функції та статичним методом.
Розгляньте це f = new Foo();
як створення екземпляра класу, Foo.prototype.bar = function(){...}
як визначення загального методу для класу та Foo.baz = function(){...}
як визначення публічного статичного методу для класу.
ECMAScript 2015 представила різноманітні синтаксичні цукру для таких видів декларацій, щоб зробити їх більш простими в застосуванні, а також було легше читати. Тому попередній приклад можна записати як:
class Foo {
bar() {...}
static baz() {...}
}
що дозволяє bar
називатися як:
const f = new Foo()
f.bar()
і baz
називатися так:
Foo.baz()
1: class
було "Майбутнім зарезервованим словом" у специфікації ECMAScript 5 , але ES6 запроваджує можливість визначати класи за допомогою class
ключового слова.
2: по суті, екземпляр класу, створений конструктором, але є багато нюансованих відмінностей, які я не хочу вас вводити в оману
3: примітивні значення - що включають undefined
, null
булеві числа, числа та рядки - технічно не є об'єктами, оскільки вони є мовними реалізаціями низького рівня. Булеви, числа та рядки все ще взаємодіють з ланцюгом прототипу так, ніби вони були об'єктами, тому для цілей цієї відповіді легше вважати їх "об'єктами", хоча вони не зовсім.
Foo.talk = function ...