函数在哪里调用,this就指向哪里,真的对吗?
JavaScript 中的 this
关键字
JavaScript 中的 this
关键字是一个非常重要的概念,它用来指代当前执行代码的上下文,通常是一个对象。了解 this
的绑定规则对于理解 JavaScript 函数和面向对象编程非常关键。但 this
的指向是动态的,它取决于代码执行的方式和上下文。这可能导致在实际使用中分不清this
实际指向的内容,从而非常困扰我们,不过不用担心,接下来我们将详细剖析这个关键词的指向规则。
this
的绑定规则
1. 默认绑定
默认绑定就是函数在哪个词法作用域里生效,this
就指向哪里,诶?难道说函数在哪里调用,this
就指向哪里,真的对吗?我们要注意,仅限于默认绑定时,我们的函数在哪个词法作用域里生效
而不是调用在哪就指向哪,所以说这句话不全对,存在很多瑕疵,让我们来用一个例子看看函数调用位置是否是this
指向:
function foo(){
function bar(){
console.log(this)
}
bar()
}
foo()
让我们运行一下试试,我们的bar()
在foo函数
中调用,那我们来看看this
会指向foo函数
吗?我们在浏览器中执行了一下结果是window对象
,我们并没有绑定上foo函数
,这就是默认绑定
。
我们再来看看在哪个词法作用域里生效,this
就指向哪里是怎样的,其实还是这个例子,我们会发现我们的bar调用在foo函数
中,而foo函数
是没有词法作用域的,函数只有作用域,那么我们就需要找foo的词法作用域
,没错,是全局
,如果一个函数没有任何上下文对象,它就会绑定到全局对象
(在浏览器中通常是 window
对象)。
2. 隐式绑定
隐式绑定发生在函数被一个对象所拥有并调用的情况下。在这种情况下,this
指向这个拥有它的对象。
const person = {
name: "Bob",
sayName: function() {
console.log(`Hello, ${this.name}`);
console.log(this); // this 指向 person 对象
}
};
person.sayName();
这个就比较简单了,我们的sayName函数
是person对象
所拥有的函数,那么this就指向了person对象
,所以我们在外面调用这个函数,函数中的this.name
会打印出Bob
也就是person对象
中的name
。这种绑定对象的情况我们称之为隐式绑定。
3. 隐式丢失
隐式丢失通常发生在函数被多个对象链式调用的情况下。这时,this
指向引用函数的对象,而不是最初的对象。我们也可以理解为就近原则。看这个例子:
const person1 = {
name: "Alice",
sayName: function() {
console.log(`Hello, ${this.name}`);
}
};
const person2 = {
name: "Bob"
};
person2.sayName = person1.sayName; // 借用 person1 的 sayName 函数
person1.sayName(); // Hello, Alice
person2.sayName(); // Hello, Bob
这个例子比较直观可以看到,谁调用就是指谁,假如我们再来一个例子:
function foo(){
console.log(this);
}
var obj = {
a: 2,
foo: foo
}
var obj2 = {
a : 3,
obj: obj
}
obj2.obj.foo()
我们使用对象obj2
来调用对象obj2
里的对象obj
,从而调用对象obj
中的foo函数
,绕晕了没?到底是对象obj2
调用的foo函数
还是对象obj
调用的foo函数
呢?我们运行一下,它输出的是2!没错,这就是就近原则,如果像这样链式调用一个对象里的函数,他指向最近的对象obj
。
在这样的调用下我们丢失了一个对象obj2
的引用,指向了对象obj
,这就是隐式丢失
4. 显式绑定
JavaScript 提供了 call
、apply
和 bind
方法,可以用来显式绑定函数的 this
。这些方法允许你在函数被调用时明确指定 this
指向的对象。
function sayName() {
console.log(`Hello, ${this.name}`);
}
const person = {
name: "Charlie"
};
sayName.call(person); // 使用 call 显式指定 this,输出 "Hello, Charlie"
5. new
绑定
当一个函数用 new
关键字调用时,它会创建一个新的对象,并将该对象绑定到函数的 this
。
function Person(name) {
this.name = name;
}
const person = new Person("Alice");
console.log(person.name); // 输出 "Alice"
在上面的例子中,Person
函数被用new
关键字调用,它在内部创建了一个新的对象(person
),并将this
绑定到这个新对象。这允许我们将属性name
赋值给新对象,最终创建了一个名为"Alice"的person
对象。
new
绑定是一种常见的方式来创建自定义对象的构造函数,这些对象可以拥有自己的属性和方法。这种方式非常有用,因为它允许你创建多个相似的对象,同时保持代码的可维护性。
需要注意的是,如果在构造函数内部没有显式返回其他对象,new
操作符会返回新创建的对象,如上面的person
对象。但如果构造函数内部显式返回了一个对象,那么new
操作符会返回该对象,而不是新创建的对象。这可以用来实现一些高级的模式,例如单例模式。
总之,new
绑定是JavaScript中用于创建对象实例的一种重要机制,它允许你构造自定义对象并初始化其属性。
this
不能引用词法作用域中的内容
首先要明确的是,this
并不是指向词法作用域(函数内部)中的变量或内容。而是根据函数被调用的方式来确定其指向。我们已经知道了this关键字的几种绑定规则,但我们要记住我们通过this关键字无法引用任何函数词法作用域中的内容:
让我们来看几个例子:
var b = 1
function foo(){
console.log(this.b)
}
foo()
这个例子很简单,我们的this在这种情况下就是默认绑定,绑定为全局对象(在浏览器中通常是 window
对象),那么我们在浏览器中执行,我们肯定能输出1对吧,但是假如我们直接用node在vs code里运行,我们会得到undefined,因为在vs code里node并没有创建全局window对象,所以我们无法引用。
那如果是在函数词法作用域呢?
function foo(){
var b = 1
function bar(){
console.log(this.b)
}
bar.call(foo)
}
foo()
这个例子里很明显我们使用显示绑定规则将this指针指向了foo,那么我们如果打印this我们会得到foo函数,但是我们这个例子里,我们能输出foo函数中的b吗?
跑一下试试,结果是undefined,失败了,我们并不能输出foo函数中的b,这也就是this
不能引用词法作用域中的内容,我们只能通过指向对象,从而获取对象中的内容。
箭头函数
最后,要注意箭头函数 (=>
) 在 this
方面的特殊行为。箭头函数没有自己的 this
绑定,而是继承自外层函数的 this
。
const obj = {
value: 42,
getValue: function() {
return () => {
console.log(this.value); // 这里的 this 指向外层函数的 this
};
}
};
const innerFunction = obj.getValue();
innerFunction(); // 输出 42
所以我们无法将指针指向箭头函数,因为箭头函数根本没有this这个概念,哪怕是箭头套箭头套箭头也是一直逐层往外找。
总之,理解 this
的绑定规则对于正确处理 JavaScript 中的上下文非常重要。不同的绑定规则可以在不同的情况下用于确保 this
指向正确的对象。我们如果想要灵活使用this指针一定要理解和掌握它的绑定规则。
那么我们这篇文章到这里就结束啦~
如果你想了解更多这类文章,点赞关注作者更新更多后续~