this是一个代词,代指一个对象
为什么要有 this ?
- 以下代码展示了不用
this时需要多层函数嵌套时传参的写法,可以发现我们这个时候需要手动显式传入上下文对象,设置形参实参才能让函数正确读取我们需要的东西
// 需要给 speak 和 identify 显式传入一个上下文对象
function identify(context) {
return context.name.toUpperCase()
}
function speak(context) {
var greeting = 'hello, I am ' + identify(context)
console.log(greeting);
}
var me = {
name: 'Tom'
}
speak(me)
- 使用
this后的代码可以长这样:可以想象,在传多个参数时代码可以变得多么简洁易读
function identify () {
return this.name.toUpperCase()
}
function speak () {
var greeting = 'hello, I am ' + identify.call(this)
console.log(greeting)
}
var me = {
name: 'FWB',
}
speak.call(me)
很显然,this提供了一种更优雅的方式来隐式的传递一个对象引用,可以让代码更加简洁易于复用
this 可以用在哪里?
- 只有作用域中才可以写
this- 全局作用域:通常在浏览器中,
this指向window - 函数作用域:根据函数调用方式决定
this指向
- 全局作用域:通常在浏览器中,
this的绑定规则
1.默认绑定
- 当函数被独立调用时,函数的
this指向window
独立调用是指函数直接被调用,前面没有"前缀"
var a = 3
function foo () {
var a = 2
function bar () {
var a = 1
console.log(this.a) // 浏览器环境下输出3,node中为 undefined
}
bar() // 独立调用
}
foo() // 独立调用
2.隐式绑定
- 当函数引用有上下文对象 且被该对象调用时,隐式绑定会把函数调用中的
this绑定到这个上下文对象
绕来绕去其实意思就是和独立调用相反,有"前缀"的调用,就是上述概念
var a = 1
function foo () {
console.log(this.a) // obj.a
}
var obj = {
a: 2,
foo: foo,
}
var obj2 = {
a: 3,
obj: obj,
}
obj2.obj.foo() // this 指向obj
i. 小贴士
隐式绑定时采取 "就近原则" ,当一个函数被多层对象调用时,this指向最近的那一层对象
ii. 隐式丢失
var a = '全局变量';
var obj = {
a: '对象属性',
foo: function() {
console.log(this.a);
}
};
var bar = obj.foo; // 将 obj.foo 赋值给 bar,此时 bar 只是一个普通函数引用
bar(); // 输出 '全局变量',因为 bar 是独立调用,this 发生隐式丢失,指向 window
setTimeout(obj.foo, 100); // 输出 '全局变量',setTimeout 的回调函数也是独立调用
3.显式绑定
-
fn.call(obj, x, x, ...)显式地将
fn里面的this绑定到obj上,call负责帮fn接受参数 -
fn.apply(obj, [x, x, ... ])显式地将
fn里面的this绑定到obj上,apply负责帮fn接受参数,参数必须是数据组形式 -
fn.bind(obj, x, x, ...)(x, x, ...)显式地将
fn里面的this绑定到obj上,bind会返回一个新的函数,bind和新函数都可以负责帮fn接受参数,参数零散的传入
function foo (x, y) {
console.log(this.a, x + y)
}
var obj = {
a: 1,
}
// foo.call(obj, 1, 2)
// foo.apply(obj, [1, 2])
const bar = foo.bind(obj, 2, 3) // 返回的是函数,需要用bar接收
// 当bind中参数已经完全接收时,bar()中的参数就不会被接收
bar(4)
4.new绑定
- new 的原理会导致函数的
this指向实例对象
function Person() {
// var obj = {}
// Person.call(obj)
this.name = 'FWB'
this.age = 20
console.log(this); // p1
// obj.__proto__ = Person.prototype
// return obj
}
const p1 = new Person()
箭头函数
箭头函数会捕获其被定义时的上下文
this值
箭头函数中没有this这个概念,写在了箭头函数中的this,也是它外层那个非箭头函数的this
案例1
function a() {
console.log(this);
let b = function() {
let c = () => {
let d = () => {
console.log(this);
}
d()
}
c()
}
b()
}
a()
其实就是window或者global,不管有多少层,这些函数的调用都是独立的,所以this指向全局
案例2
var a = 1
var obj = {
a: 2,
bar: function () {
const baz = () => {
console.log(this.a) // 2
}
baz()
},
}
obj.bar()
- 当执行
obj.bar()时,由于是作为对象方法调用,bar函数内部的this指向obj对象 bar的箭头函数baz没有自己的this绑定,而是继承其定义时所在环境(bar函数)的this- 因此,箭头函数
baz中的this.a实际上是访问obj.a,值为2
总结
- 优先使用显式绑定:当需要明确
this指向时,使用call/apply/bind - 方法调用注意上下文:确保对象方法被正确调用,避免隐式丢失
- 箭头函数谨慎使用:在需要动态
this的场景避免箭头函数 - 构造函数使用
new:确保构造函数正确实例化对象
理解this是掌握JavaScript核心的关键,它让我们的代码更加灵活高效。通过熟练掌握这四大规则,你就能在复杂场景中游刃有余地使用this了!