Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

如何修复Vue中的 “this is undefined” 问题 #204

Open
husky-dot opened this issue Mar 17, 2020 · 1 comment
Open

如何修复Vue中的 “this is undefined” 问题 #204

husky-dot opened this issue Mar 17, 2020 · 1 comment

Comments

@husky-dot
Copy link
Owner

如何修复Vue中的 “this is undefined” 问题

@husky-dot
Copy link
Owner Author

作者:Michael Thiessen
译者:前端小智
来源:techalyst

点赞再看,养成习惯

本文 GitHub https://github.com/qq449245884/xiaozhi 上已经收录,更多往期高赞文章的分类,也整理了很多我的文档,和教程资料。欢迎Star和完善,大家面试可以参照考点复习,希望我们一起有点东西。


当我们使用 Vue 在愉快的开发项目的时候,突然报了一个错误:

this is undefined

别担心,不只有你一个人,我也经常遇到这个问题很多次,接下我们一起来看看如何解决这个问题。

一个可能的原因是混淆了常规函数和箭头函数的用法,如果你遇到这个问题,我猜你用的是箭头函数。如果用常规函数替换箭头函数,它可能会为你修复这个问题。

我们再深入一点,试着理解为什么会这样。

毕竟,知识就是力量,如果知道造成问题的原因,那么我们将来可以避免很多挫败感和时间浪费。

还有一些其它原因可能也会出现此类错误。

  • 使用 fetchaxios 获取数据
  • 使用像 lodashunderscore 这类的库

理解两种主要的函数类型

在 JS 中,我们有两种不同的函数。它们以几乎相同的方式运作,除了它们处理变量的方式不同。

这给新旧Javascript开发人员带来了很多困惑,但是当我们弄懂这个问题时,就很好会有这个困惑。

常规函数

常规函数可以用几种不同的方式定义。

第一种方法在 Vue 组件中较不常见,因为写出来要更长一些:

methods: {
  regularFunction: function() {
    // Do some stuff
  }
}

第二种方法是简写方式,我们也经常使用:

methods: {
  shorthandFunction() {
    // Do some stuff
  }
}

在像这样的常规函数中,this将引用函数的“所有者”。因为我们是在Vue组件上定义它的,所以this指的是Vue组件。

在大多数情况下,我们应该在 Vue 中使用常规函数,特别是在创建时

  • methods
  • computed props
  • watched props

虽然常规函数通常是我们所需要的,但是箭头函数也非常方便。

箭头函数

箭头函数可以更短,更快的编写,因此最近获得了广泛的欢迎。但是,它们在对象上定义方法时并没有太大的不同,就像我们在编写Vue组件时所做的那样。

这是他们在Vue组件上的样子:

methods: {
  arrowFunction: () => {
    // Do some stuff
  }
}

在处理 this 问题时,真正的差异开始发挥作用。

箭头函数采用词法作用域,意味着箭头函数从它的上下文中获取this

如果试图从Vue组件上的箭头函数内部访问 this,将得到一个错误,因为 this 不存在

data() {
  return {
    text: 'This is a message',
  };
},
methods: {
  arrowFunction: () => {
    console.log(this.text);  // ERROR! this is undefined
  }
}

简而言之,尽量避免在Vue组件上使用箭头函数。这将会省去许多头痛和困惑的问题。

有时使用箭头函数是很好的,但这只在不引用this的情况下才有效。

computed: {
  location: () => window.location,
}

现在我们知道两种主要的函数类型,如何正确使用它们?

匿名函数

当我们只需要创建一个函数而不需要从其他任何地方调用它时,匿名函数非常有用。

下面是使用匿名函数的一些场景

  • 使用 axiosfetch 访存数据
  • filtermapreduce等函数方法
  • 在 Vue 方法中的任何地方

来个例子看一下:

// Fetching data
fetch('/getSomeData').then((data) => {
  this.data = data;
});

// Functional methods
const array = [1, 2, 3, 4, 5];
const filtered = array.filter(number => number > 3);
const mapped = array.map(number => number * 2);
const reduced = array.reduce((prev, next) => prev + next);

从示例中可以看到,大多数情况下,当我们创建匿名函数时,使用箭头函数。我们通常使用箭头函数有几个原因

  • 更短、更简洁的语法
  • 改善可读性
  • this 取自父类

在Vue方法中,箭头函数也可以作为匿名函数使用。

等等,我们不是刚发现当我们试图访问 this 时,箭头函数不起作用吗?

这就是区别所在。

