Как работает JavaScript .prototype?

Каждый объект JavaScript vanillajs has an internal "slot" вызвал [[Prototype]], значение которого vanilla-js равно null или object. Вы можете думать prototype-oriented о слоте как о свойстве объекта, внутреннем javascript-library для движка JavaScript, скрытом ecmascript от кода, который вы пишете. Квадратные js скобки вокруг [[Prototype]] являются преднамеренными dynamic-languages и являются соглашением спецификации vanilla-javascript ECMAScript для обозначения javascript внутренних слотов.

Значение, на javascript-execution которое указывает [[Prototype]] объекта, в javascript просторечии известно как javascript-library «прототип этого объекта».

Если prototype-oriented вы обращаетесь к свойству javascript-library через обозначение точки (obj.propName) или vanilla-javascript скобки (obj['propName']), а объект напрямую vanilla-javascript не имеет такого свойства vanilla-javascript (т. е. собственное свойство, можно отметить via javascript-execution obj.hasOwnProperty('propName')), среда выполнения вместо vanillajs этого ищет свойство с таким javascript-execution именем в объекте, на который javascript-dom ссылается [[Prototype]]. Если [[Prototype]] также не имеет javascript-execution такого свойства, его [[Prototype]] проверяется javascript-execution по очереди и т. Д. Таким vanilla-js образом, цепочка прототипов исходного объекта javascript просматривается до тех пор, пока vanillajs не будет найдено совпадение ecmascript или не будет достигнут его ecmascript конец. В верхней части цепочки dynamic-languages прототипов находится значение vanilla-js null.

Современные реализации JavaScript javascript-library предоставляют доступ для js чтения и / или записи к [[Prototype]] следующими javascript-library способами:

  1. Оператор new (настраивает цепочку прототипов для объекта по умолчанию, возвращаемого функцией конструктора),
  2. Ключевое слово extends (настраивает цепочку прототипов при использовании синтаксиса класса),
  3. Object.create установит предоставленный аргумент как [[Prototype]] полученного объекта,
  4. Object.getPrototypeOf и Object.setPrototypeOf (получить / установить [[Prototype]] после создания объекта) и
  5. Стандартизированное свойство средства доступа (например, средство получения / установки) с именем __proto__ (аналогично 4.)

Object.getPrototypeOf и Object.setPrototypeOf предпочтительнее, чем javascript-execution __proto__, отчасти из-за поведения vanilla-js o.__proto__ is unusual, когда объект имеет прототип ecmascript null.

[[Prototype]] объекта изначально устанавливается vanilla-javascript во время создания объекта.

Если .js вы создаете новый объект vanilla-javascript с помощью new Func(), по умолчанию dynamic-languages в качестве объекта [[Prototype]] будет vanilla-javascript задан объект, на который javascript-dom ссылается Func.prototype.

Обратите внимание, что dynamic-languages поэтому все классы и все функции, которые можно использовать с оператором new, имеют свойство с именем .prototype в дополнение к их собственному внутреннему слоту [[Prototype]]. Это двойное употребление dynamic-languages слова «прототип» является ecmascript источником нескончаемой путаницы vanilla-javascript среди новичков в языке.

Использование vanillajs new с функциями конструктора vanilla-javascript позволяет моделировать классическое javascript-execution наследование в JavaScript; хотя javascript-execution система наследования в JavaScript, как prototype-oriented мы видели, является прототипной, а javascript-library не классовой.

До введения javascript-execution синтаксиса классов в JavaScript dynamic-languages функции-конструкторы были js единственным способом имитировать javascript классы. Мы можем рассматривать js свойства объекта, на который vanilla-javascript ссылается свойство .prototype функции-конструктора, как js общие члены; т.е. члены, которые .js одинаковы для каждого экземпляра. В prototype-oriented системах на основе классов javascript-dom методы реализуются одинаково vanilla-javascript для каждого экземпляра, поэтому .js методы концептуально добавляются js к свойству .prototype; однако поля ecmascript объекта зависят от экземпляра vanilla-javascript и поэтому добавляются к самому dynamic-languages объекту во время создания.

Без .js синтаксиса класса разработчикам vanilla-javascript приходилось вручную настраивать javascript-dom цепочку прототипов для достижения dynamic-languages функциональности, аналогичной javascript-execution классическому наследованию. Это vanillajs привело к преобладанию различных vanillajs способов достижения этой javascript-dom цели.

Вот один способ:

function Child() {}
function Parent() {}
Parent.prototype.inheritedMethod = function () { return 'this is inherited' }

function inherit(child, parent) {
  child.prototype = Object.create(parent.prototype)
  child.prototype.constructor = child
  return child;
}

Child = inherit(Child, Parent)
const o = new Child
console.log(o.inheritedMethod()) // 'this is inherited'

... а ecmascript вот еще один способ:

function Child() {}
function Parent() {}
Parent.prototype.inheritedMethod = function () { return 'this is inherited' }

function inherit(child, parent) {
    function tmp() {}
    tmp.prototype = parent.prototype
    const proto = new tmp()
    proto.constructor = child
    child.prototype = proto
    return child
}

Child = inherit(Child, Parent)
const o = new Child
console.log(o.inheritedMethod()) // 'this is inherited'

Синтаксис vanilla-js класса, представленный в .js ES2015, упрощает ситуацию, предоставляя dynamic-languages extends как «единственный верный js способ» настроить цепочку vanillajs прототипов для имитации классического vanilla-javascript наследования в JavaScript.

Итак, аналогично javascript-dom приведенному выше коду, если javascript-execution вы используете синтаксис ecmascript класса для создания нового javascript-execution объекта следующим образом:

class Parent { inheritedMethod() { return 'this is inherited' } }
class Child extends Parent {}

const o = new Child
console.log(o.inheritedMethod()) // 'this is inherited'

... результирующий javascript-library объект [[Prototype]] будет установлен vanillajs на экземпляр Parent, чей [[Prototype]], в свою vanillajs очередь, равен Parent.prototype.

Наконец, если .js вы создаете новый объект prototype-oriented с помощью Object.create(foo), для результирующего javascript-library объекта [[Prototype]] будет установлено dynamic-languages значение foo.

javascript

dynamic-languages

prototype-oriented

2022-10-06T19:51:31+00:00