По-перше, пам’ятайте, що 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 ...