咱们今天先从概念入手,而后在经过一道例题,完完整整的了解代码执行的过程,在这个过程当中会用到做用域和做用域链查找机制;咱们在根据概念的带入详细梳理;javascript
在某一个上下文中建立函数,除了开辟内存和赋值以外,还会给当前函数设置一个做用域属性
[[scope]]
:java
- 当前函数
[[scope]]
= 当前函数建立时候所在的上下文 ;
简单来讲:当前函数的做用域取决于当前函数建立时候的上下文,它在哪一个上下文建立的,那它的做用域就是谁函数
EC(G)
中的全局变量对象VO(G)
中,存储的变量EC(XXX)
中的变量对象AO(XXX)
中,存储的变量;都有哪些是私有变量呢:ui
- 当前函数执行造成的上下文中:声明过的变量或者函数,都会存储到
AO(XXX)
中,- 函数定义的形参变量,也会存储到当前上下文的
AO(XXX)
中;
scopeChain
的概念做用域链scopeChain
里面包含了:spa
scope
做用域;
scopeChain
:<当前EC,函数[[scope]]>
;3d
以此来创建链式关系,以后咱们在查找变量的时候,就按照这个链式关系找(先找本身上下文的,本身没有,按照做用域链向上级做用域找)code
做用域链是在函数执行的时候造成的;执行函数的具体步骤为:cdn
- 建立私有上下文EC(有存放私有变量的变量对象AO)
- 进栈执行(时会把全局上下文压缩到底部)
- 初始化做用域链
scopeChain
:<当前EC,函数[[scope]]>- 初始化
THIS
指向- 形参赋值(包含初始化
ARGUMENTS
)- 变量提高
- ......这里咱们省略了一些
- 代码执行
- 执行完可能会出栈(也可能不出栈)
当函数执行的时候除了按照咱们上面的,还有不少不少操做;对象
首先看它是不是私有的blog
- 1.若是是私有的,接下来的全部操做,都是操做本身的,和别人没有关系;
- 2.若是不是私有的,则按照
scopeChain
做用域链进行查找,在哪一个上下文中找到,当前变量就是谁的- ......一直找到全局上下文为止
- 若是找到
EC(G)
都找不到:
- 是获取变量值就会报错,
- 是设置值,至关于给
GO
加属性
var a = b = 12 ;
和 var a = 12, b = 12 ;
的区别在此以前咱们先了解一个零散的小知识点:
var a = b = 12 ;
和var a = 12, b = 12 ;
的区别
var a = b = 12;
//=> 至关于:var a = 12; b = 12; 只有第一个带VAR,第二个不带VAR
var a=12, b=12;
//=> 至关于var a = 12; var b = 12; 连续建立多个变量,可使用逗号分隔;
复制代码
好了,接下来开始咱们的正式内容;
console.log(a, b);
var a = 12,
b = 12;
function fn() {
console.log(a, b);
var a = b = 13;
console.log(a, b);
}
fn();
console.log(a, b);
复制代码
此题的操做过程:
var a
;var b
;function fn(){...}
; (这里声明+定义fn )
AAAFFF000
),把函数看成字符串存进堆中;scope
做用域属性
fn[[scope]] = EC(G)
;AAAFFF000
,看成值与 fn
变量关联在一块儿;console.log(a, b);
=> undefined
/ undefined
;var a = 12 ;
12
;var a
操做已经在变量提高阶段完成,因此直接跳过);a
与12
关联;var b = 12 ;
=> 同上面var a = 12
同样;function fn(){...};
=> (变量提高阶段已经完成,直接跳过);fn() ;
=> 让fn
函数执行;
EC(fn1)
);AO
(咱们把它命名为AO(fn1)
);EC(G)
被压缩到ECStack
底部,同时EC(fn1)
进栈执行 ;scopeChain
:<当前EC,函数[[scope]]>
这里的fn[[scope]]
咱们在函数建立时,已经知道是EC(G)
,因此最终的做用域链为=> scopeChain
:<EC(fn1),EC(G)>
;var a
;console.log(a, b);
=> 此时就按照做用域链查找,
a
已经存在,只不过没有赋值,因此a
是本身的私有属性,直接操做本身的便可;=> undefined
b
不是本身的私有属性,经过做用域链查找,找到EC(G)
中的b
,此后操做的都是EC(G)
中的b
; => 12
var a = 13 ;
=> 建立值13
,var a
跳过,a
与13
关联;b = 13 ;
=> 经过做用域链找到全局的b
;给b
从新赋值13
;console.log(a, b);
=> 13
/ 13
;console.log(a, b);
=> 此时打印全局下的a
,b
;
a
=> 12 ;b
=> 13 ;console.log(a, b, c);//=> undefined undefined undefined
var a = 12,
b = 13,
c = 14;
function fn(a) {
console.log(a, b, c);//=> 10 , 13 , 14
a = 100;
c = 200;
console.log(a, b, c);//=> 100 , 13 ,200
}
b = fn(10);
console.log(a, b, c);//=> 100 undefined 200
复制代码
var ary = [12, 23];
function fn(ary) {
console.log(ary);//=> [12,23]
ary[0] = 100;
ary = [100];
ary[0] = 0;
console.log(ary);//=> [0]
}
fn(ary);
console.log(ary);//=> [100,23]
复制代码
var a=1;
var obj ={
"name":"tom"
}
function fn(){
var a2 = a;
obj2 = obj;
a2 =a;
obj2.name =”jack”;
}
fn();
console.log(a);//=> 1
console.log(obj);//=> {"name":"jack"}
复制代码
var i = 20;
function fn() {
i -= 2;
var i = 10;
return function (n) {
console.log((++i) - n);
}
}
var f = fn();
f(1);//=>10
f(2);//=>10
fn()(3);//=>8
fn()(4);//=>7
f(5);//=>8
console.log(i);//=>20
复制代码
//========有形参=======
let x = 5;
function fn(x) {
return function(y) {
console.log(y + (++x));
}
}
let f = fn(6);
f(7);//=>14
fn(8)(9);//=>18
f(10);//=>18
console.log(x);
//========无形参==========
let x = 5;
function fn() {
return function(y) {
console.log(y + (++x));
}
}
let f = fn(6);
f(7);//=>13
fn(8)(9);//=>16
f(10);//=>18
console.log(x);
复制代码
x
的状况