Hello,今天扮演大白角色,给大家深入聊聊this。看了挺多文章和相关内容的,如果有不对的地方还请大佬指正,帮助共同学习。
学JS其中最让人头疼的概念之一就是 this 关键字的绑定规则。小白其实对此很是困扰,今天就让大白我用一篇文章,将详细探讨 this 的工作原理,并通过实例来帮助你更好地理解它。
1. 调用位置:函数调用的实际位置决定了 this 的值
在 JavaScript 中,this 的值取决于函数被调用的方式,而不是它被定义的方式。为了确定 this 的值,我们需要分析调用栈,即函数调用的顺序。调用位置指的是函数实际被调用的地方,而非声明的位置。
例如:
function baz() {
bar();
console.log('baz');
}
function bar() {
// 当前调用栈:bar -> baz
console.log('bar');
}
baz(); // 调用栈开始于此
在这个例子中,baz() 被调用后,bar() 从 baz() 内部被调用,所以 bar() 的调用位置是在 baz() 函数内部。这意味着 bar() 的 this 值是由它如何被调用决定的,而不是由它在哪里被定义决定的。
2. 默认绑定:普通函数调用时 this 的行为
当一个函数作为普通函数被调用时,this 会默认绑定到全局对象(在浏览器环境中是 window,在 Node.js 环境中是 global)。在严格模式(use static)下,this 将被设置为 undefined。
function foo() {
console.log(this.a);
}
var a = 2; // 全局变量
foo(); // 输出: 2 (非严格模式) 或 undefined (严格模式)
在这个例子中,foo() 是作为一个普通函数被调用的,因此它的 this 指向全局对象。如果你在一个严格模式下的脚本中运行这段代码,this 将是 undefined,因为严格模式禁止了默认绑定到全局对象的行为。
3. 隐式绑定:作为对象方法调用时 this 的行为
- 隐式绑定发生在函数作为对象的方法被调用时。此时,
this绑定到拥有该方法的对象。
var obj = {
a: 1,
foo: function() {
console.log(this.a);
}
};
obj.foo(); // 输出: 1
在这个例子中,foo 是作为 obj 对象的一个方法被调用的,因此它的 this 指向 obj。这使得 foo 可以访问 obj 的属性 a。
- 如果我们将方法赋值给一个变量然后调用,
this将不再指向原对象,而是遵循默认绑定规则。
var a = 93;
var obj = {
a: 1,
foo: function() {
console.log(this.a);
}
};
var anotherFoo = obj.foo;
anotherFoo(); // 输出: 93 (非严格模式) 或 undefined (严格模式)
在这个例子中,anotherFoo 是 obj.foo 的一个引用,但它不是作为 obj 的方法被调用的,因此它的 this 指向全局对象或 undefined(取决于是否启用了严格模式)。
- 当有多个对象嵌套时,只有最后一层对象的属性引用会影响
this的绑定。
function foo() {
console.log(this.a);
}
var obj = {
a: 142,
foo: foo
};
var obj2 = {
a: 2,
obj: obj
};
obj2.obj.foo(); // 输出: 142
在这个例子中,foo 是作为 obj 的方法被调用的,即使它是通过 obj2 访问的。this 仍然指向 obj,因为它是最直接调用 foo 的对象。
4. 显式绑定:使用 call、apply 和 bind 设置 this 的值
JavaScript 提供了 call、apply 和 bind 方法来显式地设置 this 的值。这些方法允许你在调用函数时明确指定 this 应该指向哪个对象。下面对这几种方法做简单解释
call:立即执行函数,并允许你指定this的值以及传递参数给该函数。它的第一个参数是this的值,后面的参数依次是函数的参数。
function greet(greeting, punctuation) {
console.log(greeting + ' ' + this.name + punctuation);
}
var person = { name: 'Alice' };
greet.call(person, 'Hello', '!'); // 输出: Hello Alice!
apply:与call类似,但它接受一个参数数组而不是单独的参数列表。这对于不确定参数数量的情况非常有用。
function greet(greeting, punctuation) {
console.log(greeting + ' ' + this.name + punctuation);
}
var person = { name: 'Alice' };
greet.apply(person, ['Hi', '.']); // 输出: Hi Alice.
bind:创建并返回一个新的函数,该函数的this值会被永久绑定,无论其如何被调用。bind还可以预设一些参数,这些参数会在新函数被调用时作为固定参数传递。
function greet(greeting, punctuation) {
console.log(greeting + ' ' + this.name + punctuation);
}
var person = { name: 'Alice' };
var greetPerson = greet.bind(person); // 用了bind
greetPerson('Hey', ','); // 输出: Hey Alice,
bind 创建的新函数不会立即执行,而是一个新的函数,你可以稍后调用它。这使得 bind 在需要提前设置 this 的情况下非常有用,比如在事件处理程序中。
5. 构造器绑定:使用 new 关键字时 this 的行为
new 一个新对象是很常见的做法。当使用 new 操作符调用函数时,会创建一个全新的对象,并且 this 会被绑定到这个新实例上。构造函数通常用于创建具有特定属性和方法的对象。
function Person(name) {
this.name = name;
}
var alice = new Person('Alice');
console.log(alice.name); // 输出: Alice
在这个例子中,Person 是一个构造函数,new Person('Alice') 创建了一个新的 Person 实例,并将 this 绑定到这个新对象。因此,alice 对象有一个 name 属性,其值为 'Alice'。
6. 箭头函数中的 this
箭头函数是 ES6 引入的一种新的函数定义方式。与普通函数不同,箭头函数没有自己的 this,它会捕获其所在上下文的 this 值。这意味着箭头函数的 this 是在其定义时确定的,而不是在其调用时确定的。
const person = {
name: 'Alice',
greet: () => {
console.log(`Hello, my name is ${this.name}`);
}
};
person.greet(); // 输出: Hello, my name is undefined
在这个例子中,greet 是一个箭头函数,它的 this 捕获的是其定义时的上下文,而不是 person 对象。因此,this.name 是 undefined。如果你想在对象方法中使用 this,应该避免使用箭头函数。
7. 事件处理程序中的 this
web里面事件点击获得元素,如果通过event.target,选择器或是通过父级或是兄弟级元素进行查找等方式获取,你会发现代码长度增加了。如果使用箭头函数,没有自己的this,还需要确保event.target存在。在事件处理程序中,this 通常指向触发事件的 DOM 元素。这对于处理用户交互非常有用,因为你可以在事件处理程序中直接访问触发事件的元素。
<button id="myButton">Click me</button>
<script>
document.getElementById('myButton').addEventListener('click', function() {
console.log(this.id); // 输出: myButton
});
</script>
在这个例子中,this 指向触发点击事件的按钮元素,因此你可以直接访问它的属性,如 id。
结论
先别懵,下面让我们来总结一下:
- 默认绑定:
this指向全局对象或undefined(严格模式)。 - 隐式绑定:
this指向调用它的对象。 - 显式绑定:
this可以通过call、apply或bind来明确指定。 - 构造器绑定:
this指向新创建的实例对象。 - 箭头函数:没有自己的
this,捕获其所在上下文的this值。 - 事件处理程序:
this通常指向触发事件的 DOM 元素。
从上面文段,看出理解 this 的绑定规则对于编写清晰和可维护的 JavaScript 代码真的很重要。特别是在处理回调函数或事件处理器时,确保 this 指向正确的对象可以帮助避免很多潜在的问题。
大白是否给你讲清楚了呢,欢迎评论区留言讨论~~