【问题标题】:Hoisting function expression吊装功能表达式
【发布时间】:2015-11-20 17:38:34
【问题描述】:

据我所知,只有函数表达式的声明部分被提升而不是初始化。 例如:

var myFunction = function myFunction() {console.log('Hello World');};

所以“var myFunction;”被提升,但“function myFunction()...”没有。

现在我的问题是,我玩了一下 google auth 功能:

"use strict";

$(document).ready = (function() {
  var clientId = 'MYCLIENTID';
  var apiKey = 'MYAPIKEY';
  var scopes = 'https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/drive.readonly https://www.googleapis.com/auth/drive.appfolder https://www.googleapis.com/auth/drive.apps.readonly https://www.googleapis.com/auth/drive.file https://www.googleapis.com/auth/drive.install https://www.googleapis.com/auth/drive.metadata https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/drive.photos.readonly https://www.googleapis.com/auth/drive.scripts';

  $('#init').click(function() {
    gapi.client.setApiKey(apiKey);
    window.setTimeout(checkAuth(false, handleAuthResult), 1);
  });

  var checkAuth = function checkAuth(imm, callback) {
    gapi.auth.authorize({
      client_id: clientId,
      scope: scopes,
      immediate: imm
    }, callback);
  };

  var handleAuthResult = function handleAuthResult(authResult) {
    if (authResult) {
      gapi.client.load('drive', 'v2', initialize);
    } else {
      $('#progress').html('Anmeldung fehlgeschlagen');
    }
  };

  // Other code
})();

在第 10 行“window.setTimeout(checkAuth...”上,我调用了在该函数调用下方声明的 checkAuth 函数。我的假设是我收到一个错误消息“...checkAuth 不是函数/未定义等. ...”,但它确实有效。有人可以向我解释一下吗?

【问题讨论】:

  • “据我所知,只有函数表达式的声明部分被提升而不是初始化。” 函数表达式没有声明部分。 var checkAuth = ...; 是一个变量声明。您分配的值无关紧要。它可以是一个函数(如这里)或一个数字或其他东西。变量声明被提升。
  • window.setTimeout(checkAuth(false, handleAuthResult), 1); 这必须写成window.setTimeout(checkAuth.bind(null, false, handleAuthResult), 1);,一切都会好起来的。其他作者的更多解释如下。
  • 源代码中的顺序无所谓,代码执行的顺序是你要看的。

标签: javascript hoisting function-expression


【解决方案1】:

这是因为当元素上的实际点击事件被触发时,checkAuth然后在范围内可用。您期望的错误会以这种方式发生:

checkAuth(false, ...); // the symbol is available, but...

// its value is assigned here
var checkAuth = function checkAuth() {
    /* more code */
};

注意checkAuth()的直接调用之前它是在上面的sn-p中分配的。

在调用点可用的 是名为checkAuth符号;但是,它的值稍后会被分配。因此错误 checkAuth is not a function 而不是 checkAuth is undefined

【讨论】:

  • 这个答案不正确。事实上,该符号将在 javascript 中可用,并且 sn-p 不会抛出您提到的错误。
  • 感谢您的快速答复。但为什么它在范围内可用?点击函数有没有自己的作用域?
  • 很抱歉,我需要深入挖掘。当我使用点击事件时,我仍然不明白为什么 checkAuth 的值在范围内可用?能详细解释一下吗?
  • @lorefnon Is that so? Shimigami,看看 this answer 为什么 symbol 可用,但它的 value 稍后可用。
  • 当然!编译器首先执行所有声明(var clientId 等),然后执行脚本:1)初始化 clientId、apiKey 和范围变量 2)添加点击事件侦听器 3)初始化 checkAuth 变量 4)初始化 handleAuthResult 变量 5)其他代码...当有人单击按钮时, checkAuth 变量已经初始化(步骤 3)。我没有考虑到点击是稍后发生的。谢谢!
【解决方案2】:

命名函数的声明是different 来自将函数分配给变量。 This article 通过示例详细说明了这一点。为了完整起见,我在这里引用了重要部分:

我们首先要了解一个函数的区别 表达式和函数声明。顾名思义,一个 函数表达式将函数定义为表达式的一部分(在 这种情况下将其分配给变量)。这类功能可以 要么是匿名的,要么他们可以有一个名字。

...函数声明总是定义为命名函数 成为任何表达式的一部分。

...函数表达式只有在定义后才能调用 而函数声明前后都可以执行 这是定义

如果删除函数名,您将得到预期的错误:

 var checkAuth = function(imm, callback) {
    gapi.auth.authorize({
      client_id: clientId,
      scope: scopes,
      immediate: imm
    }, callback);
  };

此外,您似乎错误地使用了 setTimeout:

window.setTimeout(checkAuth(false, handleAuthResult), 1);

将立即执行 checkAuth 而不是延迟它,如果你想延迟 checkAuth 的执行你可以将它包装在一个匿名函数中:

window.setTimeout(function() { checkAuth(false, handleAuthResult) }, 1);

【讨论】:

  • 我试图删除函数名,但它仍然有效。是的,你是对的,我应该在 setTimeout 中使用匿名函数。
  • 添加匿名函数后,它将起作用,因为在匿名函数运行时,该函数已被分配给变量。但是,如果您尝试在分配之前执行它,它将失败:人为的示例:foo(); var foo = function() { alert('hello'); } ;VM380:2 Uncaught TypeError: foo is not a function(…)
  • 我试图删除函数名并使用匿名函数,但它仍然有效...
  • 是的,这不会使我的回答无效。请仔细阅读我之前的评论,它包含了您查询的答案。
  • 如果不使用匿名函数会报错。
猜你喜欢
  • 2018-01-15
  • 1970-01-01
  • 2021-07-23
  • 1970-01-01
  • 2020-10-29
  • 2011-07-18
  • 1970-01-01
  • 1970-01-01
  • 2018-10-21
相关资源
最近更新 更多