[技术博客] 敏捷软工——JavaScript踩坑记

一、一个令人影响深刻的坑

1.脚本语言的面向对象

面向对象特性是现代编程语言的基本特性,JavaScript中当然集成了面向对象特性。但是JavaScript作为脚本语言并没有严格地遵守面向对象的规则,而是兼容了多种语言特性。

  • 面向对象编程
  • 命令式编程
  • 函数式编程

可想而知,在兼容这些特性之后JavaScript会有很多奇特的行为,而对面向对象的固有认识会使我们在编写JavaScript时犯下无数错误。

2. 忘记过去,从新学习

C++和Java是我首先接触到的现代编程语言,它们已将面向对象范式固定到我心中。所以我在学习JavaScript时自然受到C++和Java的影响,将这两种语言的很多编程习惯迁入到JavaScript中。相应地无数的bug接踵而至,耗费了我大量的时间调试修复这些bug。这里将我踩过的坑记录下来,一是提醒自己注意JavaScript的语言特性,二是告诉自己不能因为熟悉就粗心、防止“淹死的都是会水的”发生在自己身上。

在用JavaScript实现前端与后端通讯的功能时,遇到了一个奇怪的bug。下面先将有关地源码展现出来。

checkThePath: function() {
// console.log('http://' + window.location.host + '/classrooms/auto_test_projects/validate_runner');
axios.get('http://' + window.location.host + '/classrooms/auto_test_projects/validate_runner', {
params: {
path: this.path
}
}).then(function(res) {
let data = res.data;
// console.log(data);
if (data.key === 'agile soft engineering') {
console.log('arrive init runner state');
this.runner_state.state = 1;
this.runner_state.id = data.id;
this.runner_state.os = data.os;
this.$emit('available', {
uid: this.runner_state.id,
os: this.runner_state.os,
path: this.path
});
}
}
).catch((err) => {
console.log('Error: happen in checking runner.');
console.log(err);
});
this.show_check = true;
}
}

当这段代码被执行的时候,游览器控制台报出了错误:undefined not have property runner_state。在一阵调试输出之后,我发现问题出在匿名函数中,匿名函数中的this指向的对象是undefined

这个bug是如何产生的呢?原因在于匿名函数。JavaScript中有两种编译模式,一种为sloppy 模式,也即马虎模式;而另一种为strict模式,也即严格模式。JavaScript在这两种模式下有不同的行为,对于this的解释同样如此。

  • 在一般函数中,马虎模式下this被解释为global object,而在严格模式下this被解释为undefined
  • 在构造函数中,this被解释为新创建的对象。
  • 在对象方法中,this被解释为方法调用消息的接收者。

原来在匿名函数之中this会被重新解释,即使该匿名函数在对象方法中被定义。可能Vue框架默认使用严格模式,this被解释为了undefined

找到了问题的原因,解决办法在哪里呢?我们的目标是在传入.then方法的代码块中同样能够访问到调用对象的属性,那么就在一下几种可能的解决方法。

  • 将调用对象或调用对象的属性作为参数传入匿名函数中。
  • 改变传入代码块的方式,放弃使用匿名函数,从而使在代码块中this被解释为调用对象。

第一种解决方法很容易实现,但会使得代码变得雍仲;第二种方法则更加符合简洁的编码原则。JavaScript提供了一种方便的机制使得(实际上是另一种函数声明方法),箭头函数下的的this解释等与匿名函数不尽相同。这个机制的名称为“箭头函数”,顾名思义,该函数的定义语句中箭头发挥了很大的作用。箭头函数主要有两大作用:更简短的函数并且不绑定this

箭头函数中的this与上一层作用域中的this相同,这一点完美地符合我们的目标。那么,我们现在可以将代码修改为如下形式。

checkThePath: function() {
// console.log('http://' + window.location.host + '/classrooms/auto_test_projects/validate_runner');
axios.get('http://' + window.location.host + '/classrooms/auto_test_projects/validate_runner', {
params: {
path: this.path
}
}).then((res) => {
let data = res.data;
// console.log(data);
if (data.key === 'agile soft engineering') {
console.log('arrive init runner state');
this.runner_state.state = 1;
this.runner_state.id = data.id;
this.runner_state.os = data.os;
this.$emit('available', {
uid: this.runner_state.id,
os: this.runner_state.os,
path: this.path
});
}
}
).catch((err) => {
console.log('Error: happen in checking runner.');
console.log(err);
});
this.show_check = true;
}
}

