【问题标题】:What underlies this JavaScript idiom: var self = this?这个 JavaScript 习语的基础是什么:var self = this?
【发布时间】:2015-06-01 07:06:34
【问题描述】:

我在WebKit HTML 5 SQL Storage Notes Demo的源代码中看到了以下内容:

function Note() {
  var self = this;

  var note = document.createElement('div');
  note.className = 'note';
  note.addEventListener('mousedown', function(e) { return self.onMouseDown(e) }, false);
  note.addEventListener('click', function() { return self.onNoteClick() }, false);
  this.note = note;
  // ...
}

作者在某些地方(函数体)使用self,在其他地方(方法参数列表中定义的函数体)使用this。这是怎么回事?现在我已经注意到了一次,我会开始到处看到它吗?

【问题讨论】:

标签: javascript scope closures


【解决方案1】:

看到这个article on alistapart.com。 (编者:文章自最初链接后已更新)

self 被用于维护对原始this 的引用,即使上下文正在发生变化。这是事件处理程序中经常使用的一种技术(尤其是在闭包中)。

编辑:请注意,现在不鼓励使用self,因为window.self 存在并且如果您不小心可能会导致错误。

您对变量的称呼并不特别重要。 var that = this; 很好,但这个名字并没有什么神奇之处。

在上下文中声明的函数(例如回调、闭包)将可以访问在相同范围或更高范围内声明的变量/函数。

例如一个简单的事件回调:

function MyConstructor(options) {
  let that = this;

  this.someprop = options.someprop || 'defaultprop';

  document.addEventListener('click', (event) => {
    alert(that.someprop);
  });
}

new MyConstructor({
  someprop: "Hello World"
});

【讨论】:

  • 出现 that article 变成使用 var that = this;
  • @BobStein 谢谢。我会相应地更新答案。
【解决方案2】:

该变量由方法中定义的内联函数捕获。函数中的this 将引用另一个对象。这样,您可以使函数在外部范围内持有对this 的引用。

