一、this的概念
this是JavaScript中的一个关键字,用于在函数的执行时引用当前执行上下文中的对象,代表着当前函数运行时所处的环境。this的值在函数的每次调用时都可能不同,取决于函数的调用方式和位置。弄清楚this的指向,可以让我们的代码更加简洁。在本文中,我们将深入探究JavaScript中this的用法和行为,并通过具体的代码例子来解释其工作原理。
二、提前声明
在浏览器和在node环境下,this不同。在浏览器环境中全局对象是 Window,在 Node.js 环境中是 Global,全局作用域无法进行默认绑定,所以导致this只能绑定在undefined上。执行同一段JavaScript代码的运行结果如下图所示,本文之后所有的代码示例均以谷歌浏览器控制台下的执行结果为准。
function foo() {
console.log(this.a);
}
var a = 2;
foo()
三、this的绑定规则
1.默认绑定
函数声明在哪里,词法环境就是哪里。函数在哪里调用,函数的this就指向哪里,this所处的词法环境在哪里生效了,this就绑定在哪里,当函数作为独立函数调用时,this指向全局对象Window。
function fun() {
console.log(this.name);
}
var name = '妲己'
fun() //输出:妲己
在上面的代码示例中,函数的调用应用了this的默认绑定,this指向全局对象Window,因此输出全局作用域下的变量name,值为妲己。
2.隐式绑定
当函数引用有上下文对象时,隐式绑定的规则就会把函数调用中的this绑定到这个上下文对象中。
function fun() {
console.log(this.name);
}
var obj = {
name: '赵云',
sayName:fun
};
var name = '黄忠'
obj.sayName() //输出:赵云
在上面的代码示例中,fun函数被引用至obj这个对象中,此时fun引用有上下文对象,这时根据隐式绑定规则就将fun中的this绑定到了obj这个对象当中,即this.name和obj.name是一样的,所以输出赵云。
function fun() {
console.log(this.a);
}
var obj1 = {
a: 666,
fun: fun
}
var obj2 = {
a: 888,
obj1: obj1
}
var obj3 = {
a: 999,
obj2: obj2
}
obj3.obj2.obj1.fun(); //输出:666
在上面的代码示例中,fun函数被对象obj1引用,又被obj2引用,又被obj3引用,多次引用函数,
但是这里不管有多少引用,多少嵌套,fun函数就是被绑定在obj1对象上,所以输出666。
3.隐式丢失
- 多次引用函数,最终函数调用前没有上下文对象,就会隐式丢失
- 当隐式绑定的函数丢失绑定对象,就会引用默认绑定
- 对象属性引用链只有上一层或者说最后一层在调用位置中起作用
function fun() {
console.log(this.name);
}
var obj = {
name: '赵云',
sayName:fun
};
var name = '黄忠'
var fun2 = obj.sayName
fun2() //输出:黄忠
在上面的代码示例中,fun函数被对象obj引用,之后对象obj又"不要"fun函数了,把引用给了fun2,此时隐式绑定的函数丢失了绑定对象,执行默认绑定,执行fun2函数其实就相当于执行fun函数,this指向全局作用域,输出全局变量黄忠。
4.显式绑定
显式绑定在JavaScript中,我们可以使用apply()、call()和bind()等方法显式地绑定this关键字的值。这样我们可以手动控制this的值,强行改变函数的this指向,而不依赖于隐式绑定。
function identify(){
return this.name.toUpperCase();
}
function speak(){
var greeting = 'Hello, I am' + identify.call(this)
console.log(greeting);
}
var me = {
name: 'Tom'
}
var you = {
name: 'Jerry'
}
console.log(identify.call(me)); //输出:TOM
call 修改函数的this指向,接收零散的参数。call()不传参数或传null参数时都指向Window。
apply 修改函数的this指向,参数以数组形式接收
bind 修改函数的this指向,会返回一个新的函数,接收零散的参数
function foo(x, y) {
console.log(this.a, x + y);
}
var obj = {
a: 666
}
foo.call(obj, 2, 3) //输出:666 5
foo.apply(obj, [2,3]) //输出:666 5
var foo2 = foo.bind(obj, 2, 3) //输出:666 5
foo2()
四、this的具体指向
上面已经提到了在全局作用域中和普通函数中的this指向,这里就不赘述了,说说另外三种。
1.在构造函数中
在构造函数中,this关键字指向通过构造函数创建的新对象。
function Person(name, age) {
this.name = name;
this.age = age;
}
const guanyu = new Person('关羽', 30);
console.log(guanyu.name); // 输出:关羽
console.log(guanyu.age); // 输出:30
在上面的代码示例中,通过Person构造函数创建了一个新对象guanyu,this关键字在构造函数中指向了这个新对象,并将属性name和age添加到该对象上。
2.在事件处理函数中
在事件处理函数中,this关键字指向触发事件的DOM元素。
<button>你点我一个试试!</button>
<script>
const btn = document.querySelector('button')
btn.addEventListener('click', function(){
console.log(this); // 输出:<button>你点我一个试试!</button>
});
</script>
在上面的代码示例中,当点击button按钮时,触发了click事件,事件处理函数中的this关键字指向了触发事件的按钮DOM元素,从而可以获取到该元素的DOM结构。
3.在箭头函数中
箭头函数本身没有this这个概念,但是箭头函数内是可以使用this的,写在其内部的this代表的是外部的非箭头函数的this。如果外部没有非箭头函数,那就指向全局作用域Window。箭头函数中的this是词法绑定的,它不会根据函数的调用方式和位置而改变,而是继承了上一层作用域中的this。在ES6引入的箭头函数中,this的行为有所不同。箭头函数没有自己的this关键字,它会捕获外部的this值,并在函数内部使用。
function foo() {
var bar = () => {
console.log(this.a); //输出:666
console.log(this.b); //输出:888
console.log(this.c); //输出:undefined
}
bar()
var c = 10
}
var a = 666
var b = 888
foo()
在上面的代码示例中,箭头函数bar里的this其实继承了上一层作用域中的this,也就是非箭头函数foo的this,而函数foo的this指向的是全局作用域,所以可以获取到全局作用域下的变量a和b,c变量不是全局变量,所以输出undefined。
五、最后的话
this是JavaScript中一个重要且复杂的概念,正确理解和使用this对于编写高质量的JavaScript代码至关重要。通过本文介绍的this的绑定规则和使用示例,希望可以帮助读者更好地理解和应用this关键字。在编写JavaScript代码时,务必仔细考虑函数的调用方式和位置,以确保this的正确绑定,避免出现意外的错误结果。
感谢你阅读这篇文章,如果你觉得写得还行的话,不要忘记点赞、评论、收藏哦!如有问题欢迎大家指正,祝大家生活愉快!