本文针对浏览器端的讲解
代码都是在 strict mode
下运行的,除非特别说明。关于英文单词的学习和记忆,大家可以访问这个网站。
从 这 里的 Value 部分可以得出,this
的值是 ‘A property of an execution context (global, function or eval)’,即是 execution context 的一个属性,在非 strict mode
下总是一个 object,在 strict mode
下可以是任何类型。
也就是说,有 this
属性的地方就这三个地方。eval 用的比较少,在此忽略。global execution context 可理解为 global scope,function execution context 可理解为 function scope。
global execution context 的 this
属性在浏览器端就是指 window
了。
对于没有 this
属性的地方出现了 this
this
从外部有 this
属性的地方取,这就像是寻找一个变量的值一样 看这。比如数组里,下面的例子 this
指 window
(取自 global execution context 的 this
属性)
let arr = [1, this];
console.log(arr[1]); // window
再比如对象里(下面代码片段包含左右两个例子)
let obj1 = { let obj2 = {
name: this [this]: 'derek'
} }
// window // 'derek'
console.log(obj1['name']); console.log(obj2[this]);
需要注意的是 this
是从创建的地方的外部有 this
属性的地方取的,而不是引用的地方的外部,比如下面例子
let arr = [1, this];
function func() {
console.log(this); // undefined
console.log(arr[1]); // window
}
func();
数组 arr 里的 this
值在创建 arr 的时候就已经确定了,不受引用 arr 的地方的限制。
接下来就剩函数里的 this
了。函数里 this
的值的影响因素比较多,主要包括以下几个方面
- 是普通函数还是箭头函数,
- 是否在
strict mode
下运行, - 调用该函数时前面有没有加
.
或者[]
(member access operators),可参考 The value of “this”, - 是否在
setTimout
、setInterval
的回调函数里, - 有没有使用函数的
call
、apply
、bind
方法来绑定this
的值。
下面分为普通函数和箭头函数两大类来展开。
普通函数里的 this
值
因为普通函数有 this
属性,所以普通函数里的 this
值不会从外部获取。
是否为 strict mode
下面的例子,如果是 strict mode
那么 func 函数里的 this
就为 undefined
,如果不是 strict mode
就是全局对象 window
function func() {
console.log(this); // undefined if strict mode, window otherwise
}
func();
调用该函数时前面有没有加 member access operator
当使用了 member access operator 调用该函数时,遵从 object before dot 规则,即该函数里的 this
值为调用该函数的对象,this
值是动态的,参考这。不受是否为 strict mode
的影响
let obj = {
name: "josh",
func: function () {
console.log(this === obj); // true
}
};
obj.func()
再比如下面的例子里,数组 arr 里的 this
取自函数 func,而函数 func 的 this
属性值就是调用该函数的对象 obj
let obj = {
name: "josh",
func() {
let arr = [1, this];
console.log(arr[1] === obj); // true
}
};
obj.func();
当与浏览器端的监听事件结合时的判断也一样
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>csdn blog</title>
</head>
<body>
<div id="elem">click me</div>
<script>
let elem = document.getElementById("elem");
elem.onclick = function () {
console.log(this); // div#elem,遵从 object before dot 规则
};
</script>
</body>
</html>
或者监听事件直接内嵌于 html 元素里时,this
就指那个元素
<div id="elem" onclick="console.log(this); // div#elem">click me</div>
当浏览器处理这个元素时,会生成 elem.onclick = function() { console.log(this); }
,参考这,变成了上一个例子的写法。
另外,就是监听事件用 addEventListener
方法写的时候,addEventListener
回调函数里 this
的值指调用 addEventListener
的对象,结果最终也是一样
elem.addEventListener("click", function() {
console.log(this); // div#elem
});
所以这三种事件监听的写法对应函数里 this
的值是一样的,参考这。
是否在 setTimout()
、setInterval()
的回调函数里
当是在 setTimout
、setInterval
的回调函数里时,this
指全局对象 window
,不受是否为 strict mode
的影响
function func() {
console.log(this); // window
}
setTimeout(func, 0);
因为这两个方法都属于全局对象 window
里的,也常写成
function func() {
console.log(this); // window
}
window.setTimeout(func, 0);
注意当是在 js 内置对象(build-in objects)的回调函数里时,比如 array.forEach()
里,不会影响 this
值的判断。下面例子第一个 this
的值跟前面的 ‘是否为 strict mode
’ 判断一样
function func() {
console.log(this); // undefined
setTimeout(function() {
console.log(this); // window
},0);
}
[1].forEach(func);
当 func 函数是在一个 object 里时,比如下面的情况
let obj = {
name: "josh",
func: function () {
console.log(this); // undefined
setTimeout(function() {
console.log(this); // window
},0);
}
};
[1].forEach(obj.func);
结果是一样的。注意这里只是引用了函数 obj.func
,执行 obj.func
的是 [1].forEach
函数。可以参考这。
有没有使用函数的 call、apply、bind 方法
如果是使用了函数的 call
、apply
、bind
方法,那么这个普通函数里 this
的值就是绑定的 thisArg
(在非 strict mode
下为 thisArg
的对象形式)
function func() {
console.log(this); // "a string"
};
func.call("a string");
下面的例子即使该函数前面使用了 member access operator、即使在 setTimout()
的回调函数里,结果都是绑定的 thisArg
function func() {
console.log(this); // 123
};
let obj = {
name: 'josh',
func
}
setTimeout(obj.func.bind(123), 0);
箭头函数里的 this
值
箭头函数比较特殊,它是没有 this
属性的,this
的值始终为创建这个箭头函数的时候从外部有 this
属性的地方获取的,不受其它因素的影响,跟前面讲的 对于没有 this
属性的地方出现了 this
的判断一样。
下面例子箭头函数 func 的 this
始终为 window
,即使进行了 func.call('a string');
想绑定 this
的值
let func = () => {
console.log(this); // window
};
func.call('a string');
即使使用了 .
或者 []
(member access operator)
let obj = {
name: "josh",
arrFunc: () => {
console.log(this); // window
}
};
obj.arrFunc();
有的同学可能会问箭头函数 arrFunc 是在 obj 里创建的,this
从外部获取为啥不是 obj?原因很简单,因为 obj 不是一个 execution context 并没有 this
属性。
箭头函数除了没有 this
属性外,它还没有特殊变量 arguments
和类继承里的 super
.
补充
定义在全局作用域的函数自动成为全局对象 window
的属性,参考这。
所以下面例子当调用 func 前面加了 window
的时候,func 里 this
的值就为 window
function func() {
console.log(this); // window
}
window.func();
this
在构造函数和 class
里的值
构造函数里的 this
构造函数严格来说也是普通函数,不过有两处不同 (看这)
- 命名的时候首字母通常大写,
- 仅能使用
new
操作符来调用。
当一个构造函数被 new
后,会在该构造函数里创建一个空对象并赋给 this
,该构造函数里的代码主要用来修改 this
比如添加新的属性,最后返回 this
function Town(population) {
this.population = population;
this.addOnePerson = function () {
this.population++;
}
}
let myTown = new Town(100000);
console.log(myTown.population); // 100000
myTown.addOnePerson();
console.log(myTown.population); // 100001
函数 Town 等同于
function Town(population) {
// this = {}; (隐式声明)
// 向 this 添加新的属性
this.population = population;
this.addOnePerson = function () {
this.population++;
}
// return this; (隐式声明)
}
因此构造函数里的 this
属性就是构造函数被 new
后生成的实例对象。
class
里的 this
class
也是一类函数
class Town {
constructor(population) {
this.population = population;
}
}
console.log(typeof Town); // function
关于 class
的语法可参考这: class is a function, or, more precisely the constructor method.
简单说, class
Town 里的 constructor
类似于上面讲的构造函数,constructor
里的 this
属性是 class
被 new
后生成的实例对象,class
里的方法 (method) 储存于 Town.prototype
,class
里的字段 (field) 储存在 Town 被 new
后生成的实例对象里即 this
里。实例也可以使用 Town.prototype
对象里的 method 和 constructor
属性,可参考 F.prototype。
class Home {
obj = { // class field or property
a: "obj",
bar: () => {
console.log("bar's this: ", this);
}
};
}
let home = new Home();
home.obj.bar();
因为 class
里的字段直接储存于被 new
后生成的实例里,所以 bar’s this 就是 home 实例。创建 obj 是 class
被 new
生成实例的时候。
另外,注意 class 里的 method with computed name 情况,computed name 里的 this
从 class
外部取,如果是在全局作用域下就是 window
了,比如下面的例子
// initialization order: class fields then constructor
class User {
that = console.log(this) || 'derek'; // 打印的是实例 user, 一个 empty object
// now, this = { that: 'derek' };
name = console.log(this) || this.that; // 打印 { that: 'derek' }. name is 'derek'
name = console.log(this) || this.getName(); // 打印 { that: 'derek', name: 'derek' }.
// After assignment, name is 'new derek'
constructor(name) {
console.log(this.name); // 'new derek'
name ? this.name = name : ''; // update name field based on parameter name
console.log(this.name); // josh
console.log("constructor this", this); // 实例 with 最新的 class fields
}
getName() {
return 'new derek';
}
[this]() { // this is the global object window
console.log("inside method 'this'", this); // 实例 user (the instance user)
}
}
let user = new User('josh');
user[this](); // this is the global object window
我们知道 console.log()
的参数里不管写什么,console.log()
都返回 undefined
,所以第三行 that = 'derek'
.
class
static methods 里的 this
可参考这。
class User {
static staticMethod() {
console.log(this === User);
}
}
User.staticMethod(); // true
遵从 object before dot 规则。
前面提到了 “class is a function, or, more precisely the constructor method”,所以在静态方法里可以进行下面的操作
class Article {
constructor(title, date) {
this.title = title;
this.date = date;
}
static createTodays() {
return new this("Today's digest", new Date());
}
}
let today = Article.createTodays();
console.log(today.title); // "Today's digest"
本篇文章至此就结束了,喜欢的给个赞吧。