一、this
1.1 为什么要使用this
this提供了一种更优雅的方式来隐式“传递”一个对象引用,因此可以将API设计得更加简洁并且易于复用。
1.2 对this认识的误区
1. this指向自身 ×
- 如果要从函数对象内部引用它自身,那只使用this是不够的。通常还需要一个指向函数对象的词法标识符(变量)来引用它。
- 对于一个具名函数foo(),在它的内部可以使用foo来引用自身(例如:foo.count)
- 对于匿名函数,无法从函数内部引用自身(曾经可以使用arguments.callee来引用,但目前已经被弃用)
- 可以强制this指向foo函数对象,如下:
function foo(num) {
console.log("foo: " + num);
this.count++; // 使用this关键词
}
foo.count = 0;
for(let i = 0; i < 10; i++) {
if(i>5) {
// 使用call(...)可以确保this指向函数对象foo本身
foo.call(foo, i);
}
}
console.log(foo.count); // 4
2. this指向函数的作用域 ×
- this在任何情况下都不指向函数的词法作用域,使用this不可能在词法作用域中查到什么
1.3 this是什么
当一个函数被调用时,会创建一个活动记录(也称执行上下文)。这个记录会包含:函数在哪里被调用(调用栈)、函数的调用方式、传入的参数等信息。this就是这个记录的一个属性,会在函数执行的过程中用到。
this是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件。this绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。
1.4 this绑定规则
优先级:new绑定 > 显示绑定 > 隐式绑定 > 默认绑定
1. 默认绑定(无法应用其它规则时的默认规则)
function foo() {
console.log(this.a);
}
var a = 2;
foo(); // 2
这里,foo()是直接使用不带任何修饰的函数引用进行调用的,因此只能使用默认绑定,无法应用其它规则。
另外,如果使用严格模式(strict mode),不能将全局对象用于默认绑定,因此this会绑定到undefined
2. 隐式绑定
考虑调用位置是否有上下文对象,或者说是被某个对象拥有或者包含。隐式绑定规则会把函数调用中的this绑定到这个上下文对象。
然而,对象属性引用链中只有上一层或者说最后一层在调用位置中起作用。
隐式绑定最常见的问题是隐式丢失,即,被隐式绑定的函数会丢失绑定对象,它会应用默认绑定,从而把this绑定到全局对象或者undefined上。参数传递其实就是一种隐式赋值。
3. 显式绑定
如果不想在对象内部包含函数引用,而在某个对象上强制调用函数,这个时候就需要用到显示绑定了。
可以使用call(...)和apply(...)方法。
例如:foo.call(obj)就可以在调用foo时强制把它的this绑定到obj上。如果传入了一个基本数据类型的值,会被转换成它的对象形式(“装箱”)。
面对丢失绑定问题,可以使用以下两种方法:
(1)硬绑定
在内部手动调用foo.call(obj)
(2)API调用的“上下文”
function foo(el) {
console.log(el, this.id);
}
var obj = {
id: "awesome"
}
// 调用foo(...)时把this绑定到obj
[1,2,3].forEach(foo, obj);
// 1 awesome 2 awesome 3 awesome
4. new绑定
使用new来调用函数(发生构造函数调用)时,会自动执行下面的操作:
- 创建(构造)一个全新的对象
- 这个新对象会被执行[[Prototype]]连接
- 这个新对象会绑定到函数调用的this
- 如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象
function foo(a) {
this.a = a;
}
var bar = new foo(2);
console.log(bar.a); // 2
使用new来调用foo(...)时,我们会构造一个新对象并把它绑定到foo(...)调用中的this上。new是最后一种可以影响函数调用时this绑定行为的方法,称之为new绑定。
1.5 普通函数中的this指向
- 如果函数在new中调用,this绑定的是新创建的对象。
var bar = new foo()
- 如果函数通过call apply(显示绑定)或者硬绑定调用,this绑定的是指定的对象。
var bar = foo.call(obj2)
- 如果函数在某个上下文对象中调用(隐式绑定),this绑定的是那个上下文对象。
var bar = obj1.foo()
- 如果都不是的话,使用默认绑定。在严格模式下,绑定到undefined,否则绑定到全局对象。
var bar = foo()
1.6 箭头函数中的this指向
this取值取上级作用域的值。在箭头函数中,根据当前的词法作用域来决定this
欢迎评论区补充讨论!