this:我究竟属于谁?
前言
在JavaScript编程中,this
关键字是一个核心概念,它在函数执行的上下文中扮演着至关重要的角色,决定着函数内部如何访问和操作外部数据。正确理解和运用this
的指向规则,对于编写高质量、易于维护的代码至关重要。本文将深入探讨this
的工作原理,包括其在不同场景下的指向规则,并特别介绍箭头函数对this
处理的不同之处。
一、this的作用与基本使用场景
this
的主要作用在于提供一种机制,让函数能够访问和操作定义它的上下文环境中的数据。具体来说,this
可以在全局作用域和函数体内被使用。在全局作用域下,this
默认指向全局对象,在浏览器环境中即为Window
对象;而在函数体内部,this
的值则依赖于函数的调用方式和执行环境。
二、this的指向规则
1. 默认绑定
首先,我们先解释一下什么是默认绑定,当一个函数不作为任何对象的方法直接被调用时,我们称其为默认绑定,其this
指向全局对象(在非严格模式下)或undefined
(在严格模式下)。例如:
var a = 1
function foo(){
var a = 2
function bar(){
var a = 3
function baz(){
console.log(this.a);
}
baz()
}
bar()
}
foo()
当你看完这段代码,你认为这里的this指向哪呢? 没错,这里的this指向全局,最后输出1
我们来解释一下为什么,首先我们找到this
,它现在在baz()
中,baz
企图打印出this
指向的a
,baz
函数是一个普通函数调用,并没有被某个对象所拥有,所以我们继续往上找,发现baz()
的“老大”bar()
也是一个默认绑定,那我们继续往上找,发现“老大的老大”foo()
也是默认绑定,所以我们继续找,找到全局作用域,发现全局作用域里面有一个全局变量a
并调用全局里面的a
2. 隐式绑定
当函数作为某个对象的属性(方法)被调用时,this
会被绑定到该对象上。这是最常见的this
使用场景之一:
const person = {
name: "Alice",
greet: function() {
console.log(`Hello, my name is ${this.name}`);
}
};
person.greet(); // 输出 "Hello, my name is Alice"
这个代码应该大家都见多了,就不多解释了
3. 隐式丢失
在链式调用或函数作为参数传递给其他函数时,如果中间某个环节改变了调用方式,可能会导致this
的隐式丢失。不过,这通常被视为一种错误用法,应避免或通过显示绑定来修正。
4. 显示绑定
JavaScript提供了call()
, apply()
和bind()
方法来显式地设置函数执行时this
的值,从而“掰弯”this
的指向:
var obj = {
a:1
}
function foo(x,y){
console.log(this.a,x+y);
}
foo.call(obj,1,2) //call会把foo中的this掰弯到obj中
foo.apply(obj,[2,3])
const bar = foo.bind(obj,1)
bar(2)
5. new绑定
当使用new
关键字创建一个对象实例时,构造函数内部的this
会自动绑定到新创建的对象上:
function Person(name) {
this.name = name;
this.sayName = function() {
console.log(this.name);
};
}
const alice = new Person("Alice");
alice.sayName(); // 输出 "Alice"
三、箭头函数与this
箭头函数是ES6引入的一种新的函数表达式,它在处理this
时有所不同。箭头函数不会创建自己的this
上下文,而是继承自其所在的词法环境。这意味着箭头函数内部的this
就是其定义时所在上下文的this
:
const person = {
name: "Dave",
greet: () => {
console.log(`Hello, I'm ${this.name}`); // 注意这里的this并不指向person
}
};
person.greet(); // 输出 "Hello, I'm undefined" 或者可能的全局变量名,取决于环境
在这个例子中,尽管greet
是person
对象的一个方法,但因为使用了箭头函数,其内部的this
并没有绑定到person
上,而是继承了外层(全局或最近的非箭头函数)的this
值。
四、优先级
在JavaScript中,this
的绑定规则遵循一定的优先级顺序,这有助于确定在特定情境下this
的指向。以下是this
绑定的五种类型及其优先级,从高到低排列:
1.new绑定:当函数作为构造函数通过new
关键字被调用时,this
会被绑定到新创建的对象实例上。这是最高优先级的绑定。
2.显示绑定:通过.call()
, .apply()
, 或 .bind()
方法明确指定函数的this
值。这种方式可以覆盖默认的绑定规则。
3.隐式绑定:当函数作为某个对象的属性或方法被调用时,this
会被绑定到该对象上。这是最常见的绑定情况之一。
4.默认绑定:如果以上三种情况都不适用,this
会按照以下规则默认绑定:
5.箭头函数中的绑定:箭头函数不遵循上述规则,它没有自己的this
。箭头函数中的this
是由其定义时的上下文决定的,即外层(非箭头)函数的this
值,或者全局作用域的this
值(如果是顶级作用域)。
总结起来,this
的绑定遵循一个确定的优先级顺序,其中new
绑定优先级最高,箭头函数中的特殊处理虽然不在这四类传统绑定规则之内,但其行为类似于一种特殊的默认绑定,且在实践中非常有用。理解这些优先级有助于准确预测和控制函数内部this
的指向,避免潜在的错误。
总结
正确理解和应用this
的规则,是JavaScript进阶之路上的重要一步。通过合理利用默认绑定、隐式绑定、显示绑定以及new绑定等机制,可以使代码更加灵活和高效。而箭头函数作为一种特殊的函数形式,以其简洁的语法和固定的this
行为,为解决某些场景下的问题提供了新的选择。掌握这些知识,将帮助你编写出更健壮、更易于维护的JavaScript代码。
好的,这次的内容就分享到这了,如果小友觉得整的还不错的,可以留下一个小小的赞帮助俺找回自己的脑子,谢谢啦!!!
转载自:https://juejin.cn/post/7377051427887628342