2016-02-17 3 views
1

Используя следующую ES2015 (ES6) определение класса:Как получить доступ к теневому объекту JavaScript-объекта на прототипе?

class Card { 
    constructor(val) { 
     this.val = val; 
    } 
    val(){ 
     return this.val; 
    } 
} 

(или эквивалент ES5 и более ранних версий кода)

Однако это метод следующий вызов:

new Card(val).val() 

кидает TypeError: Card.val is not a function

Card.x.call(Card) 

похоже на имеют ту же проблему.

Учитывая, что оба имени должны оставаться неизменными, как я могу правильно ссылаться на метод прототипа val вместо поля val объекта?

ответ

3

Я просто пытаюсь получить вдохновение из того, как мы можем эффективно избежать такого конфликта в javascript, предполагая, что мы должны называть их таким образом.

Чтобы избежать путаницы, давайте называть val вас на прототипе «прото val» и один присвоенным в конструктор «например val».

Вы не можете иметь свойство на объекте («экземпляр val»), имеющее то же имя, что и свойство на его прототипе («proto val»), и получить доступ к ним непосредственно через ссылку на объект, получить один или другой в зависимости от контекста. Это просто не функция JavaScript. Это неотъемлемо от того, как работает поиск свойств JavaScript, и тот факт, что «методы» JavaScript - это просто свойства, относящиеся к функциям.

Варианты ниже, но давайте более внимательно рассмотрим, почему «экземпляр val» отменяет «прото val»:

С любым из исправленных версий кода выше, эта строка:

var c = new Card(42); 

дает нам это в памяти (некоторые детали опущены):

 
     +------------------------------------------+ 
     |           | 
     \ +------------+       | 
Card>--+->| (function) |       | 
      +------------+  +-------------+ | 
      | prototype |>---+->| (object) | | 
      +------------+ / +-------------+ | 
          | | constructor |>--+ +------------+ 
          | | val   |>----->| (function) | 
          | +-------------+  +------------+ 
          | 
     +---------------+ | 
c>--->| (object) | | 
     +---------------+ | 
     | [[Prototype]] |>---+ 
     | val: 42  | 
     +---------------+ 

идентификатор Card (фактически переменная) относится к функц ион. Свойство этой функции prototype относится к объекту, на который мы положили метод «proto val». Переменная c относится к экземпляру, в котором спецификация называет «внутренний слот» под названием [[Prototype]], который ссылается на его прототип, который он получил от Card.prototype, когда мы сделали new Card. Прототип имеет свойство val, указывающее на метод, а также свойство constructor, которое указывает на обратную функцию Card.

Когда мы попросим механизм JavaScript найти объект val на c, он находит его на объекте c, ссылается на него и использует его оттуда; «instance val» переопределил «proto val».Если «экземпляр val» не был там, механизм JavaScript не нашел бы его на объекте c точек и не посмотрел бы на прототип объекта (на что указывает внутренний слот объекта [[Prototype]]), а затем он найдет свойство, указывающее на функцию. Но «пример val» мешает.

Вы в основном есть три варианта:

  1. делать то, что вы сказали, что вы не хотите делать: давать им разные имена. Свойство данных («экземпляр val») может быть, например, _val, или метод («proto val») может быть getVal (поскольку имена методов обычно должны быть глаголами) или оба (что было бы довольно распространенным) и т. Д.

  2. Создать метод val в конструкторе и не имеют свойство val данных на всех:

    // ES2015 (ES6) and higher 
    class Card { 
        constructor(val) { 
         this.val = function() { 
          return val; 
         }; 
        } 
    } 
    

    или

    // ES5 and earlier 
    function Card(val) { 
        this.val = function() { 
         return val; 
        }; 
    } 
    

    Поскольку данные val больше не является свойством экземпляра, нет конфликта с методом val.

    Это работает, потому что метод val теперь является закрытием по контексту вызова Card, и поэтому он имеет устойчивый доступ к аргументу val. Однако никакие прототипные методы не имели бы прямого доступа к нему; они должны будут использовать this.val(), чтобы получить его.

  3. Делайте то, что вы делаете сейчас и посмотреть на "прото val" на прототипе, а не на экземпляре:

    var c = new Card(val).val() 
    Object.getPrototypeOf(c).val.call(c); // ES5+ 
    

    или (менее надежный)

    var c = new Card(val).val() 
    Card.prototype.val.call(c); 
    

    или (еще менее надежный)

    var c = new Card(val).val() 
    c.constructor.prototype.val.call(c); 
    

    ... который, как вы можете видеть, это довольно болезненно. Это работает, потому что в этих примерах я не ищу «proto val» на c (потому что «экземпляр val» мешает); Я смотрю его прямо на прототипе c (определенно, в первом примере: , вероятно, во втором примере; , надеюсь, в третьем).

Смежные вопросы