理解 `this` 绑定机制:显式、隐式、默认与 new 绑定

好的,各位屏幕前的英雄们,欢迎来到今天的“this寻宝之旅”!🤠

作为一个在代码世界里摸爬滚打多年的老兵,我深知 this 这个小家伙有多么让人头疼。它就像一个调皮的精灵,一会儿躲在对象里,一会儿又跑到全局环境里,让人摸不着头脑。

别担心,今天我就要化身寻宝猎人,带领大家拨开迷雾,彻底驯服 this 这个小妖精!

引言:this,一个让人又爱又恨的小妖精

在JavaScript的世界里,this 是一个非常重要,但又常常让人感到困惑的概念。它就像一个神秘的指针,指向函数执行时的上下文。理解 this 的绑定机制,是成为一名合格的JavaScript开发者的必备技能。

this 的绑定规则看似简单,实则暗藏玄机。它会根据函数被调用的方式,动态地指向不同的对象。如果稍不留神,就可能掉入 this 的陷阱,导致代码出现意想不到的错误。

但是,只要我们掌握了 this 的绑定规则,就能轻松驾驭它,让它为我们所用。

第一站:默认绑定——this 的“无人认领”状态

首先,我们来认识一下 this 的默认绑定。顾名思义,默认绑定就是指在没有任何其他规则应用的情况下,this 所指向的对象。

在非严格模式下,默认绑定会将 this 指向全局对象。在浏览器中,全局对象就是 window;在Node.js中,全局对象就是 global

让我们来看一个简单的例子:

function foo() {
  console.log(this.name); // undefined (严格模式) 或者 空 (非严格模式,浏览器环境)
}

var name = "Global Name"; // 在全局作用域定义name变量

foo(); // 在浏览器环境下,输出 "Global Name" (非严格模式)

在上面的代码中,foo 函数是在全局作用域中被调用的,没有任何其他规则应用。因此,this 默认绑定到全局对象 window。由于我们在全局作用域中定义了 name 变量,所以 console.log(this.name) 会输出 "Global Name"。

但是,如果在严格模式下,默认绑定会将 this 指向 undefined。这是因为在严格模式下,不允许 this 绑定到全局对象,以避免意外修改全局变量。

"use strict";

function foo() {
  console.log(this); // undefined
}

foo();

记住:默认绑定是 this 的最后一道防线。只有在没有任何其他规则应用的情况下,才会触发默认绑定。

第二站:隐式绑定——this 的“对象认领”状态

接下来,我们来了解一下隐式绑定。隐式绑定是指当函数作为对象的方法被调用时,this 会指向该对象。

让我们来看一个例子:

var obj = {
  name: "Object Name",
  foo: function() {
    console.log(this.name); // "Object Name"
  }
};

obj.foo(); // 函数作为obj的方法被调用

在上面的代码中,foo 函数是作为 obj 对象的方法被调用的。因此,this 会隐式绑定到 obj 对象。console.log(this.name) 会输出 "Object Name"。

隐式绑定的关键在于函数是如何被调用的。只有当函数作为对象的方法被调用时,才会触发隐式绑定。

但是,隐式绑定也存在一些陷阱。例如,当我们将对象的方法赋值给一个变量,然后再调用该变量时,this 可能会指向全局对象。

var obj = {
  name: "Object Name",
  foo: function() {
    console.log(this.name);
  }
};

var bar = obj.foo; // 将obj.foo赋值给bar
bar(); // 函数作为普通函数被调用,this指向全局对象 (非严格模式)

在上面的代码中,我们将 obj.foo 赋值给 bar 变量。当我们调用 bar() 时,bar 实际上是一个普通函数,而不是 obj 的方法。因此,this 会默认绑定到全局对象。

为了避免这种情况,我们可以使用 bindcallapply 方法来显式地指定 this 的指向。

第三站:显式绑定——this 的“强行指派”状态

现在,我们来学习一下显式绑定。显式绑定是指使用 callapplybind 方法来显式地指定 this 的指向。

callapply 方法都可以用来调用函数,并指定 this 的指向。它们的区别在于传递参数的方式不同。call 方法接受一个参数列表,而 apply 方法接受一个参数数组。

function foo(arg1, arg2) {
  console.log(this.name, arg1, arg2);
}

var obj = {
  name: "Object Name"
};

foo.call(obj, "arg1", "arg2"); // "Object Name arg1 arg2"
foo.apply(obj, ["arg1", "arg2"]); // "Object Name arg1 arg2"

