一篇文章解决你的this指向问题

一、this指向分析

  1. 函数在调用时,JavaScript会默认给this绑定一个值
  2. this的绑定和**定义的位置(编写的位置)**没有关系
  3. this的绑定和调用方式和调用的位置有关系
  4. this是在运行时绑定的

二、this绑定规则

2.1 默认绑定

  1. 普通的函数被独立调用
javascript 复制代码
function foo() {
	console.log('foo函数', this)
}
foo()
  1. 函数定义在对象中,但是独立调用
javascript 复制代码
const obj = {
	name: 'aklry',
	bar: function() {
		console.log('foo函数', this)
	}
}
const baz = obj.bar
baz()
  1. 高阶函数
javascript 复制代码
function test(fn) {
	fn()
}
test(obj.bar)

以上三种情况均属于默认绑定,在非严格模式 下this指向window严格模式 下指向undefined。

2.2 隐式绑定

隐式绑定的this指向发起者,谁发起的函数执行,this就指向谁,与定义的位置无关。

javascript 复制代码
function foo() {
	console.log('foo函数', this)
}
const obj = {
	bar: foo
}
obj.bar()

根据以上代码,foo函数虽然在全局声明,却由obj对象发起调用,因此this隐式绑定为obj对象。

2.3 new绑定

总所周知,ES5的函数拥有两种执行方式,一种是直接执行,另外一种是通过new 关键字来执行,那么通过new关键字执行函数的过程中发生了些什么呢?

  1. 创建一个空的对象
  2. 将空的对象赋值给this
  3. 执行函数体中的代码
  4. 将新的对象默认返回

因此通过new关键字来执行函数,this指向执行函数过程中创建的这个新的对象。

2.4 显式绑定

显式绑定通过函数执行的方式强制将this绑定给一个对象(非严格模式下),因此,先来介绍一下能够改变this指向的函数

  1. apply/call

apply和call都能够改变this指向,唯一的不同是两者的传参方式不同。

javascript 复制代码
function foo(name, height, age) {
    console.log('foo函数', this)
    console.log('打印参数', name, height, age)
}
// 一般调用
foo('aklry', 1.70, 20)
// apply调用
foo.apply(123, [1.70, 20])
// call调用
foo.call(123, 1.70, 20)
  1. bind

bind 的效果与apply/call 类似,都能够改变this指向,不同点就是bind返回了一个新函数,这个新函数的指向就是我们更改之后的this指向。

javascript 复制代码
function foo(name, height, age) {
    console.log('foo函数', this)
    console.log('打印参数', name, height, age)
}
const baz = foo.bind(123, 'aklry', 1.70)
baz(20)

bind 使用时的传参方式与call类似,如果我们的参数没有传完整,并且在执行新函数时传入参数,那么在执行新函数时传入的参数会默认赋值给剩余的参数。

除此之外,在非严格模式下apply/call、apply绑定的this指向是一个对象,如果传入基本数据类型,则浏览器会将其转换为包装类对象。

2.5 this绑定规则优先级

  1. 显式绑定优先级高于隐式绑定
javascript 复制代码
function foo() {
	console.log('foo函数', this)
}
let obj = { name: 'aklry', foo: foo }
obj.foo.apply(123) // Number(123)

const bar = foo.bind('aaa')
obj = {
	name: 'aklry',
	baz: bar
}
obj.baz() // String('aaa')
  1. new绑定优先级高于隐式绑定
javascript 复制代码
obj = {
	name: 'aklry',
	foo: function() {
		console.log(this)
	}
}
new obj.foo() // foo {}
  1. new绑定和显式绑定

(1)首先new不能和call/apply一起使用,new高于bind

javascript 复制代码
const bindFn = foo.bind('aaa')
new bindFn() // foo {}

(2)bind高于apply/call

javascript 复制代码
const bindFn = foo.bind('aaa')
bindFn.apply(123) // String('aaa')

三、this绑定规则之外的情况

  1. 显式绑定null/undefined,使用默认绑定规则
  2. 间接函数引用
