引言:为什么this是前端开发的必考点?

在JavaScript中,this关键字的行为一直是开发者头疼的问题。它不像其他语言中的this总是指向当前实例,而是会根据调用方式动态变化。理解this不仅是前端面试的高频考点,更是写出高质量代码的基础。

经典陷阱案例

const user = {
  name: "张三",
  getName: function() {
    return this.name;
  }
};

// 正常调用
console.log(user.getName()); // "张三"

// 陷阱1:赋值后调用
const getName = user.getName;
console.log(getName()); // undefined(this指向window)

// 陷阱2:回调函数中调用
setTimeout(user.getName, 1000); // undefined(this指向window)

本文将系统讲解this的四大绑定规则、优先级判断、箭头函数特性及实战应用,帮你彻底攻克this难题。

一、this四大绑定规则详解

1.1 默认绑定:独立函数调用

规则:当函数独立调用时,this指向全局对象(浏览器中是window,Node中是global

案例解析

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

// 直接调用(独立函数调用)
foo(); // window

// 嵌套函数独立调用
function outer() {
  function inner() {
    console.log(this); 
  }
  inner(); // window(独立调用)
}
outer();

严格模式差异

'use strict';
function strictFoo() {
  console.log(this); 
}
strictFoo(); // undefined(严格模式下默认绑定失效)

1.2 隐式绑定:对象方法调用

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

案例解析

const obj = {
  name: "隐式绑定",
  foo: function() {
    console.log(this.name);
  }
};

obj.foo(); // "隐式绑定"(this指向obj)

// 多层对象嵌套
const parent = {
  name: "parent",
  child: {
    name: "child",
    foo: function() {
      console.log(this.name); 
    }
  }
};
parent.child.foo(); // "child"(this指向直接调用对象child)

隐式丢失陷阱

// 情况1:方法赋值给变量
const bar = obj.foo;
bar(); // undefined(this指向window)

// 情况2:作为回调函数传递
setTimeout(obj.foo, 1000); // undefined(this指向window)

1.3 显式绑定:call/apply/bind

规则:通过call/apply/bind方法强制绑定this指向

方法对比

方法 语法 特点
call func.call(thisArg, arg1, arg2…) 参数列表传递
apply func.apply(thisArg, [argsArray]) 数组传递参数
bind func.bind(thisArg, arg1…) 返回新函数,不立即执行

案例解析

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

const obj = { name: "显式绑定" };

// call调用
foo.call(obj); // "显式绑定"

// apply调用(适合数组参数)
foo.apply(obj, [1, 2]); 

// bind绑定(适合需要延迟执行的场景)
const boundFoo = foo.bind(obj);
boundFoo(); // "显式绑定"

实用技巧:解决回调函数this丢失

// 原代码(this丢失)
setTimeout(obj.foo, 1000); 

// 改进1:使用bind
setTimeout(obj.foo.bind(obj), 1000); 

// 改进2:箭头函数(后续讲解)
setTimeout(() => obj.foo(), 1000);

1.4 new绑定:构造函数调用

规则:通过new关键字调用构造函数时,this指向新创建的实例对象

new操作的底层流程

  1. 创建新对象
  2. 新对象原型连接到构造函数prototype
  3. this绑定到新对象
  4. 执行构造函数代码
  5. 返回新对象(若构造函数无返回值)

案例解析

function Person(name) {
  this.name = name; // this指向新实例
}

const person = new Person("new绑定");
console.log(person.name); // "new绑定"

二、优先级与特殊场景处理

2.1 绑定规则优先级

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

验证案例

// 显式绑定 vs 隐式绑定
const obj1 = { name: "obj1", foo: foo };
const obj2 = { name: "obj2" };
obj1.foo.call(obj2); // "obj2"(显式绑定优先级更高)

// new绑定 vs 显式绑定
const boundFoo = foo.bind(obj2);
new boundFoo(); // Person {}(new绑定优先级更高)

记忆口诀:“新显隐默”(new > 显式 > 隐式 > 默认)

2.2 特殊绑定场景

场景1:忽略显式绑定

当显式绑定传入null/undefined时,会使用默认绑定

foo.call(null); // window
foo.apply(undefined); // window
场景2:间接引用

函数的间接引用会导致默认绑定

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

obj2.foo = obj1.foo;
obj2.foo(); // "obj2"(隐式绑定)
(obj2.foo = obj1.foo)(); // window(间接引用导致默认绑定)
场景3:DOM事件绑定

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

const button = document.querySelector("button");
button.onclick = function() {
  console.log(this); // <button>元素
};

三、箭头函数:this绑定的现代解决方案

3.1 箭头函数的this特性

核心特点:箭头函数没有自己的this,它的this继承自外层作用域

对比普通函数

// 普通函数:this动态绑定
const obj = {
  foo: function() {
    setTimeout(function() {
      console.log(this); // window(默认绑定)
    }, 1000);
  }
};

// 箭头函数:this继承自外层作用域
const obj = {
  foo: function() {
    setTimeout(() => {
      console.log(this); // obj(继承foo的this)
    }, 1000);
  }
};
obj.foo();

3.2 箭头函数适用场景

场景1:回调函数中的this绑定
// 定时器回调
setTimeout(() => {
  console.log(this); // 继承外层this
}, 1000);

// 数组方法回调
const arr = [1, 2, 3];
arr.forEach(item => {
  console.log(this); // 继承外层this
});
场景2:简洁的函数表达式
// 简化回调函数
const sum = (a, b) => a + b;

// 简化对象方法(不推荐,见3.3)
const obj = {
  name: "箭头函数",
  getName: () => this.name // 注意:此处this指向外层作用域
};

3.3 箭头函数使用陷阱

陷阱1:不能作为构造函数

const Person = (name) => {
  this.name = name;
};
new Person("箭头函数"); // 报错:Person is not a constructor

陷阱2:对象方法中的this指向

const obj = {
  name: "obj",
  getName: () => {
    return this.name; // this指向window
  }
};
obj.getName(); // undefined

最佳实践

  • 优先使用箭头函数作为回调函数
  • 避免使用箭头函数作为对象方法构造函数

四、实战篇:面试题解析与最佳实践

4.1 经典面试题深度解析

题目1:this绑定优先级
var name = "window";
const obj1 = {
  name: "obj1",
  foo: function() {
    console.log(this.name);
  }
};
const obj2 = { name: "obj2" };

obj1.foo.call(obj2); // "obj2"(显式绑定优先于隐式绑定)
题目2:箭头函数this继承
var name = "window";
const obj = {
  name: "obj",
  foo: function() {
    return () => {
      console.log(this.name);
    };
  }
};
const bar = obj.foo();
bar.call({ name: "bar" }); // "obj"(箭头函数this不可变,继承自foo的this)
题目3:复杂嵌套场景
var name = "window";
function Person(name) {
  this.name = name;
  this.foo = function() {
    console.log(this.name);
  };
  this.bar = () => {
    console.log(this.name);
  };
}

const person1 = new Person("person1");
const person2 = new Person("person2");

person1.foo.call(person2); // "person2"(显式绑定生效)
person1.bar.call(person2); // "person1"(箭头函数this不可变)

4.2 开发最佳实践

实践1:固定this的三种方式
// 方式1:bind绑定
function Timer() {
  this.seconds = 0;
  setInterval(this.tick.bind(this), 1000);
}
Timer.prototype.tick = function() {
  this.seconds++;
};

// 方式2:箭头函数
function Timer() {
  this.seconds = 0;
  setInterval(() => this.tick(), 1000);
}
Timer.prototype.tick = function() {
  this.seconds++;
};

// 方式3:保存this到变量
function Timer() {
  const self = this;
  this.seconds = 0;
  setInterval(function() {
    self.tick();
  }, 1000);
}
实践2:React中的this绑定
class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    // 方式1:构造函数中bind
    this.handleClick = this.handleClick.bind(this);
  }
  
  // 方式2:箭头函数class属性(推荐)
  handleClick = () => {
    console.log(this.state);
  };
  
  render() {
    return <button onClick={this.handleClick}>Click me</button>;
  }
}
实践3:Vue中的this使用
export default {
  data() {
    return {
      message: "Vue this"
    };
  },
  methods: {
    showMessage() {
      // Vue方法中this指向组件实例
      console.log(this.message);
      
      // 定时器中使用箭头函数
      setTimeout(() => {
        console.log(this.message); // this继承自外层方法
      }, 1000);
    }
  }
};