在上面的代码中,我们使用 callapply 方法将 this 显式绑定到 obj 对象。console.log(this.name) 会输出 "Object Name"。

bind 方法也可以用来显式地指定 this 的指向。但是,bind 方法不会立即调用函数,而是返回一个新的函数,该函数的 this 已经被绑定到指定的对象。

function foo() {
  console.log(this.name);
}

var obj = {
  name: "Object Name"
};

var bar = foo.bind(obj); // 返回一个新的函数,this已经绑定到obj
bar(); // "Object Name"

在上面的代码中,我们使用 bind 方法将 this 绑定到 obj 对象,并返回一个新的函数 bar。当我们调用 bar() 时,this 已经绑定到 obj 对象,console.log(this.name) 会输出 "Object Name"。

显式绑定可以让我们更加灵活地控制 this 的指向,避免出现意外的错误。

第四站:new 绑定——this 的“新生儿”状态

最后,我们来探索一下 new 绑定。new 绑定是指当使用 new 关键字调用函数时,this 会指向新创建的对象。

当我们使用 new 关键字调用函数时,会发生以下几个步骤:

  1. 创建一个新的空对象。
  2. 将新对象的原型指向构造函数的 prototype 属性。
  3. this 绑定到新对象。
  4. 执行构造函数中的代码。
  5. 如果构造函数没有显式返回一个对象,则返回新创建的对象。

让我们来看一个例子:

function Foo(name) {
  this.name = name;
  console.log("Foo executed");
}

Foo.prototype.sayName = function() {
  console.log("My name is " + this.name);
};

var obj = new Foo("New Object"); // Foo executed
obj.sayName(); // My name is New Object
console.log(obj.name); // New Object

在上面的代码中,我们使用 new 关键字调用 Foo 函数,创建了一个新的对象 objthis 被绑定到新对象 obj。因此,this.name = name 会将 "New Object" 赋值给 obj.name

new 绑定是 JavaScript 中实现面向对象编程的重要机制。

this 绑定优先级——谁说了算?

现在,我们已经了解了 this 的四种绑定规则。但是,当多种规则同时应用时,哪个规则说了算呢?

this 的绑定优先级如下:

  1. new 绑定
  2. 显式绑定
  3. 隐式绑定
  4. 默认绑定

也就是说,new 绑定优先级最高,默认绑定优先级最低。

让我们来看一个例子:

function foo(name) {
  this.name = name;
  console.log(this.name);
}

var obj1 = {
  name: "obj1",
  foo: foo
};

var obj2 = {
  name: "obj2"
};

obj1.foo("implicit"); // implicit (隐式绑定)

foo.call(obj2, "explicit"); // explicit (显式绑定,优先级更高)

new foo("new"); // new (new绑定,优先级最高)

var bar = foo.bind(obj2);
bar("bind"); //bind

foo("default"); // default (默认绑定,优先级最低)

在上面的代码中,foo 函数同时应用了隐式绑定、显式绑定和 new 绑定。由于 new 绑定优先级最高,因此 this 最终指向新创建的对象。

总结:this 寻宝之旅的终点

恭喜各位,我们已经完成了 this 寻宝之旅!🎉

通过今天的学习,我们了解了 this 的四种绑定规则:默认绑定、隐式绑定、显式绑定和 new 绑定。我们还学习了 this 的绑定优先级。

掌握 this 的绑定机制,是成为一名优秀的JavaScript开发者的重要一步。希望今天的分享能够帮助大家更好地理解和应用 this

一张表格总结this的绑定方式

绑定类型 触发条件 this 指向 优先级 备注
默认绑定 函数独立调用 (非严格模式/严格模式) 全局对象 (window/global) / undefined (严格模式) 最低 在非严格模式下,全局环境下 this 指向全局对象;严格模式下指向 undefined
隐式绑定 函数作为对象的方法调用 调用该方法的对象 较低 obj.foo()this 指向 obj
显式绑定 使用 callapplybind 调用函数 显式指定的对象 较高 foo.call(obj)this 指向 obj
new 绑定 使用 new 关键字调用函数 新创建的对象 最高 new Foo()this 指向新创建的 Foo 实例。

记住,this 是一个动态的概念,它的指向取决于函数被调用的方式。只要我们掌握了 this 的绑定规则,就能轻松驾驭它,让它为我们所用。

希望大家在未来的编程道路上,能够更加自信地面对 this 这个小妖精!💪

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注