一、是什么
当我们面向对象编程的时候,基本上是要和 this 打交道,如果使用不当则可能出现一些意想不到的错误。那 this 是什么?通常我们把它理解为一根指针,在不同的地方使用,就会有不同的指向,注意最重要的一点:this只有在执行的时候才能确定指向。
“你变或者不变,this 就在那里,不增不减.....”
二、绑定规则
既然 this 在不同场景有不同的指向,那么它就有一定的规则,通常情况下,this 有五种使用场景,如下:
- 默认绑定
- 隐式绑定
- 显示绑定
- new绑定
- 箭头函数绑定
接下来就分别说下各种场景的 this 指向。
1、默认绑定
默认绑定分为严格模式和非严格模式,在严格模式下 this 指向 undefined,非严格模式下 this 指向全局 window。
非严格模式:
var a = 'liben';
function moren1() {
console.log(this.a) // 打印liben this指向全局window
}
moren1()
非严格模式:
var a = 'liben';
function moren2() {
"use strict"
console.log(this.a) // 报错 this指向了undefined
}
moren2()
严格模式下调用函数则不影响默认绑定:
function moren3() {
console.log(this.a) // 打印liben
}
(function(){
"use strict"
moren3()
})()
2、隐式绑定
当函数引用有上下文对象时,隐式绑定规则会把函数中的 this 绑定到这个上下文对象。通常这种情况我们认为“谁调用的它,this 就指向谁。”
var ysObj = {
b: "liben",
yinshi1: function() {
console.log(this.b)
}
}
ysObj.yinshi1() // 打印liben
被隐式绑定的函数特定情况下会丢失绑定对象,应用默认绑定,把 this 绑定到全局对象或者 undefined 上,如下面的情况:
函数别名会丢失隐式绑定
var ysObj = {
b: "liben",
yinshi1: function() {
console.log(this.b)
}
}
var yinshi2 = ysObj.yinshi1 // 函数别名
yinshi2() // undefined 丢失this绑定 this指向window
参数传递就是一种隐式赋值 丢失this绑定
var ysObj = {
b: "liben",
yinshi1: function() {
console.log(this.b)
}
}
function doYinshi(fn) {
fn()
}
doYinshi(ysObj.yinshi1) // undefined
setTimeout(ysObj.yinshi1, 1000) // undefined
doYinshi.call(ysObj, ysObj.yinshi1) // undefined
3、显示绑定
通过 call() 、apply() 或者 bind() 方法,第一个参数是一个对象,在调用函数时将这个 this 绑定到这个对象。
function xianshi() {
console.log(this.c)
}
var xsObj = {
c: "xianshi"
}
xianshi.call(xsObj) // xianshi
xianshi.apply(xsObj) // xianshi
xianshi.bind(xsObj)() // xianshi
注意 bind 比较特殊,bind多次 this 只绑定第一次,且 this 指向就不会变了。
function xianshi() {
console.log(this.c)
}
var xsObj = {
c: "xianshi"
}
var xsObj2 = {
c: "xianshi2"
}
xianshi.bind(xsObj2).bind(xsObj)() // xianshi2 bind多次this只绑定第一次
xianshi.bind(xsObj2).call(xsObj) // xianshi2 bind后this指向就不会变了
如果我们将 null 或者是 undefined 作为this的绑定对象传入call、apply或者是bind,这些值在调用时会被忽略,实际应用的是默认绑定规则。
function xianshi() {
console.log(this.c)
}
var xsObj = {
c: "xianshi"
}
var c = "moren"
xianshi.call(null) // moren
4、new 绑定
a、创建(或者说构造)一个新对象 b、这个新对象会被执行[[Prototype]]连接 c、执行构造函数方法,属性和方法被添加到this引用的对象中(这个新对象会绑定到函数调用的this) d、如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象
function New_(d) {
this.d = d
}
var dd = new New_("newnewnew")
console.log(dd.d) // newnewnew
5、箭头函数绑定
箭头函数没有自己的 this,箭头函数中的 this 继承于外层代码块中的 this 。
var jtObj = {
f: "jiantou",
jiantou: () => {
console.log(this.f)
},
putong: function() {
console.log(this.f)
}
}
var f = "123"
jtObj.jiantou() // 123
jtObj.putong() // jiantou
三、绑定优先级
箭头函数 > new绑定 > 显式绑定 > 隐式绑定 > 默认绑定
例如箭头函数中使用显示绑定也不起作用:
var jiantou = () => {
console.log(this.f)
}
var jtObj = {
f: "jiantou",
}
var f = "123"
jiantou.call(jtObj) // 123
再例如 new构造函数 和 bind 显示绑定,this不会改变,说明new绑定优先于显示绑定:
function _newBind(h) {
this.h = h
console.log(this, this.__proto__ === _newBind.prototype)
}
var newbindObj = {
h: 'h_obj'
}
var NewBind = _newBind.bind(newbindObj)
new NewBind("liben") // _newBind {h: "liben"} true
四、总结
-
函数是否在new中调用(new绑定),如果是,那么this绑定的是新创建的对象
-
函数是否通过call,apply调用,或者使用了bind(即硬绑定),如果是,那么this绑定的就是指定的对象
-
函数是否在某个上下文对象中调用(隐式绑定),如果是的话,this绑定的是那个上下文对象。一般是obj.func()
-
如果以上都不是,那么使用默认绑定。如果在严格模式下,则绑定到undefined,否则绑定到全局对象
-
如果把null或者undefined作为this的绑定对象传入call、apply或者bind,这些值在调用时会被忽略,实际应用的是默认绑定规则
-
如果是箭头函数,箭头函数的 this 继承的是外层代码块的 this
本文是笔者总结编撰,如有偏颇,欢迎留言指正,若您觉得本文对你有用,不妨点个赞~