作用域链:
JS权威指南指出”JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里.”
ECMA262中所述 任何执行上下文时刻的作用域, 都是由作用域链(scope chain)来实现.在一个函数被定义的时候, 会将它定义时刻的scope chain链接到这个函数对象的[[scope]]属性.
在一个函数对象被调用的时候,会创建一个活动对象(也就是一个对象), 然后对于每一个函数的形参,都命名为该活动对象的命名属性, 然后将这个活动对象做为此时的作用域链(scope chain)最前端, 并将这个函数对象的[[scope]]加入到scope chain中.
不必刻意去记概念。当然上面的话,我是记不住,看着也有点绕,下面是我自己这几天学习的体会:
每当执行一个函数就进入一个新的的作用域,使用一个变量或是赋值。首先从自己的当前作用域内部找变量,找到就输出,找不到就是往当前函数所在上层的作用域找,(上层的作用域就是当前函数声明的作用域)
基础知识:
console.log(a) //undefined (变量提升,在函数内部同样适用)var a = 1fn() // "2" //函数声明前置function fn(){ console.log('2')}
相关JavaScript知识点请参考
阮一峰JavaScript教程
实例剖析:
?①
var x = 10bar() function foo() { console.log(x)}function bar(){ var x = 30 foo()}/*var xx = 10function foo() { console.log(x)}function bar() { var x = 30 foo()}*/
10
分析
变量 x
提升到头部,bar()
函数声明前置,函数foo()
的变量x
的作用域不是在bar()
内部,从函数foo()
本身也没有找到,往其上层作用域找到为var x = 10
从全局变量找到,bar()
输出的结果是10
?②
var a = 1function fn1(){ function fn2(){ console.log(a) } function fn3(){ var a = 4 fn2() } var a = 2 return fn3}var fn = fn1()fn() //输出多少/*function fn1(){ var a // 函数fn1()内部的变量a, 提升到函数顶部 function fn2(){ console.log(a) } function fn3(){ var a = 4 fn2() } a = 2 return fn3}*/
2
结果分析:
可简单的看出输出的结果是fn2()
内的cobsole(2)
的打印值,fn2()
的变量对应的作用域,首先从自身既函数fn2()
内部作用域找变量。
内部无法找到就往上层找,上层所对应的作用域即是fn1()
内部,内部变量 var a = 2
**『函数内部变量提升』,所以fn(2)的变量对应的作用域在函数fn1()
内部,console.log(a)
对应的a的值是2fn = fn2() = fn(3)
所以输出结果既是函数fn2()的值2
?③
var a = 1function fn1(){ function fn3(){ var a = 4 fn2() } var a = 2 return fn3}function fn2(){ console.log(a)}var fn = fn1()fn() //输出多少
1
结果分析:
还是上例同样的套路,fn2()中a
对应的作用域,从自己本身找不到,所以往上层找,就是全局变量 a (a = 1),
function fn2(){ console.log(a)}
a的值为1fn2()
的结果为1fn = fn1() = f3()
, 函数fn1()
,fn3
的作用域都不是fn2()
的变量a所对应的作用域。
?④
var a = 1function fn1(){ function fn3(){ function fn2(){ console.log(a) } fn2() var a = 4 } var a = 2 return fn3}var fn = fn1()fn() //输出多少
undefined
结果分析:
return fn3
, fn1() = fn3() = fn2();
fn2()
对应的作用域为它的上级作用域fn3()
function fn3(){ function fn2(){ console.log(a) } fn2() var a = 4 }
fn2()的的输出结果是undefined
,这是由于在
function fn3(){ // ... // ... var a = 4 }
fn3()
中变量a
提升到函数内首部,但是函数赋值是在调用fn2()
之后,
所以console.log(a)
中的a
,并没有被赋值,既输出结果是undefined
。如果把 (var a = 4)放在调用fn2()之前则输出的结果是4
?⑤
var a = 1var c = { name: 'curry', age: 2 }function f1(n) { ++n}function f2(obj) { ++obj.age}f1(a) f2(c) f1(c.age) console.log(a) console.log(c)
// 1
// ** { name: 'curry', age:3 }**
结果分析:
var a = 1function f1(n){ ++n}f1(a)console.log(a)
等同
var a = 1function f1(){ var n = arguments[0] // 假设 n 为 函数 f1 的第一个参数 ++n}f1(a)console.log(a)
把arguments[0]
传递进来,arguments[0] = 1
,++n
,大括号内的变量增加1,但是没有返回值,所以和目前的a并没什么关系(并不能改变a的值),
函数f1(a)
,对应的结果是++a
,但是它并没有输出,所以console.log(a)
中的a
还是 a = 1
(全局变量),既输出结果1
var c = { name: 'curry', age: 2 }function f2(obj){ // var obj = c // 把c赋值给obj,把c的地址和指针赋值给obj了 // c = 0x0001 c与obj其实就是两个名字不同但是***值与地址***相同的值 ++obj.age}f2(c) f1(c.age) console.log(c)

相当于把 `obj = c`,把c赋值给obj,把c的地址和指针赋值给obj了,c与obj其实就是两个名字不同但是***值与地址***相同的值
f2(obj)
虽然也不会输出结果,但是但是因为obj的地址改变了,则c的地址也改变了
所以console.log(c) 的结果是++obj.age
{ name: 'curry', age:3 }
总结:
- 1.函数在执行的过程中,先从自己内部找变量
- 2.如果找不到,再从创建当前函数所在的作用域去找, 以此往上
- 3.注意找的是变量的当前的状态
版权声明:本文为博主原创文章,未经博主许可不得转载
原著是一个有趣的人,若有侵权,请通知删除
还没有人抢沙发呢~