js函数中this是面试官最喜欢考察的,整理一下常用的this指向的知识点。

一、全局上下文

非严格模式和严格模式中this都是指向顶层对象

1
2
this.myname = 'xyf'
console.log(this) //Window {myname: 'xyf', …}

二、函数上下文

普通函数

在非严格模式下,普通函数的this指向window

{.line-numbers}
1
2
3
4
5
6
var myname = 'xyf'
var sayName = function(){
console.log(this === window) // true
console.log(this.myname) // 'xyf'
}
sayName()

这里的sayName()相当于调用了window.sayName()。在严格模式下,普通函数的this指向undefined

1
2
3
4
5
6
7
'use strict'
var myname = 'xyf'
var sayName = function(){
console.log(this === window) // false
console.log(this.myname) // 报错,因为this是undefined
}
sayName()

在ES5中,var会给顶层对象中(浏览器是window)添加属性,而使用let则不会。

1
2
3
4
5
6
let myname = 'xyf'
let sayName = function(){
console.log(this === window) // true
console.log(this.myname) // undefined
}
sayName()

对象中的函数

对象中的函数this指向对象本身

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var myname = 'outside name'
var sayName = function(){
console.log(this.myname)
}

var person = {
myname: 'inside name',
sayName: sayName,
other: {
myname: 'other name',
sayName: sayName,
}
}

person.sayName() // 'inside name'
person.other.sayName() // 'other name'

将对象中的函数赋值成变量,这样又变成普通函数,this指向顶层对象。

1
2
var newSayName = person.sayName;
newSayName() // 'outside name'

所以,通过上面我们可以看出来,函数的定义位置不影响其this指向,this指向只和调用函数的对象有关

call、apply、bind改变this指向

通过call、apply、bind可以改变函数this的指向

语法:

1
2
fun.call(thisArg, arg1, arg2, ...)
fun.apply(thisArg, [arg1, arg2, ...])

thisArg是fun函数在运行时指定的this的值。但是指定的this值不一定就是该函数执行时真正的this值。如果这个函数处于非严格模式下,指定为null和undefined的this值会自动指向全局对象(windows中就是window对象);指定为原始值(数字,字符串,布尔值)的this会指向该原始值的自动包装对象;

1
2
3
4
5
6
7
8
9
10
11
12
var sayName = function(name1,name2){
console.log(this, name1, name2)
}

// Number {1} 'a' 'b'
sayName.call(1, 'a', 'b')

// Window { … } 'a' 'b'
sayName.call(null, 'a', 'b')

// Window { … } 'a' 'b'
sayName.call(undefined, 'a', 'b')

apply和call类似。只是参数不一样。它的参数是数组(或者类数组)。

1
sayName.apply(1, ['a', 'b'])

严格模式下,指向null和undefined的this值还是指向原来的。

1
2
3
4
5
6
7
var sayName = function(name1,name2){
'use strict'
console.log(this, name1, name2)
}

sayName.call(null, 'a', 'b')
// null 'a' 'b'

bind和call和apply的调用方式相同,第一个值也是修改this的指向,只不过bind返回一个新的函数。

构造函数调用模式

1
2
3
4
5
6
7
function Dog(name){
this.name = name;
console.log(this)
}

var puppy = new Dog('apple')
// Dog {name: "apple"}

由此可知,new操作符调用时,this指向生成的新对象。

原型链中的调用模式

1
2
3
4
5
6
7
8
9
10
function Dog(name){
this.name = name;
}

var puppy = new Dog('apple')
Dog.prototype.bark = function(){
console.log(this, this.name)
}
puppy.bark()
//Dog {name: "apple"} "apple"

箭头函数调用模式

先看箭头函数和普通函数的重要区别:

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var name = 'out'
var people = {
name: 'xyf',
sayName: function(){
var arrowFun = () => {
console.log(this.name)
}
arrowFun()
},
arrowSayName: () => {
console.log(this.name)
}
}
people.sayName()
people.arrowSayName()

总结

如果要判断一个运行中函数的 this 绑定, 就需要找到这个函数的直接调用位置。

本文地址: http://xieyufei.com/2019/03/06/This-Point.html