问题提出
在前端开发过程中,如果函数嵌套过多,非常容易出现this的上下文隐式丢失的问题。例如下面的代码,代码中的html部分请见附录。
<script>
export default {
name: "listapp",
data() {
return {
studentList: []
};
},
mounted: function() {
this.$axios
.get("../json/StudentList.json")
.then(function(resp) {
this.studentList = resp.data.students;
});
}
};
</script>
上面的代码在前端基于Vue的项目开发过程中非常常见,我们通常用axios进行异步方法调用,以获取服务器端数据。然后在Promise的then方法中,把获取到的数据渲染到页面上。
在数据渲染过程中,我们会发现在then方法中,this不再指向当前Vue对象。所以很遗憾,这段代码执行后,是不能把学生信息显示到表格里面的。
因为在then的匿名回调函数中,this并不能指向Vue对象,也就无法对界面进行渲染。
this.studentList = resp.data.students;
解决方法
针对上述问题,我提出了三种解决方案。
- 词法作用域法,在this上下文绑定丢失前,进行备份。在then方法中通过使用备份this,进行页面渲染。代码如下:
mounted: function() {
let self = this; //在this绑定丢失前,备份this
this.$axios
.get("../json/StudentList.json")
.then(function(resp) {
self.studentList = resp.data.students;
/* seft */
});
}
- 基于ES5的bind方法,将回调方法中的this强制绑定到当前Vue对象。代码如下:
mounted: function() {
this.$axios
.get("../json/StudentList.json")
.then(function(resp) {
this.studentList = resp.data.students;
}.bind(this)); // bind方法可以生成新的回调函数,将该函数绑定当前Vue对象。
}
-
基于ES6的解决方法有两个,首先是箭头函数,其次是async/await。
箭头函数的this指向是函数被创建时绑定的,它的指向就是当前词法作用域中的this,与调用方式无关,this不会因为调用者而改变。也就不存在this绑定的隐式消失问题。代码如下:
mounted: function() {
this.$axios
.get("../json/StudentList.json")
.then(resp => {
this.studentList = resp.data.students;
});
}
async/await是ES6中新的异步处理方式,无需再使用回调函数,也就不存在函数嵌套,也就没有this绑定的隐式消失问题。
mounted: async function() {
let resp = this.$axios.get("../json/StudentList.json");
this.studentList = resp.data.students;
}
分析
this绑定的隐式消失问题,存在于ES5及之前的Javascript版本中,在ES6中彻底得到解决。因此个人觉得在复杂的前端逻辑中,基于ES6的解决方式更加简单易用,同时也更符合前端的开发潮流。当然,如果需要兼容低版本的浏览器,前两个解决方案也可以在项目中使用。
附录
示例程序中的html部分:
<template>
<div class="container">
<table>
<thead>
<tr>
<th>StudentNo</th>
<th>Name</th>
<th>Address</th>
</tr>
</thead>
<tbody>
<tr v-for="item in studentList">
<td>{{item.Id}}</td>
<td>{{item.Name}}</td>
<td>{{item.Name}}</td>
</tr>
</body>
</table>
</div>
</template>