Установка прототипа JavaScript функционирует в объявлении класса объекта

Обычно, я видел опытные функции, объявленные вне определения класса, как это:

function Container(param) {
    this.member = param;
}
Container.prototype.stamp = function (string) {
    return this.member + string;
}

var container1 = new Container('A');
alert(container1.member);
alert(container1.stamp('X'));

Этот код производит два предупреждения со значениями "A" и "AX".

Я хотел бы определить опытную функцию В определении класса. Есть ли что-то не так с выполнением чего-то вроде этого?

function Container(param) {
    this.member = param;
    if (!Container.prototype.stamp) {
        Container.prototype.stamp = function() {
            return this.member + string;
        }
    }
}

Я пробовал это так, чтобы я мог получить доступ к частной переменной в классе. Но я обнаружил что, если мои опытные ссылки на функцию частный var, значение частного var всегда является значением, которое использовалось, когда опытная функция была ПЕРВОНАЧАЛЬНО создана, не значение в экземпляре объекта:

Container = function(param) {
    this.member = param;
    var privateVar = param;
    if (!Container.prototype.stamp) {
        Container.prototype.stamp = function(string) {
            return privateVar + this.member + string;
        }
    }
}
var container1 = new Container('A');
var container2 = new Container('B');
alert(container1.stamp('X'));
alert(container2.stamp('X'));

Этот код производит два предупреждения со значениями "AAX" и "ABX". Я надеялся, что вывод будет "AAX" и "BBX". Мне любопытно, почему это не работает, и если существует некоторый другой шаблон, который я мог использовать вместо этого.

Править: Обратите внимание, что я полностью понимаю, что для этого простого примера было бы лучше просто использовать закрытие как this.stamp = function() {} и не используют прототип вообще. Это - то, как я сделал бы это также. Но я экспериментировал с использованием прототипа для получения дополнительной информации о нем и хотел бы знать несколько вещей:

  • Когда имеет смысл использовать опытные функции вместо закрытий? Я должен был только использовать их для расширения существующих объектов, как Date. Я считал, что закрытия быстрее.
  • Если я должен использовать опытную функцию по некоторым причинам, это "в порядке" для определения его В классе, как в моем примере, или это должно быть определено снаружи?
  • Я хотел бы понять, почему privateVar значение каждого экземпляра не доступно для опытной функции, только значение первой инстанции.

23
задан Tauren 6 May 2010 в 22:26
поделиться

4 ответа

Когда имеет смысл использовать функции-прототипы вместо закрытий?

Ну, это самый легкий способ, допустим, у вас есть метод в прототипе некоторого конструктора, и вы создаете 1000 экземпляров объектов, все эти объекты будут иметь ваш метод в своей цепочке прототипов, и все они будут ссылаться только на одну функцию-объект.

Если вы инициализируете этот метод внутри конструктора, например, (this.method = function () {};), все ваши 1000 экземпляров объектов будут иметь объект-функцию в качестве собственного свойства.

Если мне зачем-то нужно использовать прототип функции, нормально ли определять его ВНУТРИ класса, как в моем примере, или его нужно определять снаружи?

Определение членов прототипа конструктора внутри себя не имеет особого смысла, я объясню вам подробнее, почему ваш код не работает.

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

Посмотрим на ваш код:

var Container = function(param) {
    this.member = param;
    var privateVar = param;
    if (!Container.prototype.stamp) {  // <-- executed on the first call only
        Container.prototype.stamp = function(string) {
            return privateVar + this.member + string;
        }
    }
}

Ключевым моментом в поведении вашего кода является то, что функция Container.prototype.stamp создается при первом вызове метода.

В момент создания объекта функции он сохраняет текущую объемлющую область видимости во внутреннем свойстве под названием [[Scope]].

Эта область видимости позже расширяется при вызове функции идентификаторами (переменными), объявленными в ней с помощью var или FunctionDeclaration.

Список свойств [[Scope]] образует цепочку scope, и когда вы обращаетесь к идентификатору (например, к вашей переменной privateVar), эти объекты рассматриваются.

А поскольку ваша функция была создана при первом вызове метода (new Container('A')), privateVar привязана к области видимости этого первого вызова функции, и она останется привязанной к ней, как бы вы ни вызывали метод.

Посмотрите этот ответ, первая часть посвящена оператору with, а во второй части я рассказываю о том, как работает цепочка scope для функций.

22
ответ дан 29 November 2019 в 02:18
поделиться

Чтобы получить желаемое поведение, вам нужно назначить каждый отдельный объект отдельно stamp () функции с уникальными замыканиями:

Container = function(param) {
    this.member = param;
    var privateVar = param;
    this.stamp = function(string) {
        return privateVar + this.member + string;
    }
}

Когда вы создаете одну функцию на прототипе, каждый объект использует одну и ту же функцию, при этом функция закрывается поверх самой первой privateVar контейнера.

Назначая this.stamp = ... каждый раз, когда вызывается конструктор, каждый объект получит свою собственную функцию stamp () . Это необходимо, поскольку каждая штамп () должна закрывать другую переменную privateVar .

1
ответ дан 29 November 2019 в 02:18
поделиться

Вам нужно поместить функцию в каждый конкретный экземпляр вместо прототипа, например:

Container = function(param) {
    this.member = param;
    var privateVar = param;

    this.stamp = function(string) {
        return privateVar + this.member + string;
    }
}
1
ответ дан 29 November 2019 в 02:18
поделиться

Это потому, что privateVar не является частным членом объекта, а является частью закрытия штампа. Вы можете получить эффект, всегда создавая функцию:

Container = function(param) {
    this.member = param;
    var privateVar = param;
    this.stamp = function(string) {
      return privateVar + this.member + string;
    }
}

Значение privateVar устанавливается при построении функции, поэтому вам нужно создавать его каждый раз.

РЕДАКТИРОВАТЬ: изменено, чтобы не устанавливать прототип.

0
ответ дан 29 November 2019 в 02:18
поделиться
Другие вопросы по тегам:

Похожие вопросы: