前言:为什么 this 是 JavaScript 的"灵魂之问"?
在 JavaScript 的学习之路上,this 几乎是一个无法绕开的"灵魂拷问"。无数开发者曾在它面前困惑不解,又曾在理解它之后豁然开朗。有人说,真正理解 this 是 JavaScript 进阶的重要里程碑。
你是否也曾遇到过这样的场景:
- 明明在对象中定义的方法,调用时却找不到
this.name? - 在回调函数中,
this神秘地"消失"了? - 看着箭头函数和普通函数中不同的
this表现,感到一头雾水?
这些问题看似简单,却直指 JavaScript 的核心机制。this 的设计体现了 JavaScript 的灵活性和动态性,但也正是这种灵活性,让初学者感到困惑。
本文将带你深入 this 的世界,从它的存在意义到实际应用,从基础规则到高级技巧,系统地剖析这个 JavaScript 中最重要的概念之一。无论你是正在学习前端的新手,还是希望深入理解 JavaScript 原理的开发者,相信这篇文章都能为你带来新的收获。
前言:为什么 this 是 JavaScript 的"灵魂之问"? 在 JavaScript 的学习之路上,this 几乎是一个无法绕开的"灵魂拷问"。无数开发者曾在它面前困惑不解,又曾在理解它之后豁
为什么要有 this?
在 JavaScript 的世界里,this 就像是一个隐形的"传话筒",this 提供了一个更优雅的方式隐式地传递一个对象引用,可以让代码更简洁易于复用
function identify(context) {
return context.name.toUpperCase()
}
function speek(context) {
var greeting = 'hello, I am ' + identify(context)
console.log(greeting);
}
var me = {
name: 'Tom'
}
speek(me)//不用this并且逻辑复杂情况下context写的到处都是,容易造成参数传导出现错误
下面是改使用this,原由先不做解释,但这两段代码运行结果是一样的
function identify() {
return this.name.toUpperCase()
}
function speek() {
var greeting = 'Hello, I am ' + identify.call(this)
console.log(greeting);
}
var me = {
name: 'Tom'
}
speek.call(me)
接着往下看,带你慢慢理解
this 用在哪?
this是一个代词,用在不同的地方代指不同的值。它主要出现在以下两种作用域中:
- 全局作用域:在全局作用域中,
this指向全局对象(在浏览器中是window,在Node.js中是global)。this===window
console.log(this) //在node上会输出一个{},即代表wiondow的全局
- 函数作用域:在函数内部,
this的指向取决于函数被调用的方式。
this 的绑定规则
默认绑定
当函数被独立调用时,this 默认指向全局对象:
这是独立调用
var a=1
function foo(){
console.log(this.a);
}
foo()//结果为 1
这还是独立调用
var a = 1
function foo() {
console.log(this.a);
}
function bar () {
var a = 2
foo() //虽然在函数里面,但仍然是独立调用
}
bar()
隐式绑定
当函数被某个对象"拥有"并调用时(即非独立调用时或者说被引用且被调用时),this 绑定到该对象:
function foo() {
console.log(this);
}
var a = 1
var obj = {
foo: foo
}
obj.foo()
运行结果
此时你可能会有一个问题:如果在上面那段代码外层再包装一个函数那该怎么判断?那就得提到下一个点了---隐式丢失,看下面
隐式丢失
当一个函数被多层对象调用时,函数的 this 指向最近的那个对象
function foo() {
console.log(this.a);
}
var obj = {
a: 1,
foo: foo //引用
}
var obj2 = {
a: 2,
foo: obj //引用
}
obj2.foo.foo()
此时foo中this指向的是离他最近的obj
显式绑定
显示绑定就是人为的将this绑定到指定的函数上,
使用 call、apply、bind 可以显式地指定 this:
fn.call(obj, x, x) 显示的将fn里面的 this 绑定到obj这个对象上,call 负责帮 fn 接受参数
var obj = {
a: 1
} //在v8眼中人为打造的function都是通过new Function得到的
function foo(x, y) { // new Function() // foo.__proto__ === Function.prototype
console.log(this.a, x + y);
}
foo.call(obj, 1, 2) //将foo中的this指向obj
fn.apply(obj,[x, x]) 显示的将fn里面的 this 绑定到obj这个对象上,call 负责帮 fn 接受参数
var obj = {
a: 1
}
function foo(x, y) {
console.log(this.a, x + y);
}
var arr = [1, 2]
foo.apply(obj, arr)
new 绑定
使用 new 关键字调用构造函数时,this 会绑定到新创建的对象:
构造函数中 return 的特殊情况
当构造函数中存在 return ,并且 return 的是一个引用类型的数据,则 new 的返回失效
function Person() {
this.name = '冯总'
return {a: 1}
}
let p = new Person()
console.log(p);
箭头函数中的 this
箭头函数是 ES6 的语法糖,它没有自己的 this,而是继承外层作用域的 this:
function foo(){
var bar = () => {
console.log(this.a)
}
}
这里this指向的是foo而非bar
this 绑定优先级总结
- new 绑定 - 最高优先级
- 显式绑定(call、apply、bind)
- 隐式绑定(对象方法调用)
- 默认绑定(独立函数调用)