Прежде всего, помните, что JavaScript - это в основном язык прототипа , а не язык на основе классов 1 .Foo
это не класс, это функция, которая является объектом.Вы можете создать экземпляр объекта из этой функции, используя ключевое слово new
, которое позволит вам создать нечто похожее на класс на стандартном языке ООП.
Я бы предложил игнорировать __proto__
большую часть времени, потому что он плохо поддерживает кросс-браузер, и вместо этого сосредоточиться на изучении того, как работает prototype
.
Если у вас есть экземпляр объекта, созданный из функции 2 и вы получаете доступ к одному из его членов (методы, атрибуты, свойства, константы и т. д.) любым способом, доступ будет проходить по иерархии прототипа, пока он либо (а) не найдет элемент, либо (б) не найдет другой прототип.
Иерархия начинается с вызываемого объекта, а затем ищет его объект-прототип.Если у объекта-прототипа есть прототип, он повторяется, если прототипа не существует, возвращается 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(){...}
как определение открытого статического method для класса.
В ECMAScript 2015 введены различные синтаксические сахара для объявлений такого рода, чтобы их было проще реализовать, а также было легче читать.Поэтому предыдущий пример можно записать в виде:
class Foo {
bar() {...}
static baz() {...}
}
, что позволяет bar
вызываться как:
const f = new Foo()
f.bar()
и baz
вызываться как:
Foo.baz()
1: class
было «Future Reserved Word» в спецификации ECMAScript 5 , но ES6 предоставляет возможность определять классы с помощью ключевого слова class
.
2: по сути, экземпляр класса, созданный конструктором, но есть много нюансов, которые я не хочу вводить в заблуждение
3: примитивные значения - которые включают undefined
, null
, логические значения, числа и строки - технически не являются объектами, поскольку они являются низкоуровневыми реализациями языка.Булевы числа, числа и строки по-прежнему взаимодействуют с цепочкой прототипов, как будто они являются объектами, поэтому для целей этого ответа проще считать их «объектами», даже если они не совсем.