JS 谜一样的this(浏览器中)

JavaScript 中的this是根据运行时的环境所决定的

①this在全局作用域中

1
2
3
4
5
6
foo = 'abc'
console.log(foo) // abc

var foo = 'def'
console.log(this.foo) // def
// 这里的this指的是全局对象window(浏览器中)
  • 在浏览器中,指向window对象

②this在函数/对象方法中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var boat = {
size: 'normal',
boatInfo: function() {
console.log(this === boat, this.size)
}
}

boat.boatInfo() // true, 'normal'

var bigBoat = {
size: 'big'
}

bigBoat.boatInfo = boat.boatInfo
bigBoat.boatInfo() // false, 'big'

首先要明白,在任何函数中,this的指向都不是静态的(static)。它总是在你调用一个函数,但尚未执行函数内部代码前被指定。实际上,this是 被调用的函数的父作用域提供的。

this,指向调用它所属的那个函数的那个对象

  • 如果函数左边是一个引用,那么函数里的this指向的就是这个引用
  • 否则this指向的就是全局对象(浏览器 -> window)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function bar() {
console.log(this)
}
bar()
// 这里,this指向的是全局对象。我们先看函数的左边,没东西,
// 那么bar属于谁呢?bar属于全局对象,所以this指向的就是全局对象。

var foo = {
baz: function() {
console.log(this)
}
}
foo.baz()
// 这里,this指向的是foo,先看函数左边是foo,所以baz里的this指向的就是foo

由于js中Object和Function是引用类型,所以在引用发生变化的时候,this的指向也会发生改变

1
2
3
4
5
6
7
8
9
10
var foo = {
baz: function() {
console.log(this)
}
}
foo.baz()
// 这里的this指向foo
var anotherBaz = foo.baz
anotherBaz()
// this指向全局对象,在浏览器中这个函数相当于window.anotherBaz()

嵌套在对象里的this的指向

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var anum = 0

var foo = {
anum: 10,
baz: {
anum: 20,
bar: function() {
console.log(this.anum)
}
}
}
foo.baz.bar() // 20
// 函数左边是baz,所以this就是foo.baz,
// this.anum = foo.baz.anum

var hello = foo.baz.bar;
hello() // 0
// 函数左边是啥也没有,所以this指向全局对象
// this.anum = window.anum

值得注意的是下面这几种用法,都会改变this的指向。

1
2
3
4
5
6
7
8
9
10
11
12
13
var obj ={
foo: function () {
console.log(this)
}
}

obj.foo() // obj
// 情况一
;(obj.foo = obj.foo)() // window
// 情况二
;(false || obj.foo)() // window
// 情况三
;(1, obj.foo)() // window

上面代码中,obj.foo就是一个值。这个值真正调用的时候,运行环境已经不是obj了,而是全局环境,所以this不再指向obj

  • 可以这样理解,JavaScript 引擎内部,obj和obj.foo储存在两个内存地址,称为地址一和地址二。
  • obj.foo()这样调用时,是从地址一调用地址二,因此地址二的运行环境是地址一,this指向obj。
  • 但是,上面三种情况,都是直接取出地址二进行调用,这样的话,运行环境就是全局环境,因此this指向全局环境。

上面三种情况等同于下面的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 情况一
(obj.foo = function () {
console.log(this)
})()
// 等同于
(function () {
console.log(this)
})()

// 情况二
(false || function () {
console.log(this)
})()

// 情况三
(1, function () {
console.log(this)
})()

如果this所在的方法不在对象的第一层,这时this只是指向当前一层的对象,而不会继承更上面的层。

1
2
3
4
5
6
7
8
9
10
var obj = {
name: 'Hello',
son: {
foo: function() {
console.log(this.name)
}
}
}

obj.son.foo() // undefined

此时的this指向的son,son没有name属性,就当然undefined了

③this在构造函数和类中

构造函数中的this,指的是实例对象。

1
2
3
4
5
6
7
8
9
10
11
var Obj = function (x) {
this.x = x
function sayHi() {
console.log(this.x)
}
}
Obj.prototype.sayHi = function () {
console.log(this.x)
}
var obj1 = new Obj('Hello World!')
obj1.sayHi() // "Hello World!"

类中的this,指的是实例对象。

1
2
3
4
5
6
7
8
9
10
class Obj {
constructor(x) {
this.x = x;
}
sayHi() {
console.log(this.x)
}
}
var obj1 = new Obj('Hello World!')
obj1.sayHi() // "Hello World!"

④this在dom事件中

1
2
3
4
5
6
7
8
9
10
11
12
13
<div id="test">今天是个好日子</div>
<script>
var myElem = document.getElementById('test')
myElem.onclick = function() {
console.log(this.innerHTML)
}

myElem.onclick() // 今天是个好日子
</script>
😑😶😏😣😥😮😯😍😘😗😙😚
<button onclick="this.style.display='none'">
点我后我就消失了
</button>

参考资料

阮一峰 JavaScript入门教程

日暮乡关 有道云笔记

菜鸟教程 Javascript教程

注:本文不以盈利为目的,仅做学习交流使用,若有侵权,请联系我删除,万分感谢!

作者

vear

发布于

2019-08-17

更新于

2023-11-23

许可协议

评论