编译、运行,可以看到我们期待的现象成功出现:runner_state被配置,available事件被抛向父级组件。借助解决这个坑,我们认识到了JavaScript中解释this的机制,同样学习到了JavaScript中两大定义函数的方式。

二、JavaScript中this的全貌

那么,JavaScript中this都会被如何解释?实际上有这几个场景:this在函数中、this在顶层作用域、this在传入eval的字符串中。

1.this在函数中

第一部分我们已经对这部分进行了详细的介绍,所以在只简单的复述一下this在函数中的解释情况。

  • 在一般函数中,马虎模式下this被解释为global object,而在严格模式下this被解释为undefined
  • 在构造函数中,this被解释为新创建的对象。
  • 在对象方法中,this被解释为方法调用消息的接收者。

2.this在顶层作用域中

JavaScript作为脚本语言允许在顶层作用域执行代码,那么自然的this关键字也可以在顶层作用域被使用。

  • 在游览器中,顶层作用域中的this被解释为global object
  • Node.js框架中,顶层作用域中的this被解释为module.scope。这是因为我们一般在module中执行代码,所以“顶层作用域”在里应该是module的作用域。

3.this在传入eval的字符串中

  • 如果eval被直接调用,eval中的thiseval方法所属作用域中的this相同。
  • 如果eval被间接调用,eval中的this被设置为global object

三、另一个令人影响深刻的坑

switch (this.option) {
case 'Issues':
contribution_data = this.members.map(i => {
return
{
name: i.name,
value: i.issues_count
}
});
contribution_data.push({name: 'To Do', value: this.todo});
break;
case 'Weight':
contribution_data = this.members.map(i => {
return
{
name: i.name,
value: i.issues_weight
}
});
contribution_data.push({name: 'To Do', value: this.todoWeight});
break;
default:
contribution_data = this.members.map(i => {
return
{
name: i.name,
value: i.commits_count
}
});
}

作为一个花括号换行党,我在学习JavaScript时保留了最后的尊严:构造Object时将花括号换行,以工整的显示出Object的结构。这个倔强坚持了300行代码就被JavaScript无情的击碎了:我遇到了JavaScript的经典bug——封号自动补全与return。

写完示例代码,访问网页!欸欸欸?我的网页去哪了?怎么什么都没有显示?

不怕!赶紧打开chrome的控制台。

???怎么这么多报错,我看看。

好多undifined啊,我裂开。

折腾了两小时≧ ﹏ ≦,发现死于JavaScript的封号自动补全机制。

将示例代码这样修改就OK了!

switch (this.option) {
case 'Issues':
contribution_data = this.members.map(i => {
return {
name: i.name,
value: i.issues_count
}
});
contribution_data.push({name: 'To Do', value: this.todo});
break;
case 'Weight':
contribution_data = this.members.map(i => {
return {
name: i.name,
value: i.issues_weight
}
});
contribution_data.push({name: 'To Do', value: this.todoWeight});
break;
default:
contribution_data = this.members.map(i => {
return {
name: i.name,
value: i.commits_count
}
});
}

集由这个坑,我打开了新世界的大门:JavaScript的封号自动补全机制,顺便还在知乎上围观了JavaScript编程要不要在行尾的论战。

总而言之,JavaScript会给诸如break, continue, return等关键字以“特殊待遇”。如果它们之后紧接着换行符,那么JavaScript会在行尾自动补上一个封号。我可爱的工整的对象定义就是被这样抛弃的  ̄へ ̄。

