为什么不允许将变量声明作为参数,而将函数声明作为参数?

时间:2019-01-04 02:12:06

标签: javascript

这可能是一个愚蠢的问题。我用谷歌搜索,但找不到答案。不允许将变量声明作为函数的参数,如下所示。

function t(a) {
    alert(a);
}

t(var x = 1); // Uncaught SyntaxError: Unexpected token var
t(let x = 1); // Uncaught SyntaxError: missing ) after argument list
t(x = 1); // working fine and later I am able to access x also
console.log(x); // printing 1

但是如下所示,允许函数声明作为函数的参数。

function callback(str, f1, f2) {
    if(str == "")
        f1();
    else
        f2();
};

callback("", function b1() { alert("empty") }, function b2() { alert("not empty") }); // working fine

b1(); // Throwing error Uncaught ReferenceError: b1 is not defined

任何人都可以帮助我理解

  • 为什么不允许变量声明作为函数的参数 但是可以使用函数声明作为参数吗?
  • 为什么我们不能访问声明为函数的函数 函数调用之外的参数?

1 个答案:

答案 0 :(得分:9)

好问题!我将其分为几部分。由于您的问题涉及多个深层主题,因此这里将有大量 可供Google搜索的材料。

1。声明没有价值

声明是语句。它们没有值,因此不能成为参数。这段代码...

let a = 1

...没有价值,意味着这些都不起作用:

doStuff(let a = 1)
let b = (let a = 1)
(let a = 1) + 5

名称aa + 5f(a)表达式,与声明不同,表达式具有价值。但是a本身的声明没有。

请注意,您对此的直觉并不荒谬:在其他语言中,let a = 1是一个表达式,其结果为1。不在Java中。

2。函数就是对象

,但是function关键字 具有值:它定义的Function对象。与变量(为方便起见,它们是语言构造)不同,Functions是正在运行的程序中存在的实际对象。我们说函数是一流的对象

可以完成所有这些操作:

doStuff(function f() {})
let a = function f() {}
let b = (function f() {}) + 5 // the result of this is funny

回到您的示例,然后:

callback(
  "", // a String object
  function b1() { alert("empty") }, // a Function object
  function b2() { alert("not empty") } // a Function object
);

类似于此:

function b1() { alert("empty") }
function b2() { alert("not empty") }

callback("", b1, b2)

但不完全是。让我们谈谈范围。

3。名称是在范围内定义的

诸如变量或函数之类的名称的作用域是具有该定义的代码部分。

例如:

// Top-level scope:
let a = 1

if (a == 1) {
  // Inner block scope:
  let b = 2
  console.log(a, b) // 1, 2
}

console.log(a, b) // 1, undefined

合并范围位于较大的合并范围内。内部范围可以访问周围的范围(因此ab在块内可见),但反之则不行(因此b在外部不可见)。

在通话中创建function对象时...

f(function a() { })

...它们被困在一个内部范围内,无法从外部引用。

4。分配是表达式

在示例代码中,您注意到声明a可以这样:

f(a = 5)

这是...不幸的。确实是Javascript历史的产物。在现代代码中,应始终使用letconst定义变量。

那为什么行得通呢?有两个原因。首先,因为它是一个赋值,而不是声明。像这样:

let x = 1
f(x = 2)

赋值是表达式。它们评估为分配的值。 x = 2的值为2,并且x会产生副作用。

5。有全球范围

第二个原因是不幸的。当您避免使用letvarconst关键字时,便隐式使用了global范围。

这是所有作用域的源泉,可以从代码的任何位置访问存在于其中的名称。所以,如果您只是这样做...

f(a = 5)

...在当前范围的任何地方都没有声明a时,它在全局范围中隐式声明,并且赋值发生。可以这样看(伪代码):

global let a
f(a = 5)

那当然是无效的Javascript。但是你明白了。