“我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第1篇文章,点击查看活动详情”
this的指向问题
面向对象语言中 this 表示当前对象的一个引用。
但在 JavaScript 中 this 不是固定不变的,它会随着执行环境的改变而改变。
讨论this的原理,一定离不开this的环境。
判断this的诀窍:
1. this的指向在函数创建的时候是决定不了的,在调用的时候才能决定,谁调用的就指向谁
2. this永远指向最后调用它的对象
3. apply、call、bind会改变this的指向
4. new操作符会改变函数this的指向问题
this的绑定规则(优先级从低到高)
- 默认绑定
- 隐式绑定
- 显示绑定
- new绑定
默认绑定
默认绑定最大的特点就是独立函数的调用fn()
情况一: 普通函数独立调用
this默认绑定全局对象Window,而严格模式下,全局对象无法使用默认的绑定,无法直接使用this,所以在输出的时候,直接报错了
// 非严格模式下
var name = 'pig'
function fn() {
name: 'zhuzhu',
console.log(this.name)
}
fn() // pig
// 严格模式下
'use strict'
var name = 'pig'
function fn() {
name: 'zhuzhu',
console.log(this.name)
}
fn() // Uncaught TypeError: Cannot read properties of undefined (reading 'name')
情况二: 函数定义在对象中,但被独立调用
var name = 'pig'
const a = {
name: 'zhuzhu',
fn: function() {
console.log(this.name)
}
}
var b = a.fn
b() // pig
情况三: 函数在另一个函数中被调用
var name = 'pig'
const a = {
name: 'zhuzhu',
fn: function() {
console.log(this.name)
}
}
function b(params) {
params()
}
b(a.fn) // pig
隐式绑定
隐式绑定的特点是函数被某个对象调用obj.fn(),此时的this指向该对象
案例一
var name = 'pig'
function fn() {
console.log(this.name)
}
const a = {
name: 'zhuzhu',
fn: fn
}
fn() // pig
a.fn() // zhuzhu
上述的案例一也说明了隐式绑定优先于默认绑定
将案例一做一个简单的改变,我们来看看案例二
var name = 'pig'
function fn() {
console.log(this.name)
}
const a = {
fn: fn
}
a.fn() // undefined
让我们把案例一和案例二结合起来看,不难发现当函数引用有上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象
可以将案例二和显示绑定的情况二好好再了解一下,相信你很快就能理解
案例三
var name = 'pig'
function fn() {
console.log(this.name)
}
const a = {
name: 'zhuzhu',
fn: fn
}
const b = {
name: 'cute',
a: a
}
b.a.fn() // zhuzhu
将案例三和案例一好好对比一下,你会发现在调用fn的引用链上,this永远指向最后一个引用它的对象
显示绑定
使用apply、call、bind实现显示绑定
var name = 'pig';
function fn() {
console.log(this.name);
}
const obj = {
name: 'zhuzhu',
fn
};
obj.fn(); // zhuzhu
obj.fn.apply({ name: 'apply优先级高于隐式绑定'}) // apply优先级高于隐式绑定
obj.fn.call({ name: 'call优先级高于隐式绑定'}) // call优先级高于隐式绑定
obj.fn.bind({ name: 'bind优先级高于隐式绑定'}) // 返回对象 fn
obj.fn.bind({ name: 'bind优先级高于隐式绑定'})() // bind优先级高于隐式绑定
注意: 第一个bind和第二个bind打印结果的不同,这是因为bind()方法创建一个新的函数,所以返回结果也是一个函数;而call和apply返回的是一个新的对象
new绑定
通过new关键字调用一个函数或构造器,这个时候this是在调用这个构造器时创建出来的对象
// 函数
function fn(params) {
this.name = params
}
const a = new fn('pig')
console.log(a.name) // pig
// 构造器
class Person {
constructor() {
this.name = "zhuzhu";
}
}
const b = new Person(); // Person类中的this指向对象b
console.log(b.name); // zhuzhu
new绑定优先于隐式绑定
const a = {
fn: function(name) {
this.name = name
console.log(this.name)
}
}
a.fn('pig') // pig
const b = new a.fn('zhuzhu') // zhuzhu
new绑定优先于显示绑定
new绑定优先于显示绑定中的bind,但不能与call/apply一起使用,原因是new的对象需要是函数或构造器,而call/apply返回的是对象
function fn() {
console.log(this)
}
const a = fn.bind({ name: 'bind'})
a() // {name: 'bind'}
new a() // fn {}
const b = fn.apply({ name: 'apply'}) // {name: 'apply'}
new b; // Uncaught TypeError:b is not a constructor
const c = fn.call({ name: 'call'}) // {name: 'call'}
new c; // Uncaught TypeError:c is not a constructor
/**
当尝试使用对象或变量作为构造函数
但该对象或变量不是构造函数时
会发生 JavaScript 异常“不是构造函数”。
*/
优先级规律总结:
new > bind > call/apply > 隐式绑定 > 默认绑定
箭头函数的this指向
箭头函数没有this指向,它的this是根据外层(函数或者全局)作用域决定的,不适用上面4条规则
箭头函数表达式更适用于那些本来需要匿名函数的地方,并且它不能用作构造函数,不能使用new调用
如果在箭头函数中使用this,this关键字将指向箭头函数定义位置中的this
箭头函数内部指向通过作用域链查找,一般是指向父级的this,没有父级会一直往上找
使用箭头函数的注意事项:
- 使用箭头函数,函数内部没有arguments,this指向window
const add = (a,b) => {
console.log(arguments) // arguments is not defined
return a+b;
}
console.log(add(1,3)) // 4
- 没有this指向,箭头函数不能使用new关键字实例化对象,function函数也是一个对象,但是箭头函数不是一个对象,它其实是语法糖
改变this的指向
- 使用箭头函数
- 使用bind、call、apply
- new实例化一个对象
参考
克里斯蒂亚L juejin.cn/post/713608…