this
的指向到底是什么,我们学习之前要知道以下知识:
- 函数在调用时,
JavaScript
会默认给this
绑定一个值; this
的绑定和函数定义的位置和方式没有关系;this
的绑定与函数的调用方式和调用位置有很大关系;this
是在函数运行时被绑定的;
先学习this
的四种绑定规则,再研究一些规则之外的特殊情况:
- 默认绑定(普通函数被独立调用时)
- 隐式绑定(通过某个对象发起的函数调用时)
new
绑定(用new
关键字创建一个类的对象实例时)- 显式绑定(使用
call
、apply
和bind
调用时) - 四种规则的优先级
- 内置函数中的
this
绑定 - 规则之外1——忽略显式绑定
- 规则之外2——间接函数引用
- 规则之外3——箭头函数中
this
1. 默认绑定
普通函数被独立的调用
this
指向window
,严格模式下this
指向undefined
,例子如下:
// 'use strict'
var obj = {
name: 'obj',
foo: function () {
console.log(this)
},
}
function foo1() {
console.log(this)
}
var bar1 = obj.foo
foo1() // window
bar1() // window
function foo2(func) {
func()
}
foo2(obj.foo) // window
2. 隐式绑定
当一个函数作为对象的方法调用时
this
绑定到该对象,因为该对象内部有对函数的引用,是这个引用间接的将this
绑定到了这个对象上。严格模式下无异,例子如下:
var obj1 = {
name: 'obj1',
foo: function () {
console.log(this)
},
}
var obj2 = {
name: 'obj2',
obj1: obj1,
}
obj1.foo() // obj1对象
obj2.obj1.foo() // obj1对象
3. new绑定
使用
new
关键字创建一个类的对象实例时this
指向这个对象,严格模式下无异,例子如下:
/*
new关键字做了什么?
1.创建一个空对象:使用 new 关键字时,首先会创建一个空对象。
2.设置原型:新对象的原型会被设置为构造函数的 prototype 属性。
3.绑定 this:构造函数中的 this 会绑定到新创建的对象上。
4.执行构造函数:执行构造函数中的代码,this 指向新创建的对象。
5.返回对象:如果构造函数没有显式返回对象,new 表达式会自动返回新创建的对象
*/
function Person(name, age) {
this.name = name
this.age = age
console.log(this)
}
var person1 = new Person('好好', 18) // person {name: '好好',age: 18}
4. 显式绑定
当我们不想使用隐式绑定那样在对象内部包含函数的引用,但又想强制的改变this
绑定时就要使用显示绑定了
使用
call
、apply
和bind
(例子中可以看出三者区别)明确改变this
指向时,this
指向你传入的对象,传入的若不是对象时可看以下例子,严格模式下则是传入什么绑定什么不传对象也是如此,例子如下:
// "use strict";
function foo(name, age, talk) {
console.log(this, name, age, talk);
}
var obj = {
name: "obj",
foo: function () {
console.log(this);
},
};
foo.call(obj, "小韩", 18, true); // {name: 'obj', foo: ƒ} '小韩' 18 true
foo.apply({ name: "apply" }, ["大韩", 28, true]); // {name: 'apply'} '大韩' 28 true
foo.apply("abc"); // 包装类 String {'abc'}
foo.apply(123); // 包装类 Number {123}
foo.apply(true); // 包装类 Boolean {true}
// 下面代码解释具体看规则之外1的知识
foo.apply(null); // window
foo.apply(undefined); // window
/*
bind方法:
当this总是绑定到一个对象上,我们不想每次调用都使用apply和call, 这时就要使用到bind改变this指向了。
当使用bind方法时,它返回一个新的函数,这个新函数保留了构造函数的特性,可以使用new调用这个新函数。
*/
var baz = foo.bind(obj, "小小", 18, true); // bind()创建一个新的绑定函数
baz(); // {name: 'obj', foo: ƒ} '小小' 18 true
5. 四种规则的优先级
new
绑定优先级最高bind
(可以和new
一起使用)apply
/call
(不可以和new
一起使用)- 隐式绑定
- 默认绑定(默认最低,存在其他规则就会使用其他规则绑定
this
)
例子如下:
function foo(name) {
console.log(this, name);
}
var obj = {
name: "obj",
foo: function () {
console.log(this);
},
};
obj.foo.call({ name: "apply" }); // 显式比隐式高: { name: "apply" }
var bar = foo.bind("bind");
bar.call("call"); // bind比call高 String {'bind'}
bar.apply("apply"); //bind比apply高 String {'bind'}
new bar("new"); // new绑定比bind高 foo {} 'new'
6. 内置函数中的this绑定
var obj = {
name: "obj",
foo: function () {
console.log(this);
},
};
setTimeout(function () {
console.log(this); // window
}, 2000);
var nums = ["123", "344", "577"];
nums.forEach(function (f) {
console.log(this, "this"); // 三次window
});
nums.forEach(function (f) {
console.log(this, "this"); // 三次obj对象,是你第二个参数传入的this值
}, obj);
var box = document.querySelector(".box");
box.onclick = function () {
console.log(this); // box
console.log(this === box); // true
};
7. 规则之外1——忽略显式绑定
显式绑定中我们传入
null
和undefined
时,这个显式绑定会被忽略,使用默认绑定(严格模式下无此规则它是传什么绑定什么)
function foo() {
console.log(this);
}
foo.apply(null); // window
foo.apply(undefined); // window
8. 规则之外2——间接函数引用
间接函数引用
this
指向window
,严格模式下为undefined
function foo() {
console.log(this);
}
var obj1 = {
name: "obj1",
foo: foo,
};
const obj2 = {
name: "obj2",
};
obj2.foo = obj1.foo;
obj2.foo(); // 隐式绑定 {name: 'obj2', foo: ƒ}
(obj2.foo = obj1.foo)(); // 间接函数引用 window
(obj2.foo = obj1.foo)
返回为foo
函数,因为这是一个赋值表达式,赋值表达式会返回被赋的值即obj1.foo
,然后再进行调用时就相当于独立函数调用
9. 规则之外3——箭头函数中this
箭头函数不绑定
this
,它在自己的作用域中无this
,打印this
时会向外层作用域一级级查找
var obj = {
name: "obj",
foo: function () {
return () => {
console.log(this);
};
},
};
obj.foo.apply("apply")(); // String {'apply'} 打印的是foo函数的this