Skip to content

this 绑定机制深度解析

this 是什么

this 是 JavaScript 中的一个关键字,它指向当前执行上下文的对象。this 的指向在函数被调用时确定,而不是在函数定义时确定。

this 的绑定规则

1. 默认绑定(Default Binding)

在非严格模式下,独立函数调用时,this 指向全局对象(浏览器中是 window,Node.js 中是 global)。

javascript
function showThis() {
  console.log(this); // 浏览器中: Window 对象
}

showThis(); // 默认绑定

// 严格模式
function strictShowThis() {
  "use strict";
  console.log(this); // undefined
}

strictShowThis();

2. 隐式绑定(Implicit Binding)

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

javascript
const obj = {
  name: "JavaScript",
  greet: function() {
    console.log(`Hello, ${this.name}`);
  }
};

obj.greet(); // "Hello, JavaScript" (this 指向 obj)

// 隐式丢失
const greet = obj.greet;
greet(); // "Hello, undefined" (this 指向全局对象)

3. 显式绑定(Explicit Binding)

使用 callapplybind 方法显式指定 this 的指向。

call

javascript
function greet(greeting, punctuation) {
  console.log(`${greeting}, ${this.name}${punctuation}`);
}

const person = { name: "JavaScript" };

greet.call(person, "Hello", "!"); // "Hello, JavaScript!"

apply

javascript
greet.apply(person, ["Hi", "."]); // "Hi, JavaScript."

// 实际应用:求数组最大值
const numbers = [5, 6, 2, 3, 7];
const max = Math.max.apply(null, numbers);
console.log(max); // 7

bind

javascript
const boundGreet = greet.bind(person);
boundGreet("Hey", "?"); // "Hey, JavaScript?"

// bind 创建的新函数 this 无法改变
const obj2 = { name: "Python" };
boundGreet.call(obj2, "Hello", "!"); // 仍然是 "Hello, JavaScript!"

4. new 绑定(New Binding)

使用 new 操作符调用构造函数时,this 指向新创建的对象。

javascript
function Person(name) {
  this.name = name;
  console.log(this); // Person { name: "JavaScript" }
}

const person = new Person("JavaScript");

绑定优先级

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

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

const obj1 = { name: "obj1" };
const obj2 = { name: "obj2" };

// 隐式绑定
obj1.test = test;
obj1.test(); // "obj1"

// 显式绑定优先级更高
obj1.test.call(obj2); // "obj2"

// new 绑定优先级最高
const boundTest = test.bind(obj1);
const instance = new boundTest(); // undefined (new 创建了新对象)

箭头函数的 this

箭头函数没有自己的 this,它会继承外层作用域的 this

javascript
const obj = {
  name: "JavaScript",
  traditional: function() {
    console.log(this.name); // "JavaScript"
    
    setTimeout(function() {
      console.log(this.name); // undefined (this 指向全局对象)
    }, 100);
  },
  arrow: function() {
    console.log(this.name); // "JavaScript"
    
    setTimeout(() => {
      console.log(this.name); // "JavaScript" (继承外层 this)
    }, 100);
  }
};

obj.traditional();
obj.arrow();

TIP

箭头函数的 this 在定义时确定,无法通过 callapplybind 改变。

常见应用场景

1. 事件处理

javascript
const button = document.querySelector("button");

button.addEventListener("click", function() {
  console.log(this); // button 元素
});

// 箭头函数的情况
button.addEventListener("click", () => {
  console.log(this); // Window 对象(外层作用域的 this)
});

2. 类方法

javascript
class Counter {
  constructor() {
    this.count = 0;
  }
  
  increment() {
    this.count++;
  }
  
  // 错误示例:箭头函数作为类方法
  decrement = () => {
    this.count--;
  }
}

const counter = new Counter();
const increment = counter.increment;
increment(); // TypeError: Cannot read property 'count' of undefined

const decrement = counter.decrement;
decrement(); // 正常工作(箭头函数绑定到实例)

3. 回调函数中的 this

javascript
class DataFetcher {
  constructor(url) {
    this.url = url;
  }
  
  fetch() {
    // 需要保存 this 引用
    const self = this;
    setTimeout(function() {
      console.log(self.url); // 使用保存的引用
    }, 100);
    
    // 或使用箭头函数
    setTimeout(() => {
      console.log(this.url); // 直接使用 this
    }, 100);
    
    // 或使用 bind
    setTimeout(function() {
      console.log(this.url);
    }.bind(this), 100);
  }
}

4. 函数柯里化

javascript
function multiply(a, b) {
  return a * b;
}

// 使用 bind 实现柯里化
const double = multiply.bind(null, 2);
console.log(double(5)); // 10

const triple = multiply.bind(null, 3);
console.log(triple(4)); // 12

特殊场景

数组方法中的 this

javascript
const obj = {
  items: [1, 2, 3],
  multiplier: 2,
  multiply: function() {
    return this.items.map(function(item) {
      return item * this.multiplier; // this 指向全局对象
    });
  },
  multiplyArrow: function() {
    return this.items.map(item => {
      return item * this.multiplier; // this 指向 obj
    });
  }
};

console.log(obj.multiply()); // [NaN, NaN, NaN]
console.log(obj.multiplyArrow()); // [2, 4, 6]

严格模式的影响

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

test();

// 非严格模式
function test2() {
  console.log(this); // Window 对象
}

test2();

总结

  • 默认绑定:独立函数调用,this 指向全局对象(严格模式下为 undefined
  • 隐式绑定:方法调用,this 指向调用对象
  • 显式绑定:使用 callapplybind 指定 this
  • new 绑定:构造函数调用,this 指向新创建的对象
  • 箭头函数:继承外层作用域的 this,无法改变