一、前提
1、原型与原型链
原型链的作用就是查找对象的属性。
2、执行上下文与执行上下文栈
执行上下文的作用就是保存当前作用域环境的初始化变量。
创建:在JS执行全局代码前或函数代码前,会设置执行上下文对象,并初始化环境。
死亡:函数执行完毕就会销毁。
3、作用域与作用域链
作用域链的作用就是查找变量。
作用域链的代码实现就是函数内部属性[[scopes]]属性。
4、闭包
闭包的作用就是延长函数变量的生命周期。
闭包本质上就是闭包作用域对象。
二、变量查找机制
+++ 基础
1) 沿着作用域链查找变量:
确定当前函数的作用域链,
首先在当前作用域中对应的执行上下文对象中查找变量,
如果找不到,就会到上一级作用域中对应的执行上下文对象中查找。直到全局作用域。
2) 沿着原型链查找对象属性
+++ 变量查找机制
1) 首先在自身作用域中对应的执行上下文对象中查找。
2) 如果找不到,就会到函数的作用域链属性中查找。
从索引值0开始依次向上查找。
3)查找到变量后,如果继续查找对象的属性,则会沿着原型链查找。
注意事项:
1) 作用域是在函数定义时确立的。而不是在函数调用时确立的。
2) 作用域只分为全局作用域和函数作用域
3) 在全局作用域中定义的变量会被当做window的属性来保存。
不使用var关键字定义的变量会被当作window的属性来保存。
4) 作用域链与原型链都是用于查找对象的属性。
在作用域链查找变量是沿着作用域链查找。
在对象中查找属性是沿着原型链查找。
3.1 变量查询机制
1)案例1
首先在自身作用域中对应的执行上下文对象中查找fn,
.
找不到就会到作用域链属性中查找,此时,作用域链属性中只有window对象,则会到window对象中查找fn。
var fn=function(){
//首先在自身作用域中查找变量fn。
//然后到全局作用域查找变量fn。
console.log(fn)
}
fn();
2)案例2
首先在自身作用域中对应的执行上下文对象中查找this.
var obj={
fn2:function(){
//查找obj对象中的fn2
console.log(this.fn2);
}
}
obj.fn2();
>>>>>> 作用域只分为全局作用域和函数作用域
作用域只分为全局作用域和函数作用域。
虽然fn2是obj的属性,但是作用域只分为全局作用域和函数作用域。所以fn2函数的所在的作用域链fn2函数作用域 > 全局作用域。
查找变量:首先在fn2作用域中查找fn2变量,找不到,然后到全局作用域查找,找不到后报错。
var obj={
fn2:function(){
//首先在自身作用域中查找变量fn2.
//然后到全局作用域中查找变量fn2,找不到,报错。
console.log(fn2);
}
}
obj.fn2(); //报错
>>>>>> 作用域是在编码时就已经确立的,不会改变
虽然fn函数在show函数中调用,但是fn函数的作用域链是在定义时就已经确定了,而不是调用时确定。
fn函数作用域链:fn函数作用域 > 全局作用域。
var x=10;
function fn(){
console.log(x)
}
function show(f){
var x=20;
f();
}
show(fn);
3)案例3
首先在getNameFun函数对应的执行上下文中查找this,此时this指向window。
然后会到window中查找name
//案例一
var name='The Window'
var obj={
name:'My Object',
getNameFun:function(){
return function(){
return this.name;
}
}
}
console.log(obj.getNameFun()())
首先在匿名函数对应的执行上下文中查找that
找不到就会到函数中的作用域链属性中查找。此时作用域链属性从0开始依次是闭包作用域对象、window对象。
从索引值0开始查找,找到that。
然后沿着that的原型链查找name。
//案例er
var name='The Window'
var obj={
name:'My Object',
getNameFun:function(){
var that=this;
return function(){
return that.name;
}
}
}
console.log(obj.getNameFun()())
3.3 作用域链、执行上下文与变量查询的内在联系
执行上下文与作用域的关系:
1、作用域的作用是隔离变量。
2、作用域链的作用是查询变量。
3、作用域、作用域链是在编码时就已经确定了,而执行上下文对象是在代码执行前或函数执行时确立的。
执行上下文对象从属于作用域。
函数在查找变量时,
1、首先会在当前作用域中对应的执行上下文对象查找变量,
这是由于在执行代码前,会初始化代码数据,此时会将变量或函数放入到执行上下文对象中。
2、如果找不到,就到上一级作用域对应的执行上下文对象中查找。
三、JS执行过程解析
3.1 执行上下文与作用域的直观展示
var s=12;
function ff(){
//此时闭包已经产生了。(函数提升,内部函数对象已经创建了)
var a=1;
function f(){
a++;
console.log(a)
}
return f;
}
var f=ff();
f(); //2
f();//3
4.2 JS代码执行全流程讲解
+++ 前提:
1、JS执行代码前首先会创建一个执行上下栈。
2、在创建执行上下文对象后会将执行上下对象放入到栈中。函数执行完毕,会将该对象从栈中移除。
3、执行上下文栈中最终存储window执行上下文。
+++ JS全流程讲解
1、JS代码执行时,首先会将Window设置为全局执行上下文。
2、然后初始化全局数据:
变量声明提前,并设置为window属性。
函数声明提前(函数声明实质上就是创建函数对象),并设置为window属性。
将this设置为window对象。
3、执行全局代码。
4、执行到某个函数时
1、创建函数执行上下文。
2、初始化函数数据
变量声明提前,并设置为window属性。
函数声明提前(函数声明实质上就是创建函数对象),并设置为window属性。
函数形参赋值,并设置为函数执行上下文的属性。
函数arguments赋值,并设置为函数执行上下文的属性。
函数中this设置为window对象。
3、执行函数代码
+++ JS查找变量
函数沿着作用域链查找变量时, 首先在当前作用域中对应的执行上下文对象中查找变量,
如果找不到,就会到上一级作用域中对应的执行上下文对象中查找。直到全局作用域。