this是谁?

378 阅读4分钟

一、是什么

 当我们面向对象编程的时候,基本上是要和 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


本文是笔者总结编撰,如有偏颇,欢迎留言指正,若您觉得本文对你有用,不妨点个赞~

关于作者: GitHub 简书 掘金