五、总结与升华

5.1 this绑定规则思维导图

this绑定规则
├── 四大基本规则
│   ├── new绑定:new 构造函数 → 实例对象
│   ├── 显式绑定:call/apply/bind → 指定对象
│   ├── 隐式绑定:对象.方法() → 调用对象
│   └── 默认绑定:独立调用 → window/undefined
├── 优先级:new > 显式 > 隐式 > 默认
└── 特殊情况
    ├── 箭头函数:继承外层作用域this
    ├── 显式绑定null/undefined:使用默认绑定
    └── DOM事件:this指向事件元素

5.2 前端框架中的this应用

  • React:class组件this需绑定,函数组件通过hooks避免this
  • Vue:methods中this指向组件实例,箭头函数会导致this丢失
  • Node:模块顶级this指向module.exports,函数中this指向global

5.3 学习建议

  1. 理解本质:this是执行上下文的属性,由调用方式决定
  2. 多做练习:通过 debugger 观察this指向
  3. 总结规律:记住"新显隐默"优先级口诀
  4. 现代实践:优先使用箭头函数处理回调函数this问题

掌握this不仅能解决实际开发问题,更能深入理解JavaScript的执行机制。希望本文能帮你彻底攻克this难关!

ps:如果喜欢这篇文章,欢迎关注微信公众号 01漫游者,获取更多精彩内容,期待与你相遇!

Logo

葡萄城是专业的软件开发技术和低代码平台提供商,聚焦软件开发技术,以“赋能开发者”为使命,致力于通过表格控件、低代码和BI等各类软件开发工具和服务

更多推荐