前言
在 JavaScript 中,this 是一个非常重要且有些让人困惑的概念。了解 this 的绑定规则是编写高质量代码的关键。在本文中,我们将深入探讨 this 的五种绑定规则
正文
我们先要知道this的作用是什么?this 在 JavaScript 中的作用是用来引用当前函数执行的上下文,也就是当前函数被调用时所处的环境。通过使用 this,我们可以在函数内部访问和操作当前对象的属性和方法,以及引用当前函数被调用时的上下文信息。
在某种程度上,this 可以简化代码,因为它使得我们可以更灵活地使用函数,而不需要显式地传递参数。通过正确地理解 this 的绑定规则,我们可以避免在代码中频繁地传递对象或参数,从而简化代码逻辑并提高代码的可读性和可维护性。
总的来说,this 的作用是帮助我们更方便地操作当前对象的属性和方法,简化代码逻辑,以及提高代码的可读性和可维护性。
1.this的默认绑定
我们首先需要知道,this是不能引用词法作用域里的东西的,来看下面这段代码:
js
function bar() {
var b = 3
console.log(this.b);
}
bar()
这段代码执行结果是undefined,在这段代码中,函数 bar 内部定义了一个局部变量 b,然后尝试通过 this.b 来访问一个属性 b。由于 b 并没有作为对象的属性被定义,因此 this.b 会返回 undefined。
当函数 bar() 被调用时,它的执行上下文中的 this 默认会指向全局对象(在浏览器环境中通常是 window 对象)。由于全局对象并没有一个名为 b 的属性,因此 this.b 的值为 undefined。
好,然后把这段代码变一下,变成下面这样:
js
var b = 3
function bar() {
console.log(this.b);
}
bar()
这段代码在浏览器里执行的结果就是3,这里的this是指向window的,所以可以得到默认绑定规则---函数在哪个词法作用域里生效,this就指向哪里。 但有些地方会说函数在哪里调用,this就指向哪里。这句话不全对,这里再看一个demo:
js
function foo() {
var b = 1
bar()
}
function bar() {
console.log(this.b);
}
foo()
这里按照"函数在哪里调用,this就指向哪里"这句话来说执行结果应该是1,但实际执行结果是undefined,这是因为函数虽然有作用域但没有词法作用域,函数天生就有作用域,且作用域是一个可见的属性[[scope]]
2.this的隐式绑定
隐式绑定---当函数被一个对象所拥有,再调用时,此时this会指向该对象。 直接看代码:
js
function foo(){
console.log(this.a);
}
var obj = {
a: 2,
foo: foo,
}
obj.foo()
当调用 obj.foo() 时,函数 foo 被对象 obj 所拥有,因此在函数执行时,this 会隐式绑定到对象 obj 上。这意味着在函数 foo 中的 this.a 将会指向对象 obj 中的属性 a,即 2。
因此,执行 obj.foo() 的结果会是输出 2。这就是所谓的隐式绑定规则:当函数被一个对象所拥有 ,再调用时,this 会指向该对象,从而可以访问该对象的属性和方法。
3.隐式丢失
js
function foo(){
console.log(this.a);
}
var obj = {
a: 2,
foo: foo,
}
var obj2 = {
a: 3,
obj: obj,
}
obj2.obj.foo()
想想这段代码的执行结果是2还是3,结果是2,当执行 obj2.obj.foo() 时,函数 foo 虽然被对象 obj 所拥有,但是实际上是通过 obj2 对象的属性 obj 进行链式调用的。在链式调用中,this 指向的是最后一层调用的对象,即 obj 对象。
因此,虽然 foo 函数本来是被对象 obj 所拥有的,但由于通过 obj2.obj 进行了链式调用,最终 this 指向的是对象 obj,因此 this.a 会访问对象 obj 中的属性 a,即输出 2。
这种情况被称为隐式绑定的隐式丢失,因为虽然函数最初是被一个对象所拥有的,但由于链式调用的方式,this 最终指向了引用函数的对象,而不是最初拥有函数的对象。
这就是隐式绑定的隐式丢失---当函数被多个对象链式调用时,this指向引用函数的对象。
4.this的显示绑定
显式绑定---使用call、apply、bind方法,绑定this
js
function foo(n){
console.log(this.a, n);
}
var obj = {
a: 2
}
foo.call(obj, 100)
通过调用 foo.call(obj, 100),使用了显式绑定的方式来调用函数 foo。在这里,call 方法是 JavaScript 中用于改变函数执行上下文的方法,第一个参数是要绑定到函数中的 this 值,这里是对象 obj。第二个参数及之后的参数会传递给函数 foo。
因此,在调用 foo.call(obj, 100) 时,函数 foo 的执行上下文中的 this 被显式绑定到了对象 obj,所以 this.a 将会指向对象 obj 中的属性 a,即 2,同时参数 n 为 100。因此,执行 foo.call(obj, 100) 的结果会是输出 2 100。这就是显式绑定的方法,通过 call、apply 或 bind 来明确指定函数执行时的 this 值。
js
function foo(n, m){
console.log(this.a, n, m);
}
var obj = {
a: 2
}
// foo.call(obj, 100, 200)
// foo.apply(obj, [100, 200]) apply和call不一样的就是要用数组
var bar = foo.bind(obj) // bind调用完成后会返回一个函数体,所以后面要bar调用.var bar = foo.bind(obj, 100, 200)这样调用参数也行
bar(100, 200) // 语法问题,接受就行了
通过使用显式绑定的方式,可以通过 call、apply 或 bind 方法来指定函数执行时的 this 值为对象 obj。
- 使用
foo.call(obj, 100, 200):通过call方法将函数foo的执行上下文中的this绑定到对象obj,并传递参数100和200给函数foo。这样执行foo.call(obj, 100, 200)的结果会是输出2 100 200。 - 使用
foo.apply(obj, [100, 200]):与call类似,apply方法也可以将函数foo的执行上下文中的this绑定到对象obj,不同之处在于参数的传递方式,apply接受一个包含参数的数组。这样执行foo.apply(obj, [100, 200])的结果也会是输出2 100 200。 - 使用
var bar = foo.bind(obj):bind方法会创建一个新的函数,该函数的this值永久绑定到指定的对象obj。在这里,通过var bar = foo.bind(obj)创建了一个新的函数bar,然后调用bar(100, 200)时,函数bar的执行上下文中的this被绑定到了对象obj,同时传递了参数100和200给函数foo。这样执行bar(100, 200)的结果也会是输出2 100 200。
5.new绑定
new是做了一件什么事情?第一创建this对象,第二执行函数体逻辑,往this上放属性,第三将this的隐式原型赋给构造函数的显示原型,第四return。
当使用 new 关键字来调用一个函数时,会创建一个新的对象,并将这个新对象作为函数的执行上下文,即将函数内部的 this 指向这个新对象。这种方式被称为 new 绑定。
例如,假设有一个构造函数 Person,定义如下:
javascript
function Person(name, age) {
this.name = name;
this.age = age;
this.greet = function() {
console.log("Hello, my name is " + this.name + " and I am " + this.age + " years old.");
}
}
然后通过 new 关键字来创建一个新的实例对象:
ini
var person1 = new Person("Alice", 30);
person1.greet(); // 输出:Hello, my name is Alice and I am 30 years old.
在这个例子中,通过 new Person("Alice", 30) 创建了一个新的 Person 实例对象 person1。在构造函数 Person 中,this 被绑定到了新创建的实例对象 person1,因此在 greet 方法中使用 this.name 和 this.age 访问实例对象的属性。
箭头函数
在 JavaScript 中,箭头函数是一种特殊的函数形式,它没有自己的 this 绑定。当使用箭头函数时,箭头函数会捕获在定义时所处的上下文的 this 值,并且不会被任何方式所改变。这意味着箭头函数内部的 this 始终指向箭头函数定义时所处的作用域的 this 值,而不是调用箭头函数时的上下文。这种特性使得箭头函数在处理 this 绑定时更加简洁和可靠。
举个例子,假设我们有一个对象 obj,其中包含一个方法 foo,方法内部使用箭头函数来定义一个定时器:
js
var obj = {
a: 2,
foo: function() {
setTimeout(() => {
console.log(this.a);
}, 100);
}
};
obj.foo();
在这个例子中,箭头函数内部的 this 指向的是 obj 对象,而不是 setTimeout 函数的调用者。因此,箭头函数绑定可以帮助我们避免在定时器或回调函数中出现 this 指向混乱的情况,简化了代码的编写和理解。箭头函数的绑定方式在某些情况下非常有用,特别是在需要保持一致的 this 指向时。
总结
所以,在 JavaScript 中,this 是一个非常重要且常见的概念,它用于指代当前执行上下文中的对象。在编写 JavaScript 代码时,正确理解和处理 this 的指向是至关重要的。在实际开发中,我们经常会遇到不同的 this 绑定情况,例如隐式绑定、显式绑定、默认绑定、new 绑定以及箭头函数绑定。每种绑定方式都有自己的特点和用途,了解它们可以帮助我们更好地控制代码的行为,避免出现意外的错误。通过合适地选择和应用不同的绑定方式,我们可以确保代码的逻辑正确性和可维护性,从而提升开发效率和代码质量。因此,在编写 JavaScript 代码时,务必注意对 this 的正确理解和使用,以确保代码的稳定性和可靠性。
这就是文章的全部内容了,觉得有帮助的请关注一下叭。