【问题标题】:Javascript - ScopesJavascript - 范围
【发布时间】:2013-10-23 21:28:30
【问题描述】:
<script type="text/javascript">
function BigComputer(answer) {
   this.the_answer = answer;
   this.ask_question = function () {
   alert(this.the_answer);
   }
}

 function addhandler() {
   var deep_thought = new BigComputer(42),
   the_button = document.getElementById('thebutton');

   the_button.onclick = deep_thought.ask_question;
}

window.onload = addhandler;
 </script> 

为什么这个脚本返回 undefined 而不是 42? 我是这样理解的:
1) 在网页加载时,我们运行“addhandler”方法。
2) 在这个方法中,我们为 deep_thought 变量创建 BipComputer 的实例。我们还在答案中设置了 42(BipComputer 类中的公共变量)
3) 然后我们为按钮设置引用(在 html 文件中)
4) 按钮在点击时激活,神奇之处来了:
我认为应该打印出数字 42,因为我们只是在第 2 步中为此进行了设置,但是变量是“未定义”?

另一个问题是,在没有 ()(没有要传递的参数时)和使用 () 的情况下运行方法有什么区别。 [类似于 addhandler 方法]。

也许这个问题很简单,但我以前用java和php编程,现在面向javascript编程的思维方式对我来说很奇怪。函数看起来像类,类看起来像函数,没有真正的构造函数之类的东西。
不过没关系,让我们回到问题上来:)
非常感谢任何帮助。




@编辑

感谢@lbstr,我找到了有用的链接,适用于与我提出相同问题的每个人。 http://web.archive.org/web/20080209105120/http://blog.morrisjohns.com/javascript_closures_for_dummies

【问题讨论】:

  • addhandler 在该示例中的任何地方都没有被显式调用。它作为对window.onload 的引用传递给浏览器在适当的时间调用。唯一可以在没有() 的情况下执行函数的情况是当您将其作为构造函数调用时。
  • @zzzzBov — 该函数被添加为按钮的单击侦听器,因此它被按钮的单击处理程序调用。
  • @RobG,让我重新表述一下,只有在没有() 的情况下才能显式执行一个函数,就是将它作为构造函数调用(即new Foo)。任何其他情况都需要调用浏览器函数或行为来为您调用该函数。

标签: javascript oop methods event-handling


【解决方案1】:

当点击处理程序被触发时,this 是按钮。以下是您想要执行的操作:

function BigComputer(answer) {
    this.the_answer = answer;
    var self = this;
    this.ask_question = function() {
        alert(self.the_answer);
    };
}

这一切都得益于闭包的美妙之处。许多 js 爱好者都同意这是该语言最好的部分。 Read all about 'em

区分作用域和this 很重要。尽管变量由函数限定,this 在给定的作用域链中不一定相同。一个简单的例子是,我可以使用任何我想要的 this 值来调用我想要的任何函数:

someFunc.call(someThis); // call someFunc, using someThis for this

对于事件处理程序,this 通常是事件起源的元素。这很好,因为您可能在页面上有多个按钮,并且您想对被点击的那个做一些事情。

【讨论】:

  • 所以每当我使用 DOM 元素时,我都需要使用“var self = this”吗?这不应该作为创建它的对象的范围吗?
  • @Dariss 很好的问题 - 请参阅我上面的编辑,希望能回答。
  • @Dariss——函数的 this 由函数的调用方式(或bind)设置。因此,如果您没有在调用中设置正确的 this ,则以后无法修复它。在闭包中保留对实例的引用使其再次可用,尽管这不是一种特别有效的方法。最好制定另一个方案。
【解决方案2】:

问题是你分配之后

the_button.onclick = deep_thought.ask_question;

单击按钮时,将调用分配给onclick 属性的函数,并将this 绑定到按钮。您可以通过(至少)三种方式解决问题:

  • 通过重写函数(如the answer by lbstr

  • 通过使用bind 创建一个新函数,该函数使用所需的this 执行ask_question

    the_button.onclick = deep_thought.ask_question.bind(deep_thought);
    

    请注意 bind 是 JS 1.8.5 的新内容;上面的链接有一个适用于旧 JS 引擎的 shim。

  • 通过编写闭包在ask_question 中获取正确的this

    the_button.onclick = function() { deep_thought.ask_question(); };
    

    编辑:正如 RobG 在评论中指出的那样,此代码会在某些浏览器(尤其是许多版本的 MSIE)中造成内存泄漏。解决方法是完全消除变量the_button

    document.getElementById('thebutton').onclick = function() {
        deep_thought.ask_question();
    };
    

    请参阅this tutorial 了解其他方法。

【讨论】:

  • 最后一个建议确实创建了一个闭包,但这不是它起作用的原因。它只是将 this 设置为调用中的正确值。闭包是未使用的(并且不需要,因为在这种情况下它们可能会导致某些浏览器出现内存问题)。下一行应该是the_button = null,以删除由闭包引起的最有问题的循环引用。
  • @RobG - 闭包捕获局部变量deep_thought 的值,这是调用ask_question 的关键,以将this 设置为正确的值。我不确定您所说的“这不是它起作用的原因”是什么意思。
  • @TedHopp — 我只是想强调它会将 this 设置为正确的值是因为函数的调用方式,而不是因为涉及到闭包。很高兴看到避免循环引用的替代方法。 :-)
【解决方案3】:

当您指定从按钮单击执行 ask_question() 时,“this”将是按钮的上下文,而不是基于构造函数的 this 的 deep_thought 变量。如果您从 this.the_answer 中删除“this”并将其设为 var,您将收到 42 的警报。

【讨论】:

    猜你喜欢
    • 2011-06-23
    • 1970-01-01
    • 2018-12-22
    • 2015-03-09
    • 1970-01-01
    • 1970-01-01
    • 2020-02-07
    • 2012-05-21
    • 2012-12-23
    相关资源
    最近更新 更多