面试(四)之this指向

95 阅读3分钟

摘录于面试官问:JS的this指向

函数的this在调用时绑定的,完全取决于函数的调用位置(也就是函数的调用方法)。

全局上下文

非严格模式和严格模式中this都是指向顶层对象(浏览器中是window)

this === window // true
'use strict'
this === window 
this.name = '若川'
console.log(this.name) // 若川

函数上下文

1. 普通函数调用模式

其实就是默认绑定。 非严格模式下,指向window;严格模式下,指向undefined或null。

// 非严格模式 var创建变量
var name = 'window'
var doSth = function() {
    console.log(this.name)
}
doSth() // window

// 非严格模式 let创建变量
let name = 'window'
let doSth = function() {
    console.log(this === window)
    console.log(this.name)
}
doSth2() // true, undefined
// 严格模式
'use strict'
var name = 'window'
var doSth = function() {
    console.log(typeof this === 'undefined')
    console.log(this.name)
}
doSth() // true;报错,因为this是undefined

2.对象中的函数调用模式

var name = 'window'
var doSth = function() {
    console.log(this.name)
}
var student = {
    name: '若川',
    doSth,
    other: {
        name: 'other',
        doSth
    }
}
student.doSth() // '若川'
student.other.doSth() // 'other'

// 用call类比分别⬇️
student.doSth.call(student)
student.other.doSth.call(student.other)

当把对象中的函数赋值成一个变量:

var studentDoSth = student.doSth
studentDoSth() // 'window'
// 用call类比⬇️
studentDoSth.call(undefined)

3.构造函数调用模式

function Student(name) {
    this.name = name
    console.log(this) // { name: '若川' }
    // 相当于返回了
    // return this
}

使用new操作符,会执行以下⬇️

-   创建了一个全新的对象。

-   这个对象会被执行`[[Prototype]]`(也就是`__proto__`)链接。

-   生成的新对象会绑定到函数调用的`this`。

-   通过`new`创建的每个对象将最终被`[[Prototype]]`链接到这个函数的`prototype`对象上。

-   如果函数没有返回对象类型`Object`(包含`Functoin`, `Array`, `Date`, `RegExg`, `Error`),那么`new`表达式中的函数调用会自动返回这个新的对象。

new操作符调用时,this指向生成的新对象
new调用时的返回值,如果没有显式返回对象或者函数,才是返回生成的新对象

function Student(name) {
    this.name = name
    // return function f(){}
    // return {}
}
var result = new Student('若川')
console.log(result)  // { name: '若川' }
// 如果返回函数f,则result是函数f;如果是对象{},则result是对象{}

4.原型链中的调用模式

function Student(name) {
    this.name = name
}
var s1 = new Student('若川')
Student.prototype.doSth = function() {
    console.log(this.name)
}
s1.doSth() // '若川'

5.箭头函数调用模式

箭头函数与普通函数的区别:

(1) 没有自己的this、super、arguments和new.target绑定
(2) 不能使用new调用
(3) 没有原型对象
(4) 不可以改变this的绑定
(5) 形参名称不能重复

箭头函数中没有this绑定,必须通过查找作用链来决定其值。 如果箭头函数被非箭头函数包含,则this绑定的是最近一层非箭头函数的this,否则this的值被设置为全局对象。

var name = 'window'
var student = {
    name: '若川',
    doSth: function() {
        // var self = this
        var arrowDoSth = () => {
            // console.log(self.name)
            console.log(this.name)
        }
        arrowDoSth()
    },
    arrowDoSth2: () => {
        console.log(this.name)
    }
}
student.doSth() // '若川'
student.arrowDoSth2() // 'window'

如果没有普通函数,则是全剧对象(浏览器则是window)。
无法通过call、apply、bind绑定缓存箭头函数的this(它自身没有this)。

other

优先级

var name = 'window'
var person = {
    name: 'person'
}
var doSth = function() {
    console.log(this.name)
    return function() {
        console.log('return:', this.name)
    }
}
var Student = {
    name: '若川',
    doSth
}
// 普通函数调用
doSth() // window
// 对象上的函数调用
Student.doSth() // '若川'
// call、apply调用
Student.doSth.call(person) //'person'
new Student.doSth.call(person)

new 调用 > call、apply、bind调用 > 对象上的函数调用 > 普通函数调用