[技术博客] 敏捷软工——JavaScript踩坑记的更多相关文章

  1. [敏捷软工团队博客]Beta阶段项目展示

    团队成员简介和个人博客地址 头像 姓名 博客园名称 自我介绍 PM 测试 前端 后端 dzx 秃头院的大闸蟹 大闸蟹是1706菜市场里无菜可卖的底层水货.大闸蟹喜欢音乐(但可惜不会),喜欢lol(可惜 ...

  2. 【软工】[技术博客] 用Monaco Editor打造接近vscode体验的浏览器IDE

    [技术博客] 用Monaco Editor打造接近vscode体验的浏览器IDE 官方文档与重要参考资料 官方demo 官方API调用样例 Playground 官方API Doc,但其搜索框不支持模 ...

  3. [技术博客] 软工-Ruby on Rails 后端开发总结分享

    [技术博客] 软工-Ruby on Rails 后端开发总结分享 在这次软件编写中,我们的后端使用了Ruby on Rails (RoR)框架. Rails框架是用Ruby编写的.这意味着当我们为Ru ...

  4. [敏捷软工团队博客]项目介绍 & 需求分析 & 发布预测

    项目 内容 2020春季计算机学院软件工程(罗杰 任健) 博客园班级博客 作业要求 团队项目选择 我们在这个课程的目标是 在团队合作中锻炼自己 这个作业在哪个具体方面帮助我们实现目标 了解项目整体情况 ...

  5. [福大软工] Z班——个人技术博客评分

    个人技术博客 作业地址 https://edu.cnblogs.com/campus/fzu/SoftwareEngineering2015/homework/1070 作业要求 个人技术博客单次作业 ...

  6. [技术博客]iview组件样式踩坑记录

    [技术博客]iview组件样式踩坑记录 iview官方文档. 在本次项目开发中,前端项目主要使用vue框架+iview组件构建,其中iview组件在使用过程中遇到了许多官方文档中没有明确说明或是很难注 ...

  7. 2021北航敏捷软工Beta阶段评分与总结

    概述 Beta 阶段评分,按照之前的规则,主要组成部分为: 博客部分,基于 Beta 阶段博客的评分(每篇正规博客 10 分,每篇 Scrum5 分,评定方式类比往年) 评审部分,基于 Beta 阶段 ...

  8. 50家硅谷IT公司技术博客

    分享一下 50 家硅谷优秀 IT 公司技术博客,从中可以了解企业文化,技术特色和设计语言,如果直接列出来很单调,加上点评,算吐槽版吧. 知名大厂   1. Facebook https://www.f ...

  9. Vue + TypeScript + Element 搭建简洁时尚的博客网站及踩坑记

    前言 本文讲解如何在 Vue 项目中使用 TypeScript 来搭建并开发项目,并在此过程中踩过的坑 . TypeScript 具有类型系统,且是 JavaScript 的超集,TypeScript ...

随机推荐

  1. Hive的分桶表

    [分桶概述] Hive表分区的实质是分目录(将超大表的数据按指定标准细分到指定目录),且分区的字段不属于Hive表中存在的字段:分桶的实质是分文件(将超大文件的数据按指定标准细分到分桶文件),且分桶的 ...

  2. Spring系列之集成MongoDB的2种方法

    MongoDB是最流行的NoSQL数据库,SpringBoot是使用Spring的最佳实践.今天带大家讲一讲SpringBoot集成MongoDB的两种方式,MongoDB的安装自行去官网查询,本地开 ...

  3. Mybatis-基本学习(下)

    四,MAP的使用--超常用 思考:多表连接查询怎么做?---MAP的好处!---返回List

  4. git换行符自动转换导致整个文件被修改的解决方案

    不少开发者可能遇到过这个问题:从git上拉取服务端代码,然后只修改了一处地方,准备提交时,用diff软件查看,却发现整个文件都被修改了.这是git自动转换换行符导致的问题. 原因 不同操作系统使用的换 ...

  5. matlab函数randperm()

    randperm()会返回一个行向量. 1,randperm(n) 输出一个1×n的矩阵,元素值为1~n的整数,每个元素只出现一次,元素的顺序是随机的. 2,randperm(n,k) 输出一个1×k ...

  6. Dapr + .NET Core实战(四)发布和订阅

    什么是发布-订阅 发布订阅是一种众所周知并被广泛使用的消息传送模式,常用在微服务架构的服务间通信,高并发削峰等情况.但是不同的消息中间件之间存在细微的差异,项目使用不同的产品需要实现不同的实现类,虽然 ...

  7. Mybatis中使用级联查询,一对多的查询

    一.需求描述 自己在开发一个小程序的过程中,需要做的一个查询是稍微比较复杂的查询,根据用户信息去查询用户所对应的宠物信息. 一个用户可能对应多个宠物,所以在用户和宠物信息的对应关系就是一对多的关系. ...

  8. 安卓模拟器genymotion安装

    上一篇已经讲了appium的搭建.那么搭建好后,我们需要测试不同机型,这个时候除了真机外,可以选择安装模拟器.市面上的模拟器有很多:夜神.逍遥.mumu.android emulator.genymo ...

  9. 【Vue】淘气三千问之 data为什么是函数而不是对象?这河狸吗

    朋友,当你提出以上问题的时候建议你先去复习下原型链的知识 但是我好人做到底直接就讲了吧,我们先看一下下面的这段代码: function Component () { this.data = this. ...

  10. 项目部署(ubuntu+uwsgi+nginx+supervisor+django)

    一.在开发机上的准备工作 1. 确认项目没有bug. 2.设置`ALLOW_HOST`为你的域名,以及ip地址. 4.设置`DEBUG=False`,避免如果你的网站产生错误,而将错误信息暴漏给用户. ...