javascript 复制代码
const obj1 = {
	name: 'obj1',
	foo: function() {
		console.log('obj1', this)
	}
}
const obj2 = { name: 'obj2' }
(obj2.foo = obj1.foo)() // 左侧赋值表达式返回foo这个函数,因此这种写法是独立函数调用 -> 默认绑定
  1. 箭头函数

(1)普通函数中默认有this标识符,而箭头函数默认不绑定this

(2)箭头函数没有绑定this,因此使用箭头函数时,如果使用this,那么会逐层作用域查找this

javascript 复制代码
const obj = {
	name: 'obj',
	foo: function() {
		const bar = () => {
			console.log('bar', this) // obj
		}
		return bar
	}
}
const baz = obj.foo()
baz()

根据以上代码,obj.foo()执行返回bar这个箭头函数,由于箭头函数没有this,因此会逐层作用域查找,箭头函数的上层作用域为foo 函数,而foo 函数的this取决于调用者,根据代码,foo 函数由obj 对象调用,因此this指向obj 对象,所以箭头函数捕获上层作用的this即obj对象。

四、this面试题

javascript 复制代码
var name = "window";
var person = {
  name: "person",
  sayName: function () {
    console.log(this.name);
  }
};
function sayName() {
  var sss = person.sayName;
  sss(); // 独立函数调用 -> 默认绑定 -> this(window) -> window
  person.sayName(); // 隐式绑定 -> this(person) -> person
  (person.sayName)(); // 隐式绑定 -> this(person) -> person
  (b = person.sayName)(); // 间接函数引用 -> 默认绑定 -> this(window) -> window
}
sayName();
javascript 复制代码
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(); // 隐式绑定 -> this(person) -> person1
person1.foo1.call(person2); // 隐式绑定 -> this(person) -> person2

person1.foo2(); // 箭头函数 -> 作用域查找 -> this(window) -> window
person1.foo2.call(person2); // 箭头函数 -> 作用域查找 -> this(window) -> window

person1.foo3()(); // 独立函数调用 -> 默认绑定 -> this(window) -> window
person1.foo3.call(person2)(); // 独立函数调用 -> 默认绑定 -> this(window) -> window
person1.foo3().call(person2); // 显式绑定 -> this(person2) -> person2

person1.foo4()(); // 箭头函数 -> 作用域查找 -> this(person1) -> person1
person1.foo4.call(person2)(); // 箭头函数 -> 作用域查找 -> 显式绑定 -> this(person2) -> person1
person1.foo4().call(person2); // 箭头函数 -> 作用域查找 -> this(person1) -> person1
javascript 复制代码
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
person1.foo1.call(person2) // person2

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

person1.foo3()() // window
person1.foo3.call(person2)() // window
person1.foo3().call(person2) // person2

person1.foo4()() // person1
person1.foo4.call(person2)() // person2
person1.foo4().call(person2) // person1
javascript 复制代码
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()() // window
person1.obj.foo1.call(person2)() // window
person1.obj.foo1().call(person2) // person2

person1.obj.foo2()() // obj
person1.obj.foo2.call(person2)() // person2
person1.obj.foo2().call(person2) // obj
相关推荐
irises15 分钟前
tabby-vscode代码补全的一些阅读笔记
前端·javascript
千野竹之卫19 分钟前
2025最新云渲染网渲100渲染农场使用方法,渲染100邀请码1a12
开发语言·前端·javascript·数码相机·3d·3dsmax
__不想说话__26 分钟前
面试官问我React Router原理,我掏出了平底锅…
前端·javascript·react.js
yzzzz28 分钟前
面试官:聊聊数组扁平化
javascript·面试
头发秃头小宝贝33 分钟前
JavaScript 高级之手写Promise
前端·javascript·面试
还是鼠鼠40 分钟前
Node.js Express 处理静态资源
前端·javascript·vscode·node.js·json·express
开水好喝44 分钟前
项目如何安装本地tgz包并配置局部registry
javascript
智能编织者1 小时前
用 Pinia 点燃 Vue 3 应用:状态管理革新之旅
前端·javascript·vue.js
微臣愚钝1 小时前
【12】Ajax的原理和解析
前端·javascript·ajax
徐小夕@趣谈前端1 小时前
从零到一开发电子病历编辑器(源码+教程)
前端·javascript·vue.js·编辑器·ecmascript