为了账号安全,请及时绑定邮箱和手机立即绑定

为什么以下两个例子执行结果不同?

为什么以下两个例子执行结果不同?

一只萌萌小番薯 2019-03-07 18:15:03
var x = 1;function foo(x, y = function() { x = 2; }) {  var x = 3;  y();  console.log(x);}foo() // 3x // 1而去掉var以后:var x = 1;function foo(x, y = function() { x = 2; }) {  x = 3;  y();  console.log(x);}foo() // 2x // 1为什么实例一的y执行完成没有改变任何x的值?而去掉var输出结果就会改变?
查看完整描述

4 回答

?
蓝山帝景

TA贡献1843条经验 获得超7个赞

函数声明时设置的默认参数值是在函数调用时计算赋值的,而不是在函数声明时赋值


我们可以看下下面的例子


function foo2(a, b = (function() { console.log(c); return function(){} })()) {

    b();

}

foo2();

调用 foo2();

控制台将输出:

Uncaught ReferenceError: c is not defined


at b (<anonymous>:1:47)

at foo2 (<anonymous>:2:5)

at <anonymous>:1:1

而不掉用 foo2();

控制台将不报错


以上例子说明了函数参数的默认值是在调用是赋值的,而不是在声明时。


对于问题的代码,还有注意一点,y默认值函数声明中的x是绑定为函数声明中的参数x变量而不是foo外层作用域中的变量x


function foo(x, y = function() { x = 2; }) {

  console.log("x1:"+x);

  y();

  console.log("x2:"+x);

}

foo();

输出 

x1:undefined

x2:2


那么下面的代码输出2就好理解了


var x = 1;

function foo(x, y = function() { x = 2; }) {

  x = 3;

  y();

  console.log(x);

}


foo(); // 2


查看完整回答
反对 回复 2019-03-18
?
慕娘9325324

TA贡献1783条经验 获得超4个赞

看到了很多答案,在大家的提示下 我去查了很多资料现在已经理解了这个结果。


var x = 1;

function foo(x, y = function() { x = 2; }) {

  var x = 3;

  y();

  console.log(x);

}


foo() // 3

x // 1

以上的代码 作用域链如下:

https://img1.sycdn.imooc.com//5c8f44bc00011d1608000636.jpg

以上的图涉及一个问题,那就是为什么foo的参数居然是自成一个作用域链?

这是因为es6规定:

一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域(context)。等到初始化结束,这个作用域就会消失。这种语法行为,在不设置参数默认值时,是不会出现的。

也就是说y中的x=2,因为y中是没有x变量的,所以向上一级查找,他的上一级就是传入的参数x,因此y函数的作用改变的是参数x

而当foo内部var x=3时,这时内部的x已经覆盖掉了参数x,

console.log查找的是它的上一层foo的内部变量作用域x,这个x是等于3的,因此foo执行为3,一下贴出作用域链的一个概念,觉得解释的非常好

作用域链:作用域链是一个对象列表或者链表,这组对象定义了这段代码’作用关于中’的变量。当javascript需要查找变量x的值得时候(变量解析),他会从链中的第一个对象开始查找,如果这个对象有一个名为x的属性,则会直接使用这个属性的值,如果第一个对象中不存在名为x的属性,javascript会继续查找链上的下一个对象。如果第二个对象依然没有名为x的属性,则会继续查找下一个对象,以此类推。如果作用域链上没有任何一个对象含有属性x,那么久认为这段代码的作用域链上不存在x,并最终爆出一个引用错误异常。(犀牛书P59)


而第二段代码:


var x = 1;

function foo(x, y = function() { x = 2; }) {

  x = 3;

  y();

  console.log(x);

}


foo() // 2

x // 1

作用域链如下:

https://img1.sycdn.imooc.com//5c8f44c900019da808000656.jpg

foo中的console.log(x)中的x会查到他的上一级传入的参数x,因为传入的参数被y函数改为2,所以输出2
而第一个例子中的x上一级作用域链直接是新定义的x=3所以输出3

查看完整回答
反对 回复 2019-03-18
  • 4 回答
  • 0 关注
  • 513 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信