当我们在常规函数或简写函数中使用箭头函数时,常规函数将this设置为我们的Vue组件,而箭头函数则不一样。

来个例子:

data() {
  return {
    match: 'This is a message',
  };
},
computed: {
  filteredMessages(messages) {
    console.log(this); // Vue
    
    const filteredMessages = messages.filter(
      // 引用我们的Vue组件
      (message) => message.includes(this.match)
    );
    
    return filteredMessages;
  }
}

filter方法可以访问this.match,因为箭头函数使用的方法与filteredMessages方法使用的上下文相同。 由于此方法是常规函数(而不是箭头函数),因此将其自身的上下文设置为Vue实例。

让我们进一步讨论如何使用axiosfetch来获取数据。

在获取数据时使用正确的函数

如果正在使用fetchaxios获取异步数据,最好使用 PromisePromise喜欢匿名箭头函数,它们也使处理this问题变得容易得多。

如果你正在获取一些数据并想在你的组件上设置它,这是你应该做的正确的方式:

export default {
  data() {
    return {
      dataFromServer: undefined,
    };
  },
  methods: {
    fetchData() {
      fetch('/dataEndpoint')
        .then(data => {
          this.dataFromServer = data;
        })
        .catch(err => console.error(err));
    }
  }
};

请注意,我们如何在 Vue 组件上使用常规函数作为方法,然后在 Promise 内部使用匿名箭头函数

.then(data => {
  this.dataFromServer = data;
})

fetchData()作用域内,我们将this设置为Vue组件,因为它是一个常规函数。由于箭头函数使用外部作用域作为它们自己的作用域,因此箭头函数也将this设置为我们的Vue组件。

这允许我们通过this访问 Vue 组件并更新dataFromServer

但是,如果需要将函数传递帮助库,比如lodashunderscore,该怎么办呢

与 Lodash 或 Underscore 一起使用

假设我们的Vue组件上有一个要使用LodashUnderscore方法。如何防止this is undefine的错误。

如果你用过 React ,你可能见过类似的东西。

这是我们用Vue做的。

created() {
  this.methodToDebounce = _.debounce(this.methodToDebounce, 500);
},
methods: {
  methodToDebounce() {
    // Do some things here
  }
}

就是这样!

我们要做的就是获取函数,将其包装在debounce函数中,然后返回一个内建了debounce的新函数。现在,当我们在Vue组件上调用this.methodToDebounce()时,我们将调用debounced版本。

什么是词法作用域

如前所述,常规函数和箭头函数之间存在差异的主要原因与词法作用域有关。来分析一下它的含义。

首先,作用域是程序中存在变量的任何区域。在Javascript中,window 变量具有全局作用域,它在任何地方都可用。尽管大多数变量被限制在定义它们的函数、它们所属的类或模块中。

其次,单词“词法”仅仅意味着作用域由你如何编写代码决定。某些编程语言只在程序运行时才确定作用域内的内容。这可能会让人很困惑,所以大多数语言都只使用词法作用域。

箭头函数使用词法作用域,而常规函数和简写函数不使用。

这里最棘手的部分是词法作用域如何在函数中影响 this。对于箭头函数,this与外部作用域的this绑定在一起。常规函数的this绑定方式有些奇怪,这就是引入箭头函数的原因,也是为什么大多数人尽可能多地使用箭头函数的原因。

作用域如何在函数中工作

下面是一些示例,它们演示了作用域如何在这两种函数类型之间以不同的方式工作

// 此变量在 window 作用域内
window.value = 'Bound to the window';

const object = {
  // 此变量在 object 作用域内
  value: 'Bound to the object',
  arrowFunction: () => {
    console.log(this.value); // 'Bound to the window'
  },
  regularFunction() {
    console.log(this.value);  // 'Bound to the object'
  }
};

将作用域绑定到函数上

我们可以使用 bind 方法来改变 this 的绑定

const boundFunction = unboundFunction.bind(this);

这使我们在编写Vue组件时具有更大的灵活性,更轻松地重用方法。当然,可读性相对差点,应该尽量避免太频繁地使用它。


代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug

原文:
https://www.techalyst.com/posts/vuejs-this-is-undefined-error-in-javascript-array-filter-and-functions


交流

文章每周持续更新,可以微信搜索「 大迁世界 」第一时间阅读和催更(比博客早一到两篇哟),本文 GitHub https://github.com/qq449245884/xiaozhi 已经收录,整理了很多我的文档,欢迎Star和完善,大家面试可以参照考点复习,另外关注公众号,后台回复福利,即可看到福利,你懂的。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant