0%

js中this

    下班路上头条上推荐了篇文章,点开看了下,感觉不错,特此记录!

一、引入

1
2
3
4
5
6
7
8
9
10
var obj = {
foo: function () {
console.log(this.bar)
},
bar: 1
};
var foo = obj.foo;
var bar = 2;
obj.foo() // obj环境执行,1
foo() // 全局环境执行,2

一般解释:this指的是函数运行时所在的环境,对于obj.foo()来说,foo运行在obj环境,所以this指向obj;对于foo()来说,foo运行在全局环境,所以this指向全局环境。

二、剖析

    如果对象的属性是一个值,如下:

1
var obj = { foo: 5 };

    将一个对象赋值给变量obj,js引擎会先在内存里面生成一个对象{ foo: 5 },然后把这个对象的内存地址赋值给变量obj,也就是说变量obj是一个地址(reference)。
声明一个对象
    后面如果要读取obj.foo,js引擎会先从obj拿到内存地址,然后再从该地址读出原始的对象,返回它的foo属性。

    原始的对象以字典结构保存,每一个属性名都对应一个属性描述对象。对于上面例子的foo属性实际上是以下面的形式保存的:
对象属性

注意,foo属性的值保存在属性描述对象的value属性里面


    如果对象的属性是一个函数,如下:

1
var obj = { foo: function () {} };

    这时js引擎会将函数单独保存在内存中,然后再将函数的地址赋值给foo属性的value属性。
对象的属性是函数
    由于函数是一个单独的值,所以它可以在不同的环境(上下文)执行。

1
2
3
4
var f   = function () {};
var obj = { f: f };
f() // 全局环境执行
obj.f() // obj环境执行

JavaScript允许在函数体内部,引用当前环境的其他变量。

1
2
3
var f = function () {
console.log(x);
};

    上面代码中,函数体里面使用了变量x,该变量由运行环境提供。现在问题就来了,由于函数可以在不同的运行环境执行,所以需要有一种机制,能够在函数体内部获得当前的运行环境(context)。所以this就出现了,它的设计目的就是在函数体内部,指代函数当前的运行环境。

1
2
3
var f = function () {
console.log(this.x);
}

    上面代码中,函数体里面的this.x就是指当前运行环境的x。

1
2
3
4
5
6
7
8
9
10
var f = function () {
console.log(this.x);
}
var x = 1;
var obj = {
f: f,
x: 2,
};
f() // 全局环境,引用全局环境的变量x,打印输出1
obj.f() // obj环境执行,打印输出2

    上面代码中,函数f在全局环境执行,this.x指向全局环境的x。
全局环境
obj环境执行,this.x指向obj.x。
obj环境
    回到本文开头提出的问题,obj.foo()是通过obj找到foo,所以就是在obj环境执行。一旦var foo = obj.foo,变量foo就直接指向函数本身,所以foo()就变成在全局环境执行。