前言:为什么this如此令人困惑?
在JavaScript开发中,this 关键字无疑是最令人困惑的概念之一。很多开发者都曾在各种面试中被问及this的指向问题,也在实际项目中因为this的指向不明而遭遇bug。
this的本质是一个代词,它像一个灵活的指针,在不同的执行环境中指向不同的对象。理解this的关键在于:它的指向是在函数被调用时确定的,而不是在函数定义时。
本文将系统梳理this的四大绑定规则和箭头函数的特殊行为,帮你彻底攻克这个难点。
1.为什么要有 this ?
this 提供了一个更优雅的方式隐式地传递一个对象引用,可以让代码更简洁易于复用
来看下面代码,很显然该代码就是输出打印一个名字首字母大写的一句话,如果我功能复杂一点的话,是不是context这个参数会到处传,这种情况就叫做上下文传递过于复杂了。
function identify(context){
return context.name.toUpperCase()
}
function speak(context){
var greeting='hello,I am'+identify(context)
console.log(greeting);
}
var me={
name:'Tom'
}
speek(me)
这时候就可以把这段代码改成用this的形式就不用参数了,这时候输出是一模一样的。此时我们先不解释这段代码怎么来的,你跟着我看完接下来的内容你就知道了怎么一回事。
function identify(){
return this.name.toUpperCase()
}
function speek(){
var greeting='Hello,I am'+identify.call(this)
}
var me={
name:'Tom'
}
speek.call(me)
2.this 通常用在哪?
一般可以产生作用域的都可以用this,一般只可以在全局和函数体内,一般在函数体作用域内,this就用在这两个地方
- this是一个代词,用在不同的地方代指不同的值,不看函数调用在哪,就看函数怎么调用。
我直接node在全局打印this,得到一个空对象{}叫做gobal
console.log(this);
在浏览器打印时就是window对象
所以全局作用域 this==window所以主要还是了解函数作用域这一方面来看this的绑定规则
3.this的绑定规则
3.1 默认绑定(独立函数调用)
规则:当函数被独立调用时,函数内部的this指向全局对象(浏览器中为window,Node.js中为global)。现在用代码来看是什么意思呢?这个this在函数体内,但是它指向的是谁呢?
function foo(){
console.log(this);
}
来看在浏览器下打印的结果,很显然是不是就是代指window
3.2 隐式绑定
规则:当函数被一个上下文对象引用且被该对象调用,函数中的this会绑定到这个上下文对象上。
看下面这段js代码显然它是不是被obj调用了,虽然它在foo这个函数体上。
function foo(){
console.log(this.a);
}
var a=1
var obj={
foo:foo
}
obj.foo()
所以此时的this指向obj,得到的结果就是undefined
隐式丢失的坑
这是面试中经常考察的重点!当函数被多层对象调用时,this指向直接调用该方法的那个对象。
以这段代码为例,foo是不是被多次调用。
function foo(){
console.log(this.a);
}
var a=1
var obj={
a:1,
foo:foo
}
var obj2={
a:2,
foo:obj
}
obj2.foo.foo()
根据上面的规则是不是foo里的this此时指向的就是obj啊,这个东西不就好比皇帝可以命令将军,而将军命令士兵,士兵是不是直接听将军指挥,而不是皇帝。
3.3 显式绑定(强制绑定)
当我们想手动控制this的指向时,可以使用显式绑定。
- fn.call(obj,x,x)显示地将fn里面的this绑定到obj这个对象上,call负责帮fn接受实参数,像下面这样
var obj={
a:1
}
function foo(x,y){
console.log(this.a,x+y);
}
foo.call(obj,1,2)
- fn.apply(obj,[x,x])显示地将fn里面的this绑定到obj这个对象上,call以
数组的方式接受实参数
var obj={
a:1
}
function foo(x,y){
console.log(this.a,x+y);
}
foo.apply(obj,[1,2])
-bind不会立即调用函数,而是返回一个绑定了指定this的新函数。
var obj={
a:1
}
function foo(x,y){
console.log(this.a,x+y);
}
const bar=foo.bind(obj,2,3)
bar()
4. new绑定(构造函数调用)
规则:当使用new关键字调用构造函数时,this会绑定到新创建的实例对象上。
function Person() {
this.name = '冯总'
}
let p = new Person()
console.log(p);//输出Person { name: '冯总' }
关于return的注意事项:
如果构造函数中显式返回一个引用类型(对象、数组、函数),则new操作符会返回这个对象,而不是新创建的实例。
function Person() {
this.name = '冯总'
return {a: 1}
}
let p = new Person()
console.log(p);//输出{ a: 1 }
5.箭头函数
箭头函数没有自己的this
箭头函数是ES6的重要特性,它没有自己的this,它的this继承自外层作用域。
看下面这里bar是不是指向foo,虽然foo被obj调用但是还是bar是箭头函数,所以最后输出a就是2
function foo() {
// this
var bar = () => {
this.a = 2
}
bar()
}
var obj = {
a: 1,
baz: foo
}
obj.baz()
console.log();
结语
理解this的关键在于记住:this的指向取决于函数的调用方式,而不是定义位置。掌握了四大绑定规则和箭头函数的特性,你就能在复杂的代码中游刃有余地处理this指向问题。
希望本文能帮你彻底搞懂JavaScript中的this机制,如果有任何疑问或想分享的经验,欢迎在评论区交流!