前言
Javascript的执行上下文,其实也就是JavaScript执行过程中的环境,环境中就包含了执行中需要访问的变量。总的来说,它包含了下面四个方面。
JavaScript执行上下文的组成
1.环境变量
简单来说,变量环境就是用var标识符定义的变量。
JavaScript一开始定义变量是只有var一种标识符,因为var定义的变量是没有块级作用域的,所以,一开始JS中变量只有全局作用域和函数作用域。
var a = 1;
function b () {
a = 2;
console.log(a);
}
b();// 2
console.log(a);// 2
b函数的执行改变了全局变量a的值。
如果在b函数中用var重新声明一个a变量,那么这个a变量的作用访问仅限于b函数内。
var a = 1;
function b () {
var a = 2;
console.log(a);
}
b();// 2
console.log(a);// 1
通过断点,可以看出,b函数作用域有一个变量a,其值为2,全局作用域也有一个变量a,其值为1。
2.词法环境
词法环境指的是用let,const定义的变量。
let和const的出现是为了解决JavaScript中没有块级作用域的问题,而程序的世界,最重要的一点就是要兼容老版本,为了保持兼容不能动var的逻辑,但是又要增加块级作用域,所以就新开了一条路,这条路就叫词法环境。
理论上来说,变量查找时,词法环境的优先级大于变量环境,但实际上如果我们用var和let定义了同名变量,会直接报错SyntaxError: Identifier 'a' has already been declared
let和 const会和{}一起组成块级作用域,限定变量的访问范围。不在该块级作用域内的访问会报错。
{
let a = 2;
}
console.log(a);// ReferenceError: a is not defined
function b () {
console.log(a);
}
b();// ReferenceError: a is not defined
需要注意的是,let和const不存在变量提升,在声明前访问会报错。
3.作用域链(outer)
作用域链指的是,查找变量时,会在当前作用域,一层层地向上查找。
function a() {
let a = 1;
function b() {
let b = 2;
function c() {
console.log('a', a);// 1
console.log('b', b);// 2
}
c();
}
b();
}
a();
需要注意的是,如果是返回了子函数的情况,子函数查找作用域是依据定义时的作用域嵌套关系。
function a() {
var c = 1;
return function b() {
console.log(c);
};
}
var c = 2;
let foo = a();
foo();// 1
foo函数内的变量c用的是函数定义时,其父级作用域的c。
4.this指向
this指向有4种可能,默认指的是全局对象,也就是window。
var a = 1;
function foo () {
debugger
this.a = 2;
console.log(this.a)// 2
}
foo();
console.log(a);// 2
this指向是可以改的,可以通过call、apply、bind方法显示的更改。
let obj1 = {
name:'obj1'
}
let obj2 = {
name:'obj2'
}
function foo (other) {
console.log(this.name,other)
}
foo.call(obj2,1);// obj2 1
foo.apply(obj1, [2]);// obj1 2
foo.bind(obj2)(3);// obj2 3
this指向还有两种隐式的,在JS引擎层面有默认的绑定规则的。
第一条是,通过对象访问的,this指向指向这个对象本身,
let obj1 = {
name:'obj1'
}
let obj2 = {
name:'obj2'
}
let obj3 = {
name: 'obj3',
foo: function(other) {
console.log(this.name, other);
}
}
obj3.foo(3);// obj3 3
第二条是,通过new 构造函数调用,返回的默认对象,该对象的this指向构造函数的原型。
function foo () {
}
foo.prototype.name = 'hello world';
let b = new foo();
console.log(b.name);// hello world
写在最后
总的来说,变量环境、词法环境、作用域链、this指向这四个点涵盖了JavaScript执行上下文的环境变量,搞懂了这几个规则,就能理解具体变量在执行的时候会取哪个值,写出如我们预期一样的JavaScript代码。