重学javaScript (十三)| 关于this的面试题

214 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第13天,点击查看活动详情

回顾

先来回顾一下this的绑定规则

  • 默认绑定
  • 隐式绑定
  • 显示绑定
  • new关键字绑定

特殊情况

  • 箭头函数的this
  • 规则外的this绑定

它们的优先级顺序如下,隐式绑定大于默认绑定,显式绑定大于隐式绑定,new关键字大于隐式绑定,new关键字大于显示绑定中的bind

详见 重学javaScript (十二)| this绑定规则的优先级 - 掘金 (juejin.cn)

几道题目

我们按照学习的规则来看几道面试题巩固一下

  1. 题目1
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();

先来看执行结果

image.png

解析:

  • sss() 这一行,因为是默认绑定,所以打印结果为window
  • person.sayName() 这一行,是隐式绑定到了person上,所以打印结果是person
  • (person.sayName)() 这一行跟 person.sayName() 等同,也是隐式绑定到了person上,所以打印结果是person
  • (b = person.sayName)(); 这一行相当于执行b() 是默认绑定,就是打印window
  1. 题目2
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); 

来看下运行结果

image.png

解析:

  • person1.foo1() 这一行就是隐式绑定,打印结果是person1
  • person1.foo1.call(person2) 这一行是做了显式绑定,打印结果是person2
  • person1.foo2() 这一行的foo2是箭头函数,它是不绑定this的,this指向就是父级作用域,它的父级作用域是window,所以打印window
  • person1.foo2.call(person2) 这一行foo2是箭头函数,所以打印window
  • person1.foo3()() 这一行其实是默认绑定,所以打印window
  • person1.foo3.call(person2)() 这一行其实是默认绑定,所以打印window
  • person1.foo3().call(person2) 这一行是显示绑定,所以打印person2
  • person1.foo4()() 这一行 foo4是箭头函数,找它父级作用域,所以打印 person1
  • person1.foo4.call(person2)() 这一行 foo4是箭头函数,找它父级作用域,父级函数已经经过显式绑定,所以打印 person2
  • person1.foo4().call(person2) 这一行 foo4是箭头函数,找它父级作用域,所以打印 person1
  1. 题目3
var name = 'window'
function Person (name) {
  this.name = name
  this.foo1 = function () {
    console.log(this.name)
  },
  this.foo2 = () => console.log(this.name),
  this.foo3 = function () {
    return function () {
      console.log(this.name)
    }
  },
  this.foo4 = function () {
    return () => {
      console.log(this.name)
    }
  }
}
var person1 = new Person('person1')
var person2 = new Person('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) 

来看下运行结果

image.png

解析:

  • person1.foo1() 这一行就是隐式绑定,打印结果是person1
  • person1.foo1.call(person2) 这一行是做了显式绑定,打印结果是person2
  • person1.foo2() 这一行的foo2是箭头函数,它是不绑定this的,this指向就是父级作用域,它的父级作用域是person1,所以打印person1
  • person1.foo2.call(person2) 这一行foo2是箭头函数,所以打印person1
  • person1.foo3()() 这一行其实是默认绑定,所以打印window
  • person1.foo3.call(person2)() 这一行其实是默认绑定,所以打印window
  • person1.foo3().call(person2) 这一行是显示绑定,所以打印person2
  • person1.foo4()() 这一行 foo4是箭头函数,找它父级作用域,所以打印 person1
  • person1.foo4.call(person2)() 这一行 foo4是箭头函数,找它父级作用域,父级函数已经经过显式绑定,所以打印 person2
  • person1.foo4().call(person2) 这一行 foo4是箭头函数,找它父级作用域,所以打印 person1
  1. 题目4
var name = 'window'
function Person (name) {
  this.name = name
  this.obj = {
    name: 'obj',
    foo1: function () {
      return function () {
        console.log(this.name)
      }
    },
    foo2: function () {
      return () => {
        console.log(this.name)
      }
    }
  }
}
var person1 = new Person('person1')
var person2 = new Person('person2')

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

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

解析:

  • person1.obj.foo1()() 这一行其实是默认绑定,所以打印window
  • person1.obj.foo1.call(person2)() 这一行其实是默认绑定,所以打印window
  • pperson1.obj.foo1().call(person2) 这一行其实是显式绑定,所以打印person2
  • person1.obj.foo2()() 这一行foo2是箭头函数,所以找父级作用域,打印obj
  • person1.obj.foo2.call(person2)() 这一行foo2是箭头函数,所以找父级作用域,父级指向已改变,打印person2
  • person1.obj.foo2().call(person2) 这一行foo2是箭头函数,call不起作用,所以找父级作用域,打印obj