写一写 JS中的 this 指向的问题

38 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第 17 天,点击查看活动详情

start

  • 写一写 this 指向的问题。

问题

有个小伙伴,遇到过这么一个问题:

<!DOCTYPE html>
<html lang="en">
  <body>
    <div id="tomato">this指向</div>

    <script>
      document.getElementById('tomato').addEventListener('click', function () {
        console.log('你好', this)
      })
    </script>
  </body>
</html>

请问 这里的 this 指向谁?

答:这里的 this 指向 <div id="tomato">this指向</div>

小伙伴写这一串代码,写在的是Vue中,然后通过 this 获取data的数据,无法获取。其实弄清楚 这个地方的 this ,问题就很好解决了。

说说 this 指向

总共五种情况:

  1. 默认绑定
  2. 隐式绑定
  3. 显示绑定
  4. new 绑定
  5. 箭头函数

1. 默认绑定

考虑下面代码:

function foo(){
    var a = 1 ;
    console.log(this.a);    
}
var a = 10;
foo();

这种 foo() 自己执行的情况,就是默认绑定,默认绑定this指向为 window,严格模式下为 undefined ;

所以这里的this指向window,window.a,所以打印10

2. 隐式绑定

function foo(){
    console.log(this.a);
}
var obj = {
    a : 10,
    foo : foo
}
foo();                // ?

obj.foo();            // ?

谁调用函数,函数的this就指向谁

  • 第一个打印,上一点讲过了,打印 undefined
  • 第二个打印,就是 隐式绑定,obj.foo(); obj调用的foo,所以this指向obj,所以打印 obj.a

3. 显示绑定

除了上面的两种,我们可以手动的指定 this。主要使用:call apply bind,这三个函数都可以将 this指向他们第一个参数。

function foo() {
  console.log(this.a)
}
var obj = {
  a: 10,
  foo: foo,
}

foo() // undefined

obj.foo() // 10

foo.call({ a: '使用call' }) // 使用call

foo.apply({ a: '使用apply' }) // 使用apply

var bn = foo.bind({ a: '使用bind' })
bn() // 使用bind

4. new 绑定

function foo() {
  this.a = 10
  console.log(this)
}

foo()

console.log(window.a)
var obj = new foo()
console.log(obj.a)

其实了解new 关键词做了什么,new的this指向就很清晰了。

new关键词做了:

  1. 创建一个新对象。
  2. 把这个新对象的__proto__属性指向 原函数的prototype属性。(即继承原函数的原型)
  3. 将这个新对象绑定到 此函数的this上
  4. 返回新对象,如果这个函数没有返回其他对象

所以 new 的时候,此函数的this指向新生成的对象。

  1. foo() 自己调用,所以此时的this执行 window,所以 window.a =10 ,第一个打印 window

  2. 第二个打印,因为上一句foo(),修改了window,所以此时 window.a是 10 ,第二个打印10

  3. new foo()可以理解为:

    this={}
    this.a = 10
    console.log(this)
    obj=this
    

​ 所以 new foo() 会先打印 {a:10}

  1. 第四个打印 10

5. 箭头函数

箭头函数它没有自己的this对象,内部的this就是定义时上层作用域中的this

例如将问题中的写法改成箭头函数,此时this就指向window,而不是dom对象了:

document.getElementById('tomato').addEventListener('click',  ()=>{
        console.log('你好', this)
      })

6.优先级

优先级依次为:

箭头函数》new 绑定》显示绑定》隐式绑定》默认绑定

练习

才学习完毕,来几个热乎的题目熟悉熟悉

注意:下列题目的运行环境,若无特殊说明,皆为非严格模式

题目一

题目:

var name = "window";

var person = {
  name: "person",
  sayName: function () {
    console.log(this.name);
  }
};

function sayName() {
  var sss = person.sayName;
  
  sss();
  person.sayName();
  (person.sayName)();
  (b = person.sayName)();
  
}

sayName();

解析:

其实考察的就是默认绑定和隐式绑定,这里的sss其实存储的就是 function () { console.log(this.name); }的引用地址,所以就类似于foo(),所以它的this指向window

答案:

window
person
person
window

题目二

var name = 'window'
var person1 = {
  name: 'person1',
  foo1: function() {
    console.log(this.name)
  },
  foo2: () => console.log(this.name),
  foo3: function() {
    return function() {
      console.log(this.name)
    }
  },
  foo4: function() {
    return () => {
      console.log(this.name)
    }
  }
}
var person2 = { name: 'person2' }

person1.foo1()
person1.foo1.call(person2)

person1.foo2()
person1.foo2.call(person2)

person1.foo3()()
person1.foo3.call(person2)()
person1.foo3().call(person2)

person1.foo4()()
person1.foo4.call(person2)()
person1.foo4().call(person2)

解析:

这道题其实考察的就是,隐式绑定,显示绑定,箭头函数。

**箭头函数它没有自己的this对象,内部的this就是定义时上层作用域中的this。**所以在上述代码中 foo4 中的箭头函数 this 指向一直跟随 foo4这个函数的this。

答案:


person1
person2
window
window
window
window
person2
person1
person2
person1

end

  • 回头再来看小伙伴遇到的问题,很简单有没有。
  • 想要正确的获取外部的this,解决方案:1.可以使用箭头函数;2.外部定义一个变量存储this,传入进来即可。