在嵌套的模块化方法中使用 "this" 并保留名称空间

Using "this" in nested, modular methods and preserving namespaces

我有一个巨大的 JavaScript 整体,并使用基于模块的方法使其更加结构化。

但是,我对如何在嵌套函数中使用“this”关键字而不相互覆盖感到有点迷茫,就像下面的示例可能演示的那样

function Fruit() {
  const self = this;

  this.fruits = [];

  this.addFruit = function (fruit) {
    self.fruits.push(fruit);
  };

  // Buy locally
  this.localStore = (function () {
    const self_localStore = this;
    this.buyArray = [];

    this.buy = function (fruit) {
      console.log("Bought locally");
      self_localStore.buyArray = this;
    };

    return this;
  })();

  // Buy from online store
  this.onlineStore = (function () {
    const self_onlineStore = this;
    this.buyArray = [];

    this.buy = function (fruit) {
      console.log("Bought online");
      self_onlineStore.buyArray = this;
    };

    return this;
  })();

  return this;
}

let fruit = new Fruit();

fruit.localStore.buy("apple"); // -> Bought online, instead of Bought locally

localStore.buy() 方法显然被 onlineStore.buy() 方法覆盖,因为它们都返回到 Fruit() 方法范围,使用相同的名称,最后一个覆盖第一个一.

那么,在不使用具有这些嵌套方法的对象的情况下允许像 onlineStore 和 localStore 这样的“名称空间”的解决方案是什么?

您正在两个函数中创建一个函数变量。但是 this.onlinestore 覆盖了 fruit 的函数变量。这与 setter 函数没有什么不同。想想你可以用两种不同的方法设置一个变量。从上到下,在在线商店功能中,您覆盖 this.buy 方法。

你可以使用下面的例子。

function Fruit() {
  const self = this;

  this.fruits = [];

  this.addFruit = function (fruit) {
    self.fruits.push(fruit);
  };

  // Buy locally
  this.localStore = () =>{
  console.log('local store');
    const self_localStore = this;
    this.buyArray = [];

    this.buy = function (fruit) {
      console.log("Bought locally");
      self_localStore.buyArray = this;
    };

    return this;
  };

  // Buy from online store
  this.onlineStore = () => {
    console.log('online store')
    const self_onlineStore = this;
    this.buyArray = [];

    this.buy = function (fruit) {
      console.log("Bought online");
      self_onlineStore.buyArray = this;
    };

    return this;
  };

  return this;
}

let fruit = new Fruit();

fruit.localStore().buy("apple"); // -> Bought online, instead of Bought locally

当你写一个IIFE时,this等于windowundefined(取决于你是否使用严格模式):

(function() { console.log(this === window); })() // True

正在为 this 关键字指定调用函数的对象,例如:

const obj = {
  fun: function() { console.log(this === obj); } 
}

obj.fun(); // True

如果你想创建一些有自己状态的命名空间,我建议你使用这样一个简单的对象:

function Fruit() {
  const self = this;

  this.fruits = [];

  this.addFruit = function (fruit) {
    self.fruits.push(fruit);
  };

  // Buy locally
  this.localStore = {
    buyArray: [],

    buy: function (fruit) {
      this.buyArray.push(fruit);
      console.log("Bought locally:", this.buyArray.join(', '));
    }

  };

  // Buy from online store
  this.onlineStore =  {
    buyArray: [],

    buy: function (fruit) {
      this.buyArray.push(fruit);
      console.log("Bought online:", this.buyArray.join(', '));
    }

  };

  return this;
}

let fruit = new Fruit();

fruit.localStore.buy("apple"); // -> Bought online, instead of Bought locally
fruit.localStore.buy("banana");
fruit.onlineStore.buy("orange");

但是,您可能不喜欢文字对象,因为它们不提供真正的封装(可以从 buy 函数外部访问 buyArray 属性)。

所以您也可以像您最初尝试做的那样将状态封装在 IIFE 中:

function Fruit() {
  const self = this;

  this.fruits = [];

  this.addFruit = function (fruit) {
    self.fruits.push(fruit);
  };

  // Buy locally
  this.localStore = (function() {
    const buyArray = [];

    const buy = function (fruit) {
      buyArray.push(fruit);
      console.log("Bought locally:", buyArray.join(', '));
    };
    
    return { buy };

  })();

  // Buy from online store
  this.onlineStore = (function() {
    const buyArray = [];

    const buy = function (fruit) {
      buyArray.push(fruit);
      console.log("Bought online:", buyArray.join(', '));
    };
    
    return { buy };

  })();

  return this;
}

let fruit = new Fruit();

fruit.localStore.buy("apple"); // -> Bought online, instead of Bought locally
fruit.localStore.buy("banana");
fruit.onlineStore.buy("orange");