看完这篇this指向,面试通过不在话下

227 阅读4分钟

前言

this指向是js中基础中的基础,当然也是大厂面试的时候常考的。下面我这题是阿里前端面试时出现的一道题,大家看题目的时候,思考一下,看你能否答得出来

    var name = 222
    var a = {
        name:111,
        say:function(){
            console.log(this.name)  
        }
    }
    var fun = a.say 
    fun() 
    a.say() 
    var b = {
        name: 333,
        say: function(fn) {
             fn()
        }
    }
    b.say(a.say) 
    b.say = a.say 
    b.say() 

现在我们先不着急着写答案。我们先来说说this的概念

概念

this指向在函数定义的时候是无法确定的,只有在函数执行的时候我们才能最终确定它指向谁。 this永远指向的都是最后调用它的对象,也就是看它执行的时候是谁调用的

this执行的几个场景

  • 在函数中直接使用,指向的是window
function sayHi(str){
    console.log(str)
}
sayHi('hello')
// sayHi('hello') ===sayHi.call(window,'hello')
  • 函数作为对象的方法被调用(谁调用我,我就指向谁)
var person = {
    name:'Cynthia',
    gender:'female',
    say:function(str){
        console.log(`我的英文名字是${this.name},中文名${str}`)
    }
}
person.say('...哈哈哈,不告诉你')
// person.say()相当于 person.say.call(person,'...哈哈哈,不告诉你')
  • 作为构造函数执行
function Foo(name){
	this.name = name
  console.log(this) // 指向构造函数 Foo
}
var f = new Foo('Cynthia')
  • call、apply、bind改变this的指向
     function fn(name,age){
            console.log(name)
            console.log(this) // 
        }
        fn.call({x:100},'lily',20) // this-->{x:100}
        fn.apply({b:1023},['礼拜',30]) // this-->{y:1023}
        var fn2 = function(name,age){
            console.log(name) // 张三
            console.log(this) // {y:100}
        }.bind({y:200})
        fn2('张三',30)

了解完上面的,我们就开始讲解一下开篇的那道题目吧

    var name = 222
    var a = {
        name:111,
        say:function(){
            console.log(this.name)
        }
    }
    var fun =a.say // fun.call(window) 函数的直接调用都是指向window
    fun() // this指向是window,所以是222
    a.say() // a.say.call(a) 111
    var b ={
        name:333,
        say:function(fn){
            fn()
        }
    }
    b.say(a.say) // 这里相当于是函数的直接调用 函数直接调用的话是指向window的,所以是222
    b.say = a.say // 把a.say方法直接复制给b.say,相当于b.say=function(){console.log(this.name)}
    b.say() // b/.say.call(b) 输出是333
  • fun()的结果讲解: a.say赋值给fun的时候,只是把这个a.say函数赋值了fun,此时的fun就相当于是一个函数,那么函数的this只要你不修改它的指向,它默认的指向就是指向window的。所以fun的写法就相当于是fun.call(window) ,输出结果是 222
  • a.say()的结果讲解: 这个不用多说,就是函数作为对象的方法被调用,a调用say方法,所以this指向是a,相当于 a.say.call(a),输出结果是 111
  • b.say(a.say)的结果讲解: 这里是直接把a.say传进去,然后直接调用,函数直接调用的话,指向是window,所以输出结果是 222
  • b.say()的结果讲解: 把a.say方法直接复制给b.say,相当于b.say=function(){console.log(this.name)},那么b.say相当于b.say.call(b), 输出结果是333

箭头函数中的this指向

  • 箭头函数中的this是在定义函数的时候绑定,而不是在执行函数的时候绑定
  • 箭头函数中,this指向的固定化,并不是因为箭头函数内部有绑定的this的机制,实际原因是箭头函数根本没有自己的this,导致内部的this就是外层代码块的this。正是因为它没有this,所以也就不能用作构造函数

下面我们用两道题来讲解

第一道题
   // 箭头函数中的this
    var x = 11
    var obj = {
        x:22,
        say: ()=>{
            console.log(this.x)
        }
    }
    obj.say() // 11

说明:所谓的定义时候绑定,就是this是继承自父执行上下文中的this,比如这里的箭头函数中的this.x,箭头函数say与obj中的x是以平级的key:value形式存在的,也就是箭头函数say本身所在的对象就是obj,而obj的父执行上下文就是window,因此这里的this.x实际上表示的是window.x,所以输出的是11

第二道题
var address = {
        country:'China',
        getCity:function(){
            var country = this.country
            var fn = ()=> console.log(this.country + 'GZ')
            return fn()
        }
}
    address.getCity()

说明:例子中的箭头函数本身是在getCity方法中的,getCity的父执行上下文是address,因此这里的this指向是address 注意点:箭头函数的this是不会被修改的

var name = 'Cynthia'
var person = {
    name:'Aria'
}
var fn = ()=>{
console.log(this.name)
}
fn.call(person) // Cynthia