js的this绑定函数call,bind,apply

75 阅读5分钟

js的this绑定函数call,bind,apply

在JavaScript的世界里,this无疑是最让开发者又爱又恨的关键词之一。初学时觉得它简单易懂,深入后才发现其中暗藏玄机。本文将系统梳理this的作用及其在不同场景下的指向,帮助你理清思路,避免常见误区。

this的作用:动态上下文的指针

this,虽然字面意思是"这个",但在JS中,它的真正作用是作为函数运行时的上下文指针。它决定了当前代码块中引用的对象是谁。理解this的本质,就是理解JS函数执行时的环境。

举个例子:

const obj = {
  name: '小明',
  sayHi() {
    console.log(this.name);
  }
};
obj.sayHi(); // 输出:小明

这里的this指向obj对象本身。看似简单,但实际开发中,this的指向常常因为调用方式不同而发生变化。

this的指向全景梳理

JavaScript中,this的指向并非一成不变,而是取决于函数的调用方式。下面系统梳理常见的this指向场景:

1. 全局环境下的this

在浏览器环境中,最外层的this默认指向window对象。

console.log(this === window); // true

在Node.js环境下,顶层的this{}(即module.exports),而不是global

2. 作为对象方法调用

当函数作为对象的方法被调用时,this指向该对象。

const obj = {
  name: '小明',
  sayHi() {
    console.log(this.name);
  }
};
obj.sayHi(); // 输出:小明

3. 构造函数中的this

使用new关键字调用构造函数时,this指向新创建的对象。

function Person(name) {
  this.name = name;
}
const p = new Person('小红');
console.log(p.name); // 小红

4. 箭头函数的this

箭头函数的this由其定义时的外层作用域决定,而不是调用时。它不会被任何方式改变。

const obj = {
  name: '小明',
  sayHi: () => {
    console.log(this.name); // undefined
  }
};
obj.sayHi();

5. 事件处理器中的this

在DOM事件处理函数中,this指向触发事件的DOM元素。

document.getElementById('btn').onclick = function() {
  console.log(this); // <button id="btn">
};

6. 定时器、回调函数中的this

setTimeoutsetInterval中的普通函数,this在非严格模式下指向window,严格模式下为undefined

setTimeout(function() {
  console.log(this); // window 或 undefined
}, 1000);

7. call、apply、bind显式绑定

通过callapplybind可以显式指定this的指向。

function show() {
  console.log(this.name);
}
const obj = { name: '小明' };
show.call(obj); // 小明
show.apply(obj); // 小明
const boundShow = show.bind(obj);
boundShow(); // 小明

8. 类方法与原型链上的this

类方法中的this默认指向实例对象。原型链上的方法同理,this指向调用该方法的对象。

class Cat {
  constructor(name) {
    this.name = name;
  }
  meow() {
    console.log(this.name + ' 喵喵喵');
  }
}
const c = new Cat('咪咪');
c.meow(); // 咪咪 喵喵喵

9. 立即执行函数(IIFE)中的this

非严格模式下,IIFE中的this指向window;严格模式下为undefined

(function() {
  'use strict';
  console.log(this); // undefined
})();

10. 严格模式下的this

严格模式下,单独调用函数时thisundefined,不会自动指向window

'use strict';
function test() {
  console.log(this); // undefined
}
test();

11. 数组方法、forEach等回调里的this

部分数组方法(如forEach)允许手动传递this

const obj = { name: '小明' };
[1, 2, 3].forEach(function() {
  console.log(this.name);
}, obj); // 小明 小明 小明

总结:

JavaScript中的this,本质上是函数运行时的上下文指针。其指向完全取决于函数的调用方式,而非定义方式。只有深入理解这些规则,才能在实际开发中游刃有余,避免常见的陷阱和误区。

call、apply、bind:this绑定三兄弟,谁才是你的菜?

说到this绑定,call、apply、bind绝对是前端开发者的"老熟人"。它们就像三兄弟,长得像、性格却各有千秋。下面咱们一一盘点,看看谁才是你的"真命天子"。

1. call:显式绑定this,参数挨个传

用法简介:

call方法可以让你"临时借用"别的对象来当作函数的this,并且参数要一个个写。

function sayHello(age, city) {
  console.log(`我是${this.name},今年${age}岁,来自${city}`);
}
const obj = { name: '小明' };
sayHello.call(obj, 18, '北京'); // 我是小明,今年18岁,来自北京

推荐场景:

  • 想让一个函数在不同对象上下文中执行。
  • 需要灵活传递参数。

注意事项:

  • 非严格模式下,this为null/undefined会自动指向window。
  • call不能链式继承class。

2. apply:参数打包成数组,适合批量传参

用法简介:

applycall几乎一模一样,唯一的区别就是参数要用数组打包。

function sum(a, b, c) {
  return a + b + c;
}
console.log(sum.apply(null, [1, 2, 3])); // 6

推荐场景:

  • 参数数量不确定,直接用数组传递最方便。
  • 常用于"借用"数组方法处理类数组对象。
function toArray() {
  return Array.prototype.slice.apply(arguments);
}
console.log(toArray(1, 2, 3)); // [1, 2, 3]

注意事项:

  • apply的this规则和call一致。
  • ES6后,很多场景可以用扩展运算符(...)替代apply。

3. bind:返回新函数,this永久绑定

用法简介:

bind不会立即执行函数,而是返回一个永久绑定this的新函数。

const obj = { name: '小明' };
function showName() {
  console.log(this.name);
}
const boundShow = showName.bind(obj);
boundShow(); // 小明

推荐场景:

  • 需要延迟执行、定时器、事件回调等场景。
  • React/Vue等框架中,常用来绑定组件方法的this。

注意事项:

  • bind返回的新函数,this无法再被改变。
  • 绑定参数后,调用时还可以继续追加参数(柯里化)。
function add(a, b) { return a + b; }
const add5 = add.bind(null, 5);
console.log(add5(10)); // 15

三兄弟的区别和选择建议

  • call:参数挨个传,立即执行。
  • apply:参数打包成数组,立即执行。
  • bind:返回新函数,this永久绑定,延迟执行。

实际开发中,call和apply用来"借用"方法、动态切换上下文最方便;bind适合需要延迟执行、回调、定时器等场景。

个人理解:

this绑定三兄弟,掌握好用法,开发效率直接起飞。别让this"反向拿捏"你,灵活运用,才能写出优雅又健壮的代码。遇到this混乱时,别慌,回头看看这三兄弟,准没错!

如果觉得有用,记得收藏点赞,咱们下篇见!