谈一谈JS中this指向问题

715 阅读3分钟

this的指向存在以下几种场景:

1-1 new 绑定

  • 构造函数被new调用
class A {
  constructor(a,b) {
    this.a = a;
    this.b = b
  }
  func1() {
    console.log(this.a) // 1
  }
}

const bar = new A(1,2) // 输出: bar实例,这里的this就是bar

可以看出,作为构造函数new调用的时候。this指向的是新创建的构造函数的实例


这里顺便解释下new的过程:

  • 创建一个新的对象 object
  • 把构造函数的prototype赋值给这个新对象的_proto_,即 object. _proto_ = A.prototype
  • 把新的对象赋值给当前的this; this = object
  • 执行构造函数 A()
  • 如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象,如果返回的不是对象,将会被忽略

1-2 显示绑定

  • 作为对象的方法使用,使用call, apply,bind改变this指向;
function attribute(name, height) {
    this.name = name
    this.height = height
}

function Person(name, height, occupation) {
    attribute.call(this, name, height)       // call 方式调用
    // attribute.bind(this, name, height)       // bind 方式调用
    // attribute.apply(this, [name, height])    // apply 方式调用,跟一个数组
    this.occupation = occupation
}

new Person('tiger', 178, '前端开发')
  • 顺便理一下bind的实现原理

一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。

Function.prototype.bind = (context) => {
 let self = this;
 const args = Array.prototype.slice.call(arguments, 1);
 const Temp = function() {}
 const Bind = function() {
   let args2 = Array.prototype.alice.call(arguments);
   // 当 bind 返回的函数作为构造函数的时候,bind 时指定的 this 值会失效,但传入的参数依然生效
   return self.apply(this instanceof temp ? this : context, args.concat(args2));
 }
 Temp.prototype = this.prototype; // 修改返回函数的prototype为绑定函数的prototype,实例就可以继承函数绑定中的值,其实是Bind.prototpe = self.prototype, 但若是直接修改Bind的原型,会直接修改绑定函数的prototype,所以接触中转函数Temp来实现
 Bind.prototype = new Temp();
 return Bind
}

1-3 隐式绑定

  • 函数在某个上下文中调用,this绑定的就是当前的上下文
let a = 'javascript';
const obj = {
  a: 'css',
  func: function() {
    console.log(this.a) 
  }
}

obj.func() // css

这里可以看出this的指向就是指向obj,同样我们再看看另一种情况

var a = 'javascript'

var obj = {
    a: 'css',
    b:{
        a:'html',
        func: function() {
            console.log(this.a)
        }
    }
}

obj.b.foo()      // 浏览器中输出: "html",这里的this指向 b

通过以上两个例子,可以得出结论,隐式调用的this指向: 就是指向当前调用该函数的上下文, 如果嵌套了多个对象,那么指向最后一个调用这个方法的对象。

1-4 默认绑定

  • 在不使用任何修饰的函数进行调用(即不包括以上三种情况); 非严格模式下this 绑定到全局对象(浏览器下是 winodw,node 环境是 global),严格模式下 this 绑定到 undefined (因为严格模式不允许 this 指向全局对象)。
let a = 'javascript';
function func1() {
  let a = 'css'console.log(this.a); 
}

func() // javascript, 这里的this就是默认绑定,指向window
  • this指向当前函数执行的上下文(理解这句话很重要)

2.this绑定的优先级

new 绑定 > 显示绑定 > 隐式绑定 > 默认绑定

!注意,箭头函数没有自己的this;箭头函数的 this 绑定是无法通过 call、apply、bind 被修改的,且因为箭头函数没有构造函数 constructor,所以也不可以使用 new 调用,即不能作为构造函数,否则会报错。