【讨论】:

    【解决方案3】:

    是的,你会在任何地方看到它。通常是that = this;

    看看self是如何在事件调用的函数中使用的?这些将有自己的上下文,因此self 用于保存进入Note()this

    self 仍然可供函数使用的原因,即使它们只能在 Note() 函数完成执行后执行,是由于 导致内部函数获取外部函数的上下文关闭

    【讨论】:

    • 对我来说,有说服力的一点是self 没有特殊含义。我个人更喜欢使用命名为 self 以外的变量,因为它经常让我感到困惑,因为我希望 'self' 是一个保留字。所以我喜欢你的回答。在 OP 的示例中,我更喜欢 var thisNote = this 或类似的。
    • @steve 同意,尽管我尽量避免使用 this/self 引用,因为它们在可维护性方面非常脆弱。
    【解决方案4】:

    还应注意,如果您不喜欢 var self = this 惯用语,还有一种替代代理模式可用于在回调中维护对原始 this 的引用。

    由于可以使用function.applyfunction.call 在给定的上下文中调用函数,因此您可以编写一个包装器来返回一个函数,该函数使用给定的上下文使用applycall 调用您的函数。有关此模式的实现,请参阅 jQuery 的 proxy 函数。这是一个使用它的例子:

    var wrappedFunc = $.proxy(this.myFunc, this);

    然后可以调用wrappedFunc 并将您的this 版本作为上下文。

    【讨论】:

      【解决方案5】:

      我认为不应该再以这种方式使用变量名“self”,因为现代浏览器提供了一个global variable self 指向普通窗口或 WebWorker 的全局对象。

      为避免混淆和潜在冲突,您可以改写var thiz = thisvar that = this

      【讨论】:

      • 我一般用_this
      • @djheru +1。比“that”好多了(我的大脑永远不会习惯)。
      • 我开始使用“我”:)
      • 直到现代浏览器开始提供全局变量 _this、that 或 me。
      • 使用名称self 绝对没有问题,只要您将其声明为variable,它就会影响全局。当然,如果您忘记了 var,那么它也不会与任何其他名称一起使用。
      【解决方案6】:

      实际上 self 是对窗口 (window.self) 的引用,因此当您说 var self = 'something' 时,您会覆盖对自身的窗口引用 - 因为 self 存在于窗口对象中。

      这就是为什么大多数开发者更喜欢var that = this 而不是var self = this;

      无论如何; var that = this; 不符合良好实践...假设您的代码稍后将被其他开发人员修改/修改,您应该使用开发人员社区中最常见的编程标准

      因此,您应该使用类似 var oldThis / var oThis / 等的东西 - 在您的范围内明确 // ..不是那么多,但会节省几秒钟和几个大脑周期

      【讨论】:

      • @prior 我认为直到最后一段才有意义。
      【解决方案7】:

      正如上面多次提到的,“self”只是用于在进入函数之前保留对“this”的引用。一旦在函数中,“this”指的是别的东西。

      【讨论】:

      • @JohnPaul ...这确实提供了答案。这可能不是正确的答案,但你怎么能说“它被用来……”不是“他为什么这样做”的答案?
      【解决方案8】:

      这是一个 JavaScript 怪癖。当函数是对象的属性(更恰当地称为方法)时,this 指的是对象。在事件处理程序的示例中,包含对象是触发事件的元素。当调用标准函数时,this 将引用全局对象。当您在示例中具有嵌套函数时, this 根本与外部函数的上下文无关。内部函数确实与包含函数共享范围,因此开发人员将使用 var that = this 的变体,以便在内部函数中保留他们需要的 this

      【讨论】:

        【解决方案9】:

        正如其他人所解释的,var self = this; 允许 closure 中的代码引用父范围。

        然而,现在是 2018 年,ES6 得到了所有主要网络浏览器的广泛支持。 var self = this; 习语不像以前那么重要了。

        现在可以通过使用arrow functions 来避免var self = this;

        在我们会使用var self = this的情况下:

        function test() {
            var self = this;
            this.hello = "world";
            document.getElementById("test_btn").addEventListener("click", function() {
                console.log(self.hello); // logs "world"
            });
        };
        

        我们现在可以在没有var self = this 的情况下使用箭头函数:

        function test() {
            this.hello = "world";
            document.getElementById("test_btn").addEventListener("click", () => {
                console.log(this.hello); // logs "world"
            });
        };
        

        箭头函数没有自己的this,只是假设封闭范围。

        【讨论】:

        • 或者——震惊,恐怖! – 为什么不将实际相关的东西作为参数传递给您的函数(闭包)?你为什么要引用超出范围的状态,为什么会有人像这样编程?没有任何真正的理由这样做。取而代之的是 .addEventListender("click", (x) => { console.log(x); }); 你已经非常清楚地解释了如何以及为什么,我同意使用箭头函数更有意义,但仍然......这只是可怕、懒惰、混乱的编程。
        • 在 JavaScript 中,回溯到父作用域是非常普遍和必要的。它是语言的基本部分。您将父范围作为事件处理程序的参数传递的建议实际上是不可能的。此外,在 ES6 中,箭头函数使用词法作用域——“this”指的是它当前的周围作用域,而不是进一步的——它不是“引用超出作用域的状态”或类似的东西。
        【解决方案10】:
        function Person(firstname, lastname) {
          this.firstname = firstname;
        
          this.lastname = lastname;
          this.getfullname = function () {
            return `${this.firstname}   ${this.lastname}`;
          };
        
          let that = this;
          this.sayHi = function() {
            console.log(`i am this , ${this.firstname}`);
            console.log(`i am that , ${that.firstname}`);
          };
        }
        
        let thisss = new Person('thatbetty', 'thatzhao');
        
        let thatt = {firstname: 'thisbetty', lastname: 'thiszhao'};
        

        thisss.sayHi.call(thatt);

        【讨论】:

        • 你应该用你所做的特别的代码添加一些解释。
        猜你喜欢
        • 2021-06-09
        • 2012-07-19
        • 1970-01-01
        • 1970-01-01
        • 2011-05-21
        • 2010-09-25
        • 2011-11-06
        相关资源
        最近更新 更多