此文首发于 https://lijing0906.github.io
在上篇JS继承中涉及到的好几个知识点都想写,比如call()
、apply()
从而牵出和bind()
的区别,由Object.create()
想到与new Object()
、{}
的区别,原型链以及作用域,然而在参考其他博客时发现,应该先把JS中的this
指向弄明白。
《你不知道的JavaScript》(上卷)第二部分讲到了this
,算是比较权威的关于this
的讲解,但我觉得有些地方讲得还是晦涩难懂,需要结合一些博客来理解会容易理解一些。
为什么要用this
this
提供一种优雅的方式来隐式“传递”一个对象引用,在函数中显示传入一个上下文对象,避免在代码越来越复杂的情况下造成上下文对象混乱。
this到底是什么
this
就像它的词性一样,是个代词,表指代什么,在JS中表示指代某个对象。this
是在函数运行时绑定到某个对象上,并不是在函数定义时被绑定的,因此this
的绑定(即this
的指向)与函数的声明位置没关系,只取决于函数的调用方式。
绑定规则
说五种绑定规则之前,先说说不同作用域中this
的指向,包括全局作用域(Global Scope)和局部作用域(Local Scope)。
全局作用域(Global Scope)
所有运行环境中JS运行时都只有唯一的全局对象,在浏览器中,全局对象是window
;在node.js
中全局对象是global
。
在全局作用域中(任何函数体外的代码),this
指向的是全局对象,不管是不是在严格模式下。
局部作用域(Local Scope)
局部作用域可以理解为{}
包裹的区域,this
的指向就根据调用方法不同而不同。
一、默认绑定
- 独立函数调用,没有其他规则绑定时的默认规则,也是最常用的绑定规则,
this
指向全局对象window
。
function foo() {
console.log(this);
console.log(this.a);
}
var a = 2;
foo(); // Window对象 2
- 严格模式下,无法执行默认绑定把this绑定到全局对象上,因此,没有指定值时,
this
会绑定到undefined
上。
虽然this
的绑定规则完全取决于调用位置,但是只有foo()
***运行***在非严格模式下时,默认绑定才能把this
绑定到全局对象;严格模式下***调用***函数则不影响默认绑定。
function foo() {
// 运行在严格模式下,this会绑定到undefined
"use strict";
console.log(this.a);
}
var a = 2;
// 这里虽然foo()是在全局作用域中执行,但是foo里的代码运行在严格模式下,所以this被绑定到了undefined上。
foo(); // TypeError: Cannot read property 'a' of undefined
// --------------------------------------
function foo() {
console.log(this.a);
}
var a = 2;
(function() {
// 严格模式下调用函数则不影响默认绑定
"use strict";
foo(); // 2
})();
二、隐式绑定
当函数作为对象属性被调用时,函数中的this
指向(被绑定到)调用这个函数的对象,这就是隐式绑定。注意:最后一层在调用中起作用,即最接近函数调用的那层起作用。
function foo() {
console.log(this.a);
}
var a = 2;
var obj = {
a: 3,
foo: foo
};
obj.foo(); // 3
为什么是3?那就需要了解一下内存中基础类型数据和引用数据类型是怎么存储的,可以看看阮一峰的JavaScript的this原理。
上面代码的执行过程:获取obj.foo
属性——>根据引用关系(引用地址)找到foo
函数,执行函数调用。obj
离foo()
最近,this
被绑定到obj
上。
- 多层调用
function foo() {
console.log(this.a);
}
var a = 2;
var obj1 = {
a: 4,
foo: foo
};
var obj2 = {
a: 3,
obj1: obj1
};
obj2.obj1.foo(); // 4
同样看一下调用过程:获取obj2.obj1
属性——>根据引用关系(引用地址)获取obj1
对象——>再重复第二步找到foo
函数——>执行函数调用。obj1
离foo()
最近,this
被绑定到obj1
上。
2. 隐式丢失(函数别名)
function foo() {
console.log(this.a);
}
var