前言
在 JavaScript 中,this 关键字是一种指向当前执行上下文的特殊方式,它的值取决于函数被调用的方式。为了更好地理解它是如何出现的,本文会用通俗易懂的语言带你深入了解一下this关键字。
首先this会有哪些应用场景呢,或者说,你在哪些地方见过this呢?
this 的应用场景
1.全局环境下
首先,让我们看看在全局环境中 this :
console.log(this); // 在浏览器中输出 window,Node.js 中输出 global
这里,我们直接在全局打印this,this 指向全局对象,结果是 window(浏览器环境)或 global(Node.js 环境)。在全局环境下,this 就是整个程序的上下文。
2.函数内部
现在,我们来看看在函数内部 this :
function foo() {
console.log(this);
}
foo(); // 在浏览器中输出 window,Node.js 中输出 global
同样,函数内部的 this 默认指向全局对象。
3.对象方法中
接下来,我们看看在对象方法中 this:
let myObject = {
myMethod: function() {
console.log(this);
}
};
myObject.myMethod(); // 输出 myObject 对象
在这个例子中,this 指向包含这个方法的对象,也就是 myObject。
4.箭头函数中
最后,我们看一下箭头函数中的 this:
let foo = () => {
console.log(this);
};
foo(); // 在浏览器中输出 window,Node.js 中输出 global
在这个例子中,this 也指向全局对象。
以上就是几种this的应用场景,那它的指向到底是遵循什么样的规律呢?我们继续往下看······
this 的绑定规则
1. 默认绑定
我的理解:默认绑定是指函数在哪个词法作用域生效,this就指向哪里。
举个栗子:
function foo() {
var b = 1
bar()
}
function bar() {
console.log(this.b);
}
foo();
undefined
bar 函数被调用时,它尝试输出 this.b 的值。然而,由于 bar 函数是在foo函数内被调用,但是foo函数没有词法作用域,所以会向外继续寻找,直到找到一个词法作用域,而这个地方词法作用域为全局,所以bar函数实际上是在全局作用域中即生效的,它的 this 指向全局对象,所以会输出undefined。
注意:
1. 是不是最终都会指向全局:
在函数调用时,如果没有其他规则适用,this 最终都会指向全局对象。
2. 为什么输出undefined而不是报错:
我们知道,在JS中,如果尝试访问不存在的属性,不会抛出错误,而是返回undefined。当你在 bar 函数中尝试访问 this.b 时,JavaScript引擎会查找 this 对象中是否有名为 b 的属性。由于 this 是默认绑定到全局对象,而全局对象中没有 b 属性,所以返回的结果是 undefined,而不是抛出错误。
2. 隐式绑定
当函数被一个对象拥有时,通过该对象进行调用,this 将指向该对象。
举个栗子:
function foo() {
console.log(this.a);
}
var obj ={
a:2,
foo:foo//引用
}
obj.foo()//2
2
在这个例子中,foo 函数被赋值给了 obj 对象的属性 foo,即foo函数被obj属性引用,然后通过 obj.foo() 的方式进行调用。这样的调用方式是作为对象方法调用的,因此 this 将指向调用它的对象,即 obj。所以,当 foo 函数内部执行 console.log(this.a); 时,this.a 实际上指向 obj.a,因此输出结果是 2。
3. 隐式丢失
隐式丢失指的是在函数多个对象链式调用时,this 指向最后一层对象。如果函数被提取出来单独调用,this 就会丢失其隐式绑定。通俗来讲,就是当函数多个对象链式调用时,this指向引用函数的对象。
举个栗子:
function foo() {
console.log(this.a);
}
var obj ={
a:2,
foo:foo//引用
}
var obj2 = {
a:3,
obj:obj//把foo给obj2
}
var bar = obj2.obj.foo; // 提取 foo 函数
bar(); // 隐式丢失,此时的调用上下文是全局对象
在这个例子中,我们有两个对象:obj 和 obj2。obj 对象有一个属性 foo,它引用了全局定义的函数 foo。然后,obj2 对象有一个属性 obj,它引用了 obj 对象。我们将 foo 函数从 obj2 对象中提取出来,并在全局上下文中调用。这样,foo 函数将失去对对象的隐式绑定,导致 this 指向全局对象。
4. 显示绑定
通过 call、apply 或 bind 方法,可以显式地指定函数内的 this 值。
举个栗子:
function foo(){
console.log(this.a);
}
var obj = {
a:2,
}
// foo.call(obj)
// foo.apply(obj)
var bar = foo.bind(obj)
bar()//2
在这个例子中,定义了一个函数 foo 和一个对象 obj,然后使用 bind 方法(call、apply同理)创建了一个新的函数 bar,并将 obj 作为 foo 函数的上下文。这样 foo 函数内的 this.a 将输出 obj.a 的值,即 2。
5. new 绑定
当函数被用作构造函数来创建对象实例时,this 指向新创建的对象。
举个栗子:
function Person(name) {
this.name = name;
}
const person = new Person('阳阳');
console.log(person.name); // 输出 "阳阳"
在这个例子中,你定义了一个构造函数 Person,并通过 new 关键字创建了一个实例 person。通过使用 new 关键字调用构造函数,你创建了一个新的 Person 对象,并将其赋值给变量 person。构造函数中的 this 指向新创建的对象,因此 person.name 的值为传递给构造函数的参数 '阳阳'。
6. 箭头函数
箭头函数没有自己的 this,它会继承外层函数的 this。这使得箭头函数在某些场景下更为方便,同时,由于缺乏 this 的概念,箭头函数不能用作构造函数。
举个栗子:
var obj = {
name: '阳阳',
show: function(){
var bar = function(){
console.log(this.name);
}
bar()//独立调用,默认绑定
},
}
obj.show();
在这个例子中,obj 对象包含一个 show 方法,而 show 方法内部包含一个函数 bar。在 bar 函数内,尝试输出 this.name。然而,由于 bar 函数是在全局作用域中独立调用的,而不是作为对象的方法调用,这导致了默认绑定,this 会指向全局对象。
于是我们修改成以下代码:
var obj = {
name: '阳阳',
show: function(){
var bar = () => {//箭头不看this,往外看,被show引用,所以隐式绑定
console.log(this.name);
}
bar()
},
}
obj.show();
我们用箭头函数 bar 替代了原来的普通函数。箭头函数的特性是它没有自己的 this,而是继承自外部作用域的 this。在这个例子中,bar 函数被定义在 show 方法内,因此继承了 show 的 this,而 show 的 this 是 obj 对象。所以,通过箭头函数 bar,避免了普通函数中 this 指向默认绑定的问题。现在,在 bar() 被调用时,this.name 指向了 obj 对象的 name 属性,即'阳阳'。
最后
在 JavaScript 中,this 是一个关键字,它的值在函数被调用时动态确定。理解 this 的行为对于编写灵活、可维护的 JavaScript 代码至关重要。我们探讨了 this 的几种绑定规则,包括默认绑定、隐式绑定、显式绑定、new 绑定、以及箭头函数中的绑定规则。
希望本文对你理解 this 关键字提供了帮助,欢迎大家评论区交流学习。
我的Gitee: CodeSpace (gitee.com)
技术小白记录学习过程,有错误或不解的地方还请评论区留言,如果这篇文章对你有所帮助请 “点赞 收藏+关注” ,感谢支持!!