我们首先来看一个例子:
function fn() {
console.log(this);
}
// 调用方式一: 直接调用
fn(); // window
// 调用方式二: 将fn放到一个对象中,再调用
var obj = {
name: "hh",
fn: fn
}
obj.fn() // 对象obj
// 调用方式三: 通过call调用
fn.call("xxx"); // 字符串"xxx"
可以看到同一个函数通过不同的方式调用,其中的this也是不同的,而其中的绑定规则是怎样的呢?我们现在就来搞清楚!
1. 默认绑定
当函数作为独立函数(可以理解为函数没有绑定到任何对象上进行调用)调用为默认绑定,此时的this指向为全局对象(window)
例1:普通调用
function fn() {
console.log(this); // window
}
fn();
例2:链式调用
function test1() {
console.log(this); // window
test2();
}
function test2() {
console.log(this); // window
test3()
}
function test3() {
console.log(this); // window
}
test1();
例3:高阶函数调用
function fn(foo) {
foo()
}
function bar() {
console.log(this); // window
}
fn(bar);
这些例子的结果都为window,这是因为这些函数都没有任何的对象绑定,是作为一个独立函数的调用
2. 隐式绑定
当函数是通过某个对象进行调用为隐式绑定,此时的this指向为调用的对象
例1:通过对象调用
function fn() {
console.log(this); // obj
}
var obj = {
name: "why",
fn: fn
}
obj.fn();
例2:隐式丢失
function fn() {
console.log(this); // window
}
var obj = {
name: "obj",
fn: fn
}
var bar = obj.fn;
bar();
此时this指向的是window,这是为什么呢?因为fn最终是被bar调用的,而bar没有绑定任何的对象,也就没有形成隐式绑定,相当于是一种默认绑定
3. 显式绑定
明确的表示绑定了this指向的对象,我们称之为显示绑定
3.1 call、apply和bind
通过call、apply和bind显式绑定this对象后,此时this指向绑定的对象
- 特殊情况:当传入参数为
null或undefined时,this指向为全局对象(window)
function fn() {
console.log(this);
}
var bar = fn.bind('haha');
fn.call(window); // window
fn.call(123); // 123
fn.call(null); // window
fn.call(undefined); // window
bar() // 'haha'
3.2 内置函数
有时候我们要调用一些JS的内置函数,或者第三方库中的内置函数,而这些函数中的this又是如何绑定的呢?
-
setTimeout
setTimeout中会传入一个函数,这个函数中的this通常绑定的是全局对象(window)
setTimeout(function() { console.log(this); // window }, 3000);
-
数组的forEach
在默认情况下传入的函数是自动调用函数(默认绑定),所以此时传入函数this指向也是全局对象(window)
var arr = ["aa", "bb", "cc"]; arr.forEach(function(item) { console.log(this); // window });如果我们想要改变forEach指向可不可以呢?答案是可以的,其实forEach可以传入第二个参数来指定this的指向
var arr = ["aa", "bb", "cc"]; arr.forEach(function(item) { console.log(this); // 666 }, 666); -
div的点击
假设我们有一个id为box的div元素,js代码如下
var box = document.querySelector("#box"); box.onclick = function() { console.log(this); // box对象 }此时this的指向为box对象,这是因为在发生点击时,传入的回调函数被调用时,会将box对象绑定到该函数中
4. new绑定
在JS中,可以使用new关键字来调用函数时,来创建一个全新的对象,此时this指向为创建的新对象
function Person(name) {
this.name = name;
return this; // Person {name: 'hh'}
}
var hh = new Person("hh");
console.log(hh);
了解了这么多绑定规则,那么如果某个函数调用出现多条规则时该怎样判断应用哪条规则呢?这时候就要知道这几条规则间的优先级是怎样的了
在这四条规则中优先级为:new绑定 > 显示绑定 > 隐式绑定 > 默认绑定
大家感兴趣的话可以举些简单的例子验证一下
另外说明:this的四种标准规则不适用于箭头函数(也就是不绑定this),而是根据外层作用域来决定this。