js中的
百科
2026-10-17 04:46:19
0
JavaScript里最容易被低估的东西,大概就是`this`了。很多人写了两三年代码,一碰到`this`就头皮发麻,然后干脆用箭头函数一梭子解决问题。但箭头函数不是万能药,它只是把`this`藏起来了,并没有让你真正理解它。 先看一个最基础的场景。你写了个对象方法: ```javascript const user = { name: '张三', greet() { console.log(`你好,我是${ this.name}`); } }; user.greet(); // 你好,我是张三 ``` 这没问题。但如果你把这个方法拆出来单独调用呢? ```javascript const fn = user.greet; fn(); // 你好,我是undefined ``` `this`丢了。为什么?因为`this`的指向取决于**调用方式**,而不是定义位置。`user.greet()`是“通过user来调用”,所以`this`指向user;`fn()`是裸调用,在非严格模式下`this`指向全局对象,严格模式下是`undefined`。 这就是`this`的核心规则:**谁调用了它,它就指向谁**。听起来简单,但实际场景里坑特别多。 比如DOM事件。你写了个按钮点击: ```javascript button.addEventListener('click', function() { console.log(this); // 指向button元素 }); ``` 这里`this`是button,因为浏览器内部是这么调用的:`button.handleEvent(event)`。但如果你在回调里用了箭头函数: ```javascript button.addEventListener('click', () => { console.log(this); // 指向外层作用域的this }); ``` 箭头函数不绑定自己的`this`,它会“继承”定义时所在作用域的`this`。这特性在某些场景下很爽,比如React类组件里: ```javascript class Counter extends React.Component { constructor() { super(); this.state = { count: 0 }; } increment = () => { this.setState({ count: this.state.count + 1 }); }; render() { return ; } } ``` 这里用箭头函数定义类属性,`this`永远指向组件实例,省去了在constructor里bind的麻烦。但反过来,如果你在对象方法里用箭头函数,可能就出问题了: ```javascript const counter = { count: 0, increment: () => { this.count++; // this不指向counter! } }; counter.increment(); // NaN,因为this是外层作用域 ``` 这种写法纯粹是自找麻烦。 再说一个我实际踩过的坑。有一次写一个数据处理的工具函数,内部有个回调需要访问外层对象的属性: ```javascript function processData(data) { const config = { multiplier: 3 }; data.forEach(function(item) { console.log(item * this.multiplier); // this是undefined }); } ``` `forEach`的回调里,`this`默认指向undefined(严格模式)。我当时第一反应是用`bind`: ```javascript data.forEach(function(item) { console.log(item * this.multiplier); }.bind(config)); ``` 但后来发现更简洁的做法是保存外层`this`: ```javascript function processData(data) { const config = { multiplier: 3 }; const self = this; // 或者用that data.forEach(function(item) { console.log(item * config.multiplier); // 直接引用config也行 }); } ``` 实际上,如果你不需要动态改变`this`,直接用闭包引用变量更清晰。`this`机制的存在意义在于**复用**——同一个函数,在不同调用者下表现不同。比如: ```javascript function introduce() { console.log(`我是${ this.name}`); } const person1 = { name: '李四', intro: introduce }; const person2 = { name: '王五', intro: introduce }; person1.intro(); // 我是李四 person2.intro(); // 我是王五 ``` 这比写两个几乎一样的函数优雅多了。 最后说说`call`、`apply`和`bind`。它们能显式指定`this`。`call`和`apply`的区别只是传参方式:`fn.call(obj, 1, 2)` vs `fn.apply(obj, [1, 2])`。`bind`则返回一个新函数,永久绑定`this`: ```javascript const logName = function() { console.log(this.name); }.bind({ name: '临时对象' }); logName(); // 临时对象 ``` 但注意,`bind`只能绑一次。你再bind别的也没用: ```javascript const boundAgain = logName.bind({ name: '另一个' }); boundAgain(); // 还是输出“临时对象” ``` `this`的坑,说到底就是“调用时才知道指向谁”这个特性带来的。理解它,比背规则有用。下次遇到`this`不对,别急着换箭头函数,先想想:这个函数是怎么被调用的?谁在调用它?搞清楚了,问题自然就解决了。