this指向谁,只需记住这几条绑定规则

this知多少

什么是this?

在 JavaScript 中,this 是一个指向对象的引用,这个对象是在运行时基于函数的执行环境动态绑定的。换句话说,this 的值取决于函数被调用时的上下文。它允许函数在不同的上下文中使用相同的代码。

为什么要有this?

this在JS中发挥着很重要的作用,具体体现如下:

1. 上下文感知

this 允许函数根据调用它的方式来引用不同的对象,使得代码更具灵活性和通用性。这意味着同一个函数可以在不同的上下文中执行不同的操作。

示例场景: 在一个事件处理函数中,根据触发事件的对象不同,执行相应的操作。

js 复制代码
const button1 = document.getElementById('button1');
const button2 = document.getElementById('button2');

function handleClick() {
  console.log(`Button "${this.textContent}" was clicked.`);
}

button1.addEventListener('click', handleClick);
button2.addEventListener('click', handleClick);

2. 简化代码

通过使用 this,可以避免硬编码特定对象的引用,使代码更易于维护和重用。这样可以使函数更加通用,适用于不同的情况。

示例场景: 创建一个通用的计算器对象,可以执行加法和乘法操作。

js 复制代码
const calculator = {
  add: function(a, b) {
    return this.sum(a, b);
  },
  multiply: function(a, b) {
    return this.product(a, b);
  },
  sum: function(a, b) {
    return a + b;
  },
  product: function(a, b) {
    return a * b;
  }
};

console.log(calculator.add(5, 3));       // 输出: 8
console.log(calculator.multiply(2, 4));  // 输出: 8

3. 面向对象编程

在面向对象的编程中,this 是一种常见的机制,用于访问对象的属性和方法。它允许在对象内部引用对象自身的属性和方法。

示例场景: 创建一个 Person 对象,其中包含姓名和打招呼的方法。

js 复制代码
function Person(name) {
  this.name = name;
}

Person.prototype.greet = function() {
  console.log(`Hello, my name is ${this.name}.`);
};

const john = new Person('John');
john.greet(); // 输出: Hello, my name is John.

如果不使用this:

在许多其他编程语言中,对象的方法通常可以直接访问对象的属性。

但是在js中,对象方法内部的 尝试访问的是对象属性,但是在该函数内部并没有定义该变量,会抛出一个 ReferenceError。要访问对象的属性,应该使用 this 关键字来引用对象的属性,而不是直接引用变量名。

4. 函数重用

通过动态绑定 this,可以使函数适用于不同的对象上,从而实现代码的重用。这使得同一个函数可以在不同的上下文中被调用,执行不同的任务。

示例场景: 使用构造函数创建多个对象,并共享同一个方法。

js 复制代码
function Car(make, model) {
  this.make = make;
  this.model = model;
}

Car.prototype.start = function() {
  console.log(`${this.make} ${this.model} is starting...`);
};

const car1 = new Car('Toyota', 'Corolla');
const car2 = new Car('Honda', 'Civic');

car1.start(); // 输出: Toyota Corolla is starting...
car2.start(); // 输出: Honda Civic is starting...

this绑定规则

默认绑定:

当一个函数独立调用,不带任何修饰符的时候

  • 函数在哪个词法作用域下生效(调用),函数中的this就指向哪里

(词法作用域是指在代码写作时定义变量的位置所确定的作用域。)

  • 只要是默认绑定,this一定指向window
js 复制代码
function sayName() {
  console.log(this.name);
}

var name = "Global";

sayName(); // 输出: Global

注意点:

在严格模式下(非浏览器运行环境下),默认绑定不会指向全局对象,而是会绑定到 undefined

隐式绑定:

当函数的引用有上下文对象时(当函数被某个对象拥有)

  • 函数中的this指向引用它的对象
  • 当一个函数被多个对象链式调用时,函数的this指向就近的那个对象

例如:

js 复制代码
var obj = {
  a: 1,
  foo: foo
}

var obj2 = {
  a: 2,
  obj: obj
}

function foo() {
  console.log(this.a);
}
var a = 3
obj2.obj.foo();

在这种情况下,foo 函数内的 this 关键字将指向 obj 对象,因为它是通过 obj2.obj.foo() 调用的。因此,this.a 将输出 1,而不是 3

  • 隐式丢失:当一个函数作为对象的方法被调用时,函数内部引用了其他对象的方法,导致 this 指向丢失的现象。

隐式丢失通常发生在以下几种情况下:

  1. 回调函数中的隐式丢失 :当将一个函数作为回调传递给另一个函数,并且在回调函数中引用了对象的方法时,由于回调函数的执行环境不再是该对象,this 指向会丢失。
js 复制代码
var obj = {
  name: "Alice",
  sayName: function() {
    console.log(this.name);
  },
  greet: function() {
    setTimeout(function() {
      console.log("Hello, " + this.name); // 隐式丢失
    }, 1000);
  }
};

obj.greet(); // 输出: "Hello, undefined"(在非严格模式下输出: "Hello, [全局对象的某个属性]")

在这个例子中,setTimeout 函数内部的回调函数中的 this 指向了全局对象,而不是 obj 对象,导致了隐式丢失。

  1. 事件处理函数中的隐式丢失 :当将一个对象的方法作为事件处理函数,绑定到 DOM 元素的事件上时,由于事件处理函数的执行环境是 DOM 元素,而不是对象,this 指向会丢失。
