1.作用域
首先比较下面两段代码:
// A--------------------------
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f();
}
checkscope();
// B---------------------------
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f;
}
checkscope()();
运行可得,A和B两端代码输出返回的都是local scope。
JavaScript采用的是词法作用域,函数的作用域基于函数创建的位置。
当查找变量时,会从当前函数上下文的变量对象中查找,如果没找到,就会从父级的变量对象中查找,一直找到全局对象。
这样由多个执行上下文的变量对象构成的链表就叫作用域链。
2.执行上下文
JavaScript 引擎并非一行一行地分析和执行程序,而是一段一段地分析执行,创建了执行上下文栈来管理执行上下文。
当执行一个函数的时候,会创建一个执行上下文,并且压入执行上下文栈,当函数执行完毕时,会将函数的执行上下文从栈中弹出。
上面两段代码的区别是执行上下文栈的变化不一样。
// A--------------------------
push(checkscope) //创建checkscope的执行上下文
push(f) //创建f的执行上下文
pop() //f执行完毕
pop() //checkscope执行完毕
// B---------------------------
push(checkscope) //创建checkscope的执行上下文
pop() //checkscope执行完毕
push(f) //创建f的执行上下文
pop() //f执行完毕
再继续刨根问底,研究每个执行上下文的过程,可以参考《一道js面试题引发的思考》
3.this
this是指向函数运行时所在的环境。
var obj = {
foo:function(){
console.log(this.bar);
bar:1
}
}
var foo=obj.foo;
var bar=2;
obj.foo(); // 1
foo(); // 2
obj.foo():是通过obj找到foo,foo运行在obj环境里,this指向obj,因此返回当前函数上下文的变量bar:1
foo():因为var foo=obj.foo,foo直接指向函数本身,所以foo运行在全局环境,this指向全局环境,因此返回全局变量bar:2
那么函数的运行环境是怎样决定的?跟内存的数据结构有关,详细参考JavaScript 的 this 原理
-
直接调用:this指向全局变量
-
call()、apply(),bind():this指向被绑定的对象,即第一个参数
-
箭头函数:this都指向外层,函数定义时所在的环境,而不是执行时的环境
-
作为对象中的一个方法:this指向调用函数的对象
-
作为构造函数:this指向被绑定的构造函数的新对象
-
作为DOM事件处理函数:this指向触发事件的DOM节点
总结判断顺序:
-
由new调用:绑定到新创建的对象
-
由call,apply,bind调用:绑定到指定的对象
-
由上下文对象调用:绑定到上下文对象
-
默认:全局对象