0

http://www.ecma-international...
中提到:

If the function’s formal parameters do not include any default value initializers then the body declarations are instantiated in the same Environment Record as the parameters. If default value parameter initializers exist, a second Environment Record is created for the body declarations. Formal parameters and functions are initialized as part of FunctionDeclarationInstantiation. All other bindings are initialized during evaluation of the function body.

ES6中参数的默认值会产生一个参数作用域,并且该作用域与函数作用域是独立的。但是在查阅了很多博客和相关例子之后,有两个问题不太明白:

一.

function f1 ( x = 2,  f = function () { x=3; } ){
  let x = 5;
  f( );
  console.log( x );  
} 
f1();  

参数作用域和函数体作用域是独立的、不共享的。既然如此,为什么上面的例子会报错,说x已经存在呢?如果在同一个作用域内用let重复声明一个变量,确实会报错,但是无法理解为什么这里不是同一个作用域也会报错。

二.
参数作用域和函数体作用域是什么关系?查阅了很多资料也没找到这两者具体的关系,但是用“父子关系”来解释很多例子基本都是行得通的,所以假设参数作用域包含了函数体作用域。但是,这样的话又无法解释下面这段代码:

function f1 ( x = 2,  f = function () { x=3; } ){
  var x;
  f( );
  console.log( x );  
} 
f1();   // 2

根据作用域链查找的规则,在准备打印x的时候,其实函数体内已经找得到x了,只是这个x未赋值,所以这时候理应就是打印这个未赋值的x。但是最后输出的结果是2,这应该怎么理解呢?难道这里访问到的是参数作用域中的x吗?那么函数体内的x在作用域链查询的时候为什么看起来好像被忽略了?

以上两个问题没有想清楚,希望可以得到回答,或者说是提供思考和验证的方向。谢谢。

Chor 433
2019-05-23 提问
1 个回答
0

我说说我的理解,不一定对,如有错误,轻喷。

问题一:
x = 2 和函数体中的作用域是相同的,这里不是参数作用域,因此,let x = 5; 自然会报错。
f = function () { x=3; } 是参数作用域,和函数体中的作用域无关,因此,就算你改为 f = function () { let x=3; } 也不会报错。

问题二:
var 用于变量声明,是有变量提升效果的,结合问题一中关于作用域的声明,x=2var x; 在同一个作用域,然后 var 会造成变量提升,因此这段代码实际效果等于下面代码:

...
var x
x = 2
...

另外,关于你对于作用域包含关系的推测,我认为是正确的,比如你把问题一的代码改一下:

function f1 ( x = 2,  f = function () { console.log(x); } ){
  f( );
} 
f1(); // 2  

总体来说,如果将作用域的讨论范围限制为 es6 ,在工程角度上看,最好就不要带上 var 了,真的很蛋疼,如果是学术角度的话,自我斟酌吧。

--- updated

另外,关于其实关于这种问题的话,一般验证的更好方式是通过 babel 或者 ts 转码为 es5 看看,比如将上面的代码,用 babel 转码如下:

function f1() {
  var x = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 2;
  var f = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {
    console.log(x);
  };
  f();
}

这样一目了然了吧??

撰写答案

推广链接