前言
在前面的文章当中,我们说到一个函数执行时,相当于先声明了一个var this = {},然后通过this.xxx,来访问this对象中的元素,其实这个说法是有瑕疵的,在 JavaScript 的世界里,this 就像一位神秘的旅行者------它从不固定在某个地方,而是根据调用场景的不同变换身份。有时它指向对象,有时指向全局,甚至会在严格模式下悄然隐身。无数开发者曾为它辗转反侧,也有无数代码因它的"善变"而暗藏玄机。
你是否也困惑过:为什么同样的函数,换个调用方式 this 就变了?箭头函数里的 this 为何如此特立独行?当 this 与闭包、原型链相遇时,又会碰撞出怎样的火花? 让我们一起揭开 this 的神秘面纱,探索它在不同场景下的变换法则,掌握这把解锁 JavaScript 核心奥秘的钥匙。
引入this原因
- this 是 js 中的一个关键字,他提供了一种更加优雅的方式隐式的传递一个对象的引用,可以让代码更简洁 易于复用,让我用一段例子给你具体展现
this的强大功能
js
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时,我们要频繁在identify()和speek()函数之间传递假设变量为context的引用,显得繁琐而复杂,不易于复用。引入this后,提前透露:fn.call(obj,x,y)为显示绑定,因而
- speek.call(me) 将 speek 的 this 强制绑定到 me
- 在 speek 执行时, this 就是 me
- identify.call(this) 把 identify 的 this 也绑定到了同一个 me
- 所以 identify 内部 this.name 就是 me.name ,即 "tom"
this可以出现在哪
1.全局 this === window
例如
js
console.log(this)
可以看到控制台输出了 window。浏览器全局作用域中的 this指向的就是全局 window 对象。
函数体内
js
function foo(){
console.log(this.a);
}
可以看到函数体内,而函数体内,this用在不同的地方,代指的内容是不一样的,接下来说他的绑定规则
this五大绑定规则
1.默认绑定 ---当函数独立调用时,函数中的 this 指向 window
给段示例:
js
var a = 1
function bar(){
var a = 2
function foo(){
console.log(this.a);
}
foo()
}
bar()
可以看到,代码中函数的调用都是单独调用,所以可以预见,this代指windo全局,从而this.a=1
1.隐式绑定
---当一个函数被一个上下文对象所拥有,且被告该对象调用时,函数中的 this 指向该对象
这就像演员走到哪个剧组,就属于哪个剧组。 演员(函数)本身是独立的,当他被某个剧组(对象)雇佣(作为方法) ,并在该剧组工作(被调用)时 ,他的身份就属于这个剧组( this 指向该对象) 那么我们来看段代码示例
js
function foo(){
console.log(this);
}
var obj = {
a: 1,
foo: foo //引用,相当于foo:function foo(){console.log(this.a);}
}
// foo()//this === window
obj.foo()//this === obj
可以看到foo()hanshu的调用是在"obj"对象的"牵引"下执行的,也就是我们所谓的隐式绑定,从而使this指向obj对象,而foo: foo相当于引用,相当于foo:function foo(){console.log(this.a);},结果当然是输出obj中的1

隐式丢失
--- 当一个函数被多层对象调用时,函数的 this 指向最近的那个对象 看一段示例代码:
js
function foo(){
console.log(this.a);
}
var obj = {
a: 1,
foo: foo //引用,相当于foo:function foo(){console.log(this.a);}
}
var oo = {
a: 2,
foo: obj
}
oo.foo.foo() //this === obj,输出1,就近原则,隐式丢失
调用链可以抽象成

因此,this最后还是只想着obj对象,看 执行结果: 
这就类似于:俄罗斯套娃,看最后! 前面的都是"路",最后一个才是"主人"
显示绑定
显式绑定的核心 : 我说了算! , call / apply :我临时指定你是谁 ,bind :我永远指定你是谁 ,就像电影导演说:"你来演这个角色!" 演员就得是这个角色! 看代码
js
function foo(x,y){
console.log(this.a,x+y);
}
var liu = {
a: 1
}
foo.call(liu,1,2)
var jie = {
a: 2
}
foo.apply(jie,[3,3])
var fu={
a: 3,
}
const bar = foo.bind(fu,1,3)
bar(4)
输出结果:

第一个foo.call(liu,1,2),call将a强制绑定到liu,从而输出a=1,x+y=3,而第二个foo.apply(jie,[3,3]),只是传入引用从()变成【】,过程一样,第三个我们可以看到其等同于fn.bind(obj,x,y)( )== const bar = fn.bind(obj,x,y) bar(x),一样存在先后,1,3先传,从而x+y等于4.
new 绑定
--- 当一个函数被 new 关键字调用时,new的原理会导致函数中的 this 指向新创建的实例对象
new 绑定就像 工厂生产一个新商品 , this 就是这个新商品的"身份牌"! 看代码
js
function Person(){
//var obj = {} 1
//Person.call(obj) 2
this.name = 'liu' //3 obj.name = 'liu'
//obj.__proto__ = Person.prototype //4
//return obj //5
}
const p = new Person()
console.log(p);
具体导致函数中的 this 指向新创建的实例对象的过程如下
1.const p = new Person() [1️⃣] 创建空对象: obj = {} [2️⃣] 绑定 this: Person.call(obj) [3️⃣] 执行代码: this.name = 'liu' (就是 obj.name = 'liu') [4️⃣] 链接原型: obj.proto = Person.prototype [5️⃣] 返回对象: p = obj 最终 p = { name: 'liu' } 这下就能解决前言中的疑惑了。
箭头函数
箭头函数没有this这个概念,写在箭头函数中的 this,也是他外层那个非箭头函数的(总结:先看this在哪里,再看this指向谁)看一段代码
js
function foo(){
var fn = ()=>{
this.a = 2
}
fn()
}
var obj = {
a: 1,
bar: foo
}
obj.bar()
console.log(obj);
过程分析:
可以看到,箭头函数中this指向外层foo(),而foo又因隐式绑定使this最终指向obj。