Fork me on GitHub

this使用和执行上下文栈

js中this使用和执行上下文栈

this使用

在介绍this使用之前,先说明:this到底取何值,是在函数真正被调用执行的时候确定的,函数定义的时候确定不了。因为this的取值是执行上下文环境的一部分,每次调用函数,都会产生一个新的执行上下文环境。

构造函数

1
2
3
4
5
6
7
function Aa() {
this.name = "name";
this.age = 13;
}
var obj = new Aa();
console.log("name:"+obj.name+"age:"+obj.age);

如果函数作为构造函数用,那么其中的this就代表它即将new出来的对象;反之如果直接调用函数,而不是new,this会指向window。
在构造函数的prototype和整个原型链中,this代表的也都是当前对象的值。

函数作为对象的一个属性

1
2
3
4
5
6
7
8
9
10
11
12
13
var obj = {
name: "name",
fnName: function() {
console.log(this.name);
//name;
}
}
情况1
obj.fnName();
//name
情况2
var fn = obj.fnName();
// undefined

1。如果函数作为对象的一个属性,并且作为对象的一个属性被调用时,函数中的this指向该对象。如情况1
2。如果fnName函数被赋值到了另一个变量中,并没有作为一个属性被调用,那么this的值就是window,this.name为undefined。

函数用call或者apply调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var obj = {
x: "obj"
};
var fn = function() {
console.log(this);
console.log(this.x);
}
fn.apply(obj);
当一个函数被call和apply调用时,this的值就取传入的对象的值。
注:call/apply简单用法(可以改变this指向):
fn.apply(obj,参数数组);
fn.call(obj,参数1,参数2,参数3);
1、fn代表一个函数、obj代表一个执行作用域。
2、apply传入参数必须是个参数数组。

全局 & 调用普通函数

在全局环境

在全局环境下,this永远是window

普通函数

普通函数在调用时,其中的this也都是window。

执行上下文栈

执行全局代码时,会产生一个执行上下文环境,每次调用函数都又会产生执行上下文环境。当函数调用完成时,这个上下文环境以及其中的数据都会被消除,再重新回到全局上下文环境。处于活动状态的执行上下文环境只有一个。这是一个压栈出栈的过程——执行上下文栈。

全局上下文环境 =压栈=》(函数上下文环境/全局上下文环境) =出栈销毁=》全局上下文环境

理想情况

1
2
3
4
5
6
7
8
9
10
11
12
var a = 1,
fn1,
fn2 = function(x) {
var b = x;
fn1(x+a);
}
fn1 = function(y) {
var c = 5;
console.log(y + c);
}
fn2(10);

1、首先在执行代码之前,首先将创建全局上下文环境。
创建变量a,fn1,fn2,this
2、当执行到fn2时,上下文环境中的变量都在执行过程中被赋值。
3、执行fn2,并挑到fn2函数内部,会创建一个新的上下文环境。并将这个执行的上下文环境压栈,设置为活动状态。
4,执行到fn1时,跳到fn1中,在执行fn1语句之前,会创建fn1函数的执行上下文环境,并压栈,设置为活动状态。
5、待fn1函数执行完。此次调用fn1所生成的上下文环境出栈,并且被销毁(已经用完了,就要及时销毁,释放内存)。
6、待fn2函数执行完。此次调用fn2所生成的上下文环境出栈,并且被销毁(已经用完了,就要及时销毁,释放内存)。

闭包

闭包的两种情况

函数作为返回值

1
2
3
4
5
6
7
8
9
10
11
function fn1() {
var max = 100;
return function fn3(x) {
if(x>max){
console.log(x);
}
}
}
var fn2 = fn1();
fn2(130);

fn3函数作为返回值,赋值给fn1变量。执行fn2(120)时,用到了fn1作用域下的max变量的值。
注意:作用域取值,要到创建这个函数的那个作用域中取值——是创建,而不是调用的地方

函数作为参数传递

1
2
3
4
5
6
7
8
9
10
11
var max = 100,
fn1 = function(x) {
if(x>max){
console.log(x);
}
};
(function(f) {
var max = 50;
f(130);
})(fn1)

fn1函数作为一个参数被传递进入另一个函数,赋值给f参数。执行f(130)时,max变量的取值是100,而不是50。

1
2
3
4
5
6
7
8
9
10
11
function fn1() {
var max = 100;
return function fn3(x) {
if(x>max){
console.log(x);
}
}
}
var fn2 = fn1();
fn2(130);

分析闭包:
1、代码执行前生成全局上下文环境,并在执行时对其中的变量进行赋值。此时全局上下文环境是活动状态。
2、当执行到var fn2 = fn1()时,调用fn1函数,产生fn1()执行上下文环境,压栈,并设置为活动状态。
3、执行完fn1函数,fn1函数的执行上下文不能被销毁,因为执行fn1()时,返回的是一个函数。函数的特别之处在于可以创建一个独立的作用域。且这个函数中还有一个自由变量max要引用fn1作用域下的的max。因此不能销毁max,否则fn3函数将找不到max的值,因此fn1函数不能被销毁。即执行完fn1函数。全局上下文变成活动状态,fn1还是不能被销毁,依旧存在在执行栈中。
4、执行到fn2函数,即执行fn3(130),创建上下文,并将其设为活动状态。当执行fn3函数,需要用到max。但是fn1函数已经执行过了。但是其执行上下文环境还存在与栈中,因此fn3可以找到max。如果销毁fn1函数上下文,就找不到max了。
5、执行完fn3就是上下文环境的销毁过程。

(完)

-------------本文结束感谢您的阅读-------------