Совместное использование переменных закрытия JavaScript между экземплярами - PullRequest
0 голосов
/ 27 февраля 2019

Вот код JavaScript (или JSFiddle ).Я хочу заставить каждый экземпляр Test использовать собственную ссылку на переменную self.Как мне этого добиться?

(function (w) {
  var self;

  w.Test = function (name) {
    // how to redeclare `self` here to not use it by reference? This doesn't help:
    // self = undefined;
    // delete self;

    self = this;
    self.name = name;
  };

  w.Test.prototype.getName = function () {
    return self.name;
  }

  w.Test.prototype.test = function () {
    console.debug(self.name);
    console.debug(self == this);
  }

})(window);

var a = new Test("a");
var b = new Test("b");

console.log(a.getName() + b.getName());

// expected: ab
// actual: bb

a.test();
b.test();

// expected: a > true > b > true
// actual: b > false > b > true

Переопределение второго вызова self переменная.Как я могу получить ожидаемый результат?Я понимаю, что общим решением является использование локального self метода внутри каждого метода Test, но есть ли другой способ сделать это без такого дублирования?Есть ли способ переопределить переменную замыкания?

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

1 Ответ

0 голосов
/ 28 февраля 2019

В javascript замыкание является обобщением концепции глобальной переменной - это переменная, видимая в нескольких областях.Глобальная переменная фактически может рассматриваться как замыкание - в глобальной области видимости.Нет никакого противоречия или путаницы в вызовах глобальных переменных (хотя некоторые движки могут реализовывать их по-разному, на самом деле они могут быть реализованы с использованием точно такого же механизма).

Для иллюстрации:

var a;
var b;

(function () {
  var shared; // a shared variable behaving like a sub-global variable

  a = function (x) {shared = x}
  b = function () {return shared}

})()

function c {return shared};

a(100);
b(); // returns 100
c(); // errors out because it is not part of the shared scope.

Функции создают экземпляры областей, к сожалению, техническое имя экземпляров называется замыканиями, потому что слово замыкание также относится к основному алгоритму для создания такой вещи, а неофициально - к переменной, захваченной замыканием (техническое имя для неепеременная вложена, но люди часто просто говорят "закрытие").ООП, с другой стороны, имеют совершенно разные слова для каждого понятия - классы, экземпляры, экземпляры, свойства и методы.

Копии области действия

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

function makeShared () {
  var shared // this thing will have more than one copy/instance in RAM

  return {
    a: function (x) {shared = x},
    b: function () {return shared}
  }
}

var x = makeShared();
var y = makeShared();

x.a(100);
y.a(200);
x.b();     // returns 100
y.b();     // returns 200

Как вы можете видеть, в теории замыкания и объекты концептуально похожи.На самом деле есть бумага, в которой заявляется, что это одно и то же.С почти нулевым ООП мы создали объектную систему ( почти только потому, что мы возвращаем литерал объекта, но если js подобен Perl или PHP с реальными картами / хешами / ассоциативными массивами, мы можем сделать это с нулевым ООП).

TLDR

Так как же мы можем получить то, что вы хотите?Что ж, мы можем использовать шаблон проектирования в js, который называется шаблон модуля (не путать с модулями js).На самом деле это код makeShared, который я иллюстрирую выше - мы отказываемся от возможностей ООП в js в пользу изобретения собственной системы ООП с использованием функционального программирования.

Ваш код будет выглядеть примерно так в шаблоне модуля:

function newTest (name) { // in case you like the word "new"

  var self = {};
  self.name = name;

  self.getName = function () {
    return self.name;
  }

  self.test = function () {
    console.debug(self.name);
    console.debug(self == this);
  }

  return self;
};

var a = newTest("a"); // note: newTest instead of new Test
var b = newTest("b");

console.log(a.getName() + b.getName());

В дни возрождения javascript, когда люди начали воспринимать его всерьез как язык программирования (примерно через 10 лет после его создания), шаблон модуля стал любимым среди поклонников языка.В отличие от прототипа, он выглядит как класс, и все определение заключено в пару фигурных скобок {}.Вы даже можете свернуть свою собственную систему наследования (с методами, начиная от паразитического наследования и заканчивая клонированием прототипа).Но это нарушает функциональность instanceof.Для большинства людей это был хороший компромисс, потому что большинство считает код, который должен знать о своих типах, своего рода запахом кода.Но библиотекам часто приходится делать такие вещи.

TLDR 2

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

function Test (name) {
  var self = this;
  self.name = name;

  self.getName = function () {
    return self.name;
  }

  self.test = function () {
    console.debug(self.name);
    console.debug(self == this);
  }

  // constructors don't need to return anything
};

var a = new Test("a"); // note: new
var b = new Test("b");

console.log(a.getName() + b.getName());

Основное возражение против этого стиля программирования состоит в том, что теперь у вас есть несколько экземпляров getName и test, поэтому он не так эффективен в использовании памяти, как использование прототипа(это также относится к шаблону модуля).Но некоторые люди сочли бы это хорошим компромиссом.

TLDR 3

На самом деле вам вообще не нужно псевдоним this.Все, что вам нужно сделать, это понять, как это работает, и жить с этим (прочитайте мой ответ на этот другой вопрос: Как ключевое слово "this" в Javascript действует внутри литерала объекта? , я обещаю сохранитьответ обновляется, пока я жив).

Удалите все вещи self и просто используйте this.Для асинхронных обратных вызовов вам просто нужно знать, как правильно вызывать ваши методы:

var a = new Test("a");

// either:

someAsyncThing(function() {
  a.getName();
});

// or:

anotherAsyncThing(a.getName.bind(a));

// with promises:

promiseSomething.then(a.getName.bind(a));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...