
嘻道奇闻
- 文章199742
- 阅读14625734
JavaScript中this指向问题与函数调用的正确姿势
哎呦喂!你是不是经常对着屏幕抓狂:"为什么我的this总是指向奇怪的地方?" 别慌,今天咱们就把这个磨人的小妖精扒个底朝天。准备好瓜子饮料,咱们这就开整!
??第一问:this到底是个啥玩意儿???
简单粗暴地说,this就是当前代码执行的上下文对象。不过你品你细品——它就像个变色龙,会随着调用方式变来变去。举个栗子:
javascript复制function showThis() { console.log(this); } // 不同调用方式结果完全不同 ↓ showThis() // 浏览器里是window,Node里是global document.querySelector('button').onclick = showThis // 指向被点击的按钮 new showThis() // 指向新创建的实例对象
看到没?同一个函数,不同的调用姿势,this完全换了三副面孔。咱们得记住啊:??this的值取决于函数被调用的方式,而不是定义的位置??,这点特别容易踩坑。
??灵魂拷问:普通函数调用为啥this会飘???
很多萌新栽在这个场景里:
javascript复制const obj = { name: '小明', sayHi: function() { console.log('我是' + this.name); } } const func = obj.sayHi; func(); // 输出"我是undefined" 就问你懵不懵?
这里的关键在于:??当函数被单独调用时,this默认指向全局对象(浏览器是window)??。所以把方法赋值给变量再调用,上下文就丢了。这时候就要祭出咱们的救星了:
??三大保命绝招??
- ??bind大法??:
const safeFunc = obj.sayHi.bind(obj)
- ??箭头函数??:改写方法定义方式(后面细说)
- ??直接调用??:老老实实
obj.sayHi()
别搞骚操作
??对象方法调用时的玄机??
先看这段经典迷惑代码:
javascript复制const phone = { brand: '华为', show: function() { setTimeout(function() { console.log(this.brand); // 输出undefined!惊不惊喜? }, 100) } }
问题出在setTimeout里的回调函数是普通函数调用,这时候this早就不是phone对象了。解决方法简单到哭:
??两种修正姿势对比??
原始写法 | 修正方案1 | 修正方案2 |
---|---|---|
function() { ... } | arrow function | 提前保存this |
↓ | ↓ | |
() => { console.log(this.brand) } | const self = this |
这里划重点:??箭头函数没有自己的this,它会继承外层作用域的this??,这个特性在异步操作里简直救命!
??构造函数里的this陷阱??
用new操作符的时候,this的指向规则又不一样了:
javascript复制function Person(name) { this.name = name setTimeout(function() { console.log(this.name); // 这里this又变成window了! }, 100) } new Person('老王') // 输出undefined
这时候咱们得用箭头函数或者bind来锁定this:
javascript复制// 正确写法 setTimeout(() => { console.log(this.name); // 现在能正确输出"老王"了 }, 100)
??call/apply/bind三兄弟怎么选???
这三个方法都能显式绑定this,区别在于传参方式:
func.call(context, 参数1, 参数2)
→ 适合确定参数个数func.apply(context, [参数1, 参数2])
→ 适合动态参数func.bind(context)
→ 返回新函数,适合多次调用
举个实战例子:
javascript复制function introduce(lang, year) { console.log(`我会${lang},有${year}年经验`); } introduce.call(null, 'JavaScript', 5) // 直接传参 introduce.apply(null, ['Python', 3]) // 数组传参 const boundFunc = introduce.bind({}, 'Java') // 预设参数 boundFunc(2) // 输出"我会Java,有2年经验"
??个人观点时间??
干了这么多年前端,我发现this的问题大多源于两个坏习惯:
- 在需要明确上下文的地方用普通函数
- 总想着走捷径不绑定this
现在的项目里,我强烈推荐多用箭头函数和class语法。特别是在React/Vue这些框架里,配合现代语法糖能让this的问题减少80%。不过要注意啊,箭头函数不能用作构造函数,这个雷区可别踩!
最后送大家一句话:??理解this的关键不在于死记规则,而在于看清函数被调用的姿势??。下次再遇到this乱跑的情况,先冷静下来看看调用栈,保准你能揪出那个捣蛋鬼!