魔术师this的奇幻之旅:从默认绑定到主动操控
前言
想象一下,this
不是枯燥的编程概念,而是一位戴着面具的神秘嘉宾,在不同的派对(函数调用场景)中扮演着多重角色。它时而化身为王国的统治者(全局对象),时而变成某个家庭的一员(对象方法),甚至还能奇迹般地自我复制(构造函数)。接下来,让我们跟随这位善变的魔术师,展开一场奇妙的探索之旅吧!
第一幕:无名英雄——全局舞台(默认绑定)
在JavaScript的广阔天地里,this
的默认身份是那个默默无闻却又无处不在的全球守护者。在浏览器的王国,它被称为window
,而在Node.js的地下世界,则化身global
。当你在一片混沌中(全局作用域)呼唤this
,它便露出真身:
console.log(this); // 在浏览器中输出window,在Node.js中输出global
在严格模式('use strict';
)下,全局作用域中的this
会是undefined
:
'use strict';
console.log(this); // 输出undefined
在函数中无论嵌套多少层,this
都指向全局:
var b = 2
function foo() {
var b = 1
function bar() {
baz()
}
function baz() {
console.log(this.b)
}
bar()
}
foo() //2
总而言之,在默认绑定下,函数在哪个词法作用域里生效,this就指向哪里。
第二幕:家的温暖——对象方法的归属(隐式绑定)
当this
被邀请到某个对象的家中做客(作为对象的一个方法被调用),this
不再漂泊,它找到了自己的根:
function foo() {
console.log(this.a)
}
var obj = {
a: 2,
foo: foo //读到函数体(引用)
}
obj.foo() //2
稍微修改一下代码:
function foo() {
console.log(this.a)
}
var obj = {
a: 2,
foo: foo()
}
obj.foo // undefined
为什么变成了undefined
呢?需要注意的是,函数foo
只是在obj
里被调用了,他并没有被对象所拥有,所以可以得出结论:
在隐式绑定中,当函数被一个对象所拥有,再调用时,此时this
才会指向该对象。
第三幕:迷失的向导——隐式丢失
然而,当this
的家谱变得复杂,比如在链式调用或赋值操作中,它可能会失去方向,指向最后一个触碰它的对象,仿佛在众多家庭中迷失了自己:
function foo() {
console.log(this.a)
}
var obj = {
a: 2,
foo: foo
}
var obj2 = {
a: 3,
obj: obj
}
obj2.obj.foo() //2
由此可见,当函数被多个对象链式调用时,this
指向最终引用函数的对象。
第三幕:自我复制术——构造函数的奇迹(new 绑定)
如果说this
还有什么隐藏技能,那一定是它的“分身术”了。当你在它的名字前念出“new
”的咒语,它就会摇身一变,创造出一个全新的自己,也就是对象实例:
function foo(name) {
this.name = name;
console.log("I am " + this.name);
}
const Kkk = new foo("kkk"); // "I am kkk"
所以,使用new
关键字调用函数(即构造函数)时,this
指向新创建的对象实例。
第四幕:箭头函数——不变的初心
箭头函数,就像是那些坚守本心,不受外界影响的角色。无论你如何变换场景,它始终坚持自我,它的this
值永远继承自外层上下文,不会在函数内部发生改变:
var obj = {
name: 'TOM',
show: function () {
// console.log(this)
var bar = () => {
console.log(this.name)// 箭头函数外部的非箭头函数show的this
}
bar()
}
}
obj.show() //TOM
箭头函数没有this
这个概念,写在箭头函数中的this
也是他外层函数的this
。
尾声:掌握主动权——call, apply, bind
最后,如果你想要在JavaScript的剧场里彻底掌握this
的命运,.call()
, .apply()
, .bind()
这三个道具绝对不能少。它们能让你在任何时候,任何地点,指定this
想要扮演的角色:
1. call()
call()
方法允许你直接调用一个函数,并且指定 this
的值以及传递给函数的参数。参数列表是以逗号分隔的形式传递的:
function foo(n, m) {
console.log(this.a, n, m)
}
var obj = {
a: 2
}
// call 强行把this绑定在obj上
//第一个参数:你想设置为this的值
//后续参数:传递给被调用函数的参数,按顺序列出
foo.call(obj, 20, 30) // 2 20 30
2. apply()
apply()
方法与 call()
类似,也是改变函数内部 this
的值,并传入参数,但不同的是,apply()
接收两个参数:一个是 this
的值,另一个是一个数组或类数组对象,其中的数组元素将作为单独的参数传递给被调用的函数:
function foo(n, m) {
console.log(this.a, n, m)
}
var obj = {
a: 2
}
//第一个参数:你想设置为this的值
//第二个参数:一个数组或类数组对象,作为参数传入函数
foo.apply(obj, [20, 30]) // 2 20 30
3. bind()
.bind()
方法创建一个新的函数,该函数的this
值会被永久绑定到.bind()
的第一个参数。与前两者不同,.bind()
不会立即调用函数,而是返回一个已绑定上下文的新函数供后续调用:
function foo(n, m) {
console.log(this.a, n, m)
}
var obj = {
a: 2
}
// 会返回一个函数体,接受的参数给bind和函数体都可以
var bar = foo.bind(obj, 20)
bar(30) // 2 20 30
结语
就这样,我们结束了这次关于this
的奇妙探险。记住,每一次this
的变换,都是JavaScript为你精心准备的魔术表演。只要掌握了规律,你也可以成为这场表演的主宰者!
转载自:https://juejin.cn/post/7377208418567929891