xml 复制代码
<button id="myButton">Click Me</button>
<script>
  var obj = {
    name: "Alice",
    handleClick: function() {
      console.log("Button clicked by " + this.name); // 隐式丢失
    }
  };

  document.getElementById("myButton").addEventListener("click", obj.handleClick);
</script>

在这个例子中,当按钮被点击时,handleClick 方法中的 this 会指向按钮元素,而不是 obj 对象,导致了隐式丢失。

  1. 函数赋值后的隐式丢失 :当将一个对象的方法赋值给一个变量,并在其他上下文中调用该变量时,由于执行环境的改变,this 指向会丢失。
js 复制代码
var person = {
  name: "Alice",
  sayName: function() {
    console.log(this.name);
  }
};

var say = person.sayName;
say(); // 输出: undefined(在非严格模式下输出: [全局对象的某个属性])

在这个例子中,say 变量在调用时没有指定上下文对象,导致 this 指向了全局对象,而不是 person 对象,发生了隐式丢失。

显式绑定:

通过call、apply、bind方法来指定this指向

  1. call 方法

    call 方法立即调用函数,并将一个指定的 this 值和若干个指定的参数传递给函数。

    call 方法的语法为 function.call(thisArg, arg1, arg2, ...)

js 复制代码
var person1 = { name: "Alice" };
var person2 = { name: "Bob" };

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

sayName.call(person1); // 输出: "My name is Alice"
sayName.call(person2); // 输出: "My name is Bob"
  1. apply 方法

    apply 方法与 call 方法类似,但它接受一个包含参数的数组作为参数。

    apply 方法的语法为 function.apply(thisArg, [argsArray])

js 复制代码
var person = { name: "Alice" };

function sayName(greeting) {
  console.log(greeting + ", my name is " + this.name);
}

sayName.apply(person, ["Hello"]); // 输出: "Hello, my name is Alice"
  1. bind 方法

    bind 方法创建一个新函数,在调用时将其 this 值绑定到传递给 bind 的值,并在调用时指定任何预设的参数。

    bind 方法的语法为 function.bind(thisArg[, arg1[, arg2[, ...]]])

js 复制代码
var person = { name: "Alice" };

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

var sayNameWithPerson = sayName.bind(person);
sayNameWithPerson(); // 输出: "My name is Alice"

不同之处

  1. 在使用 callapply 方法时,函数会立即执行,并且可以传入单个参数或者一个参数数组;
  2. callapply 的区别在于参数的传递方式:call 是逐个传参,而 apply 是以数组形式传参。而 bind 方法可以预先绑定 this 和部分参数,并在需要时执行。
  3. bind 方法返回的是一个绑定了指定上下文的新函数,不会立即执行,需要手动调用执行。

new绑定:

this指向创建的实例对象

ini 复制代码
function Person(name) {
  this.name = name;
}

var john = new Person("John");
console.log(john.name); // 输出: John

在这个例子中,this 被绑定到了新创建的 john 对象,因此在构造函数中可以正确地设置对象的属性。

错误示例:

js 复制代码
function Person(name) {
  this.name = name;
}

// 错误示例:忘记使用 new 关键字
var john = Person("John");
console.log(john); // undefined
console.log(name); // 输出: John

// 错误示例:忘记返回对象
function createPerson(name) {
  this.name = name;
}
var alice = new createPerson("Alice");
console.log(alice); // 输出: { name: 'Alice' }
console.log(name); // 输出: Alice

在这些错误示例中,忘记使用 new 关键字或者忘记在构造函数中返回对象都会导致意料之外的结果,因为此时 this 指向全局对象,而不是新创建的实例对象。

this绑定优先级:new绑定 > 显式绑定 > 隐式绑定 > 默认绑定

箭头函数中的this

箭头函数中的this是在定义函数时确定的,而不是在运行时。箭头函数没有自己的this绑定,它会捕获其所在上下文的this值(写在箭头函数中的this也是它外层非箭头函数的this ),并在整个函数生命周期内保持不变。

js 复制代码
const obj = {
  name: 'Alice',
  sayName: function() {
    setTimeout(() => {
      console.log(this.name);
    }, 1000);
  }
};

obj.sayName(); // 输出 'Alice',箭头函数捕获了 sayName 方法的 this 值

总结

掌握this,好好学习,天天向上

相关推荐
ifanatic6 分钟前
[面试]-golang基础面试题总结
面试·职场和发展·golang
I_Am_Me_19 分钟前
【JavaEE进阶】 JavaScript
开发语言·javascript·ecmascript
℘团子এ30 分钟前
vue3中如何上传文件到腾讯云的桶(cosbrowser)
前端·javascript·腾讯云
学习前端的小z35 分钟前
【前端】深入理解 JavaScript 逻辑运算符的优先级与短路求值机制
开发语言·前端·javascript
程序猿进阶1 小时前
堆外内存泄露排查经历
java·jvm·后端·面试·性能优化·oom·内存泄露
前端百草阁1 小时前
【TS简单上手,快速入门教程】————适合零基础
javascript·typescript
彭世瑜1 小时前
ts: TypeScript跳过检查/忽略类型检查
前端·javascript·typescript
Backstroke fish1 小时前
Token刷新机制
前端·javascript·vue.js·typescript·vue
zwjapple1 小时前
typescript里面正则的使用
开发语言·javascript·正则表达式
小五Five1 小时前
TypeScript项目中Axios的封装
开发语言·前端·javascript