首页 > 奇闻 > 正文内容

JavaScript中this指向问题与函数调用的正确姿势

奇闻2025-05-19 13:41:26

哎呦喂!你是不是经常对着屏幕抓狂:"为什么我的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)??。所以把方法赋值给变量再调用,上下文就丢了。这时候就要祭出咱们的救星了:

??三大保命绝招??

  1. ??bind大法??:const safeFunc = obj.sayHi.bind(obj)
  2. ??箭头函数??:改写方法定义方式(后面细说)
  3. ??直接调用??:老老实实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的问题大多源于两个坏习惯:

  1. 在需要明确上下文的地方用普通函数
  2. 总想着走捷径不绑定this

现在的项目里,我强烈推荐多用箭头函数和class语法。特别是在React/Vue这些框架里,配合现代语法糖能让this的问题减少80%。不过要注意啊,箭头函数不能用作构造函数,这个雷区可别踩!

最后送大家一句话:??理解this的关键不在于死记规则,而在于看清函数被调用的姿势??。下次再遇到this乱跑的情况,先冷静下来看看调用栈,保准你能揪出那个捣蛋鬼!

搜索