这个bug出现在一年前,当时自己大学还没毕业,刚刚进入一家公司实习。那个时候还没有用seajs或者requirejs那样的模块化管理的库,也没有用一个自执行的函数将要执行的代码包裹起来,于是bug就在这样的一个场景下诞生了。当时自己定位了比较久,也不知道status是window下的一个属性,所以请了高手帮忙定位,高手也是定位了半天才定位出来,只是凑巧将status换了一个名字就正常了,后来我问高手原因,他当时也答不出来,后来就一直没管它了,也忘记了。就在前几天,群里有人在讨论一些bug以及要注意的一些坑,我突然想起了一年前自己遇到过的坑,于是提了出来,在各位高手的讨论下终于搞懂了这个bug出现的原因以及原理,于是记录下,方便那些跟我一样做开发的同学能够绕过这些坑,少走弯路。

1.场景再现(为了方便最简化代码,当时的情景不像下面这么直白的提示错误):

    

咦,为什么这里status是一个数组,为什么会提示它没有push函数呢,这到底是为什么呢?这个bug对于当时初出茅庐的我来说简直就是一个大挑战,那个时候对调试还不熟,看到bug那个小心脏顿时就有点受不了了,紧张啊,抓狂啊随之而来。因为当时代码量比较多,所以当时不能一下子定位到这里的问题。

2.讨论

  

  我们看到就因为变量名不同,却一个出错一个正常,难道不能将一个数组赋值给status吗?

  

  我们看到将一个数组赋值给status是完全没有问题的,它是数组类型。既然是数组为什么就没有push方法呢?这个时候我们不防打印下status的类型

  

  我们看到我们将一个数组赋值给status,按理说应该是object类型,可是这里却是string类型,string类型没有push方法,这时我们对于为什么报错就没有那么疑惑了。按这样理解的话,就是在给status赋值时确实是将一个数组赋给了它,但是就是在读取status时浏览器强制将status转化成了字符串。我们不防在chrome控制台看看。

  

  看来我们的猜测是对的,赋值成功,在取值的时候将status强制转化为了字符串,那要是将一个对象赋值给status在读取status的时候是不是也会将status转化为json格式的字符串呢?

  

  我们看到,浏览器并没有按我们的预期将它转化为一个json格式的字符串,而是转化为[object Object]这样的东东,这不是我们经常用来判断变量的类型吗?一般我们会调用Object.prototype.toString.call(变量)来查看变量类型,因为使用typeof太不靠谱。于是我们猜到在将status转化为字符串的时候是调用了toString方法。

  难道status只能是字符串吗?想想status当初设计出来的初衷,它就是为了临时在状态栏展示一些用户信息,所以必须是字符串。这样理解的话就顺理成章了。

  所以我们看到使用status来定义变量是不可行的,除非定义的status是string类型,但是有的人就说,经常用status,没啥问题啊。

  

  看,用得挺好的,妥妥的啊。

  我们再来看另外一种情况。

  

  xx,报错了,咋回事?在项目中,由于我们的疏忽,有时候定义的变量忘记写var关键字都是时常有的事,在一个代码量很庞大的应用中,定位这样的一个bug肯定需要花费不少时间,而且很容易让人抓狂。

  上面那种情况将一个数组赋值给status并调用push方法为啥不出错,这里就涉及到javascript变量作用域的问题了,因为一个自执行函数就是一个作用域,系统在查找这个变量时是先在这个作用域内进行查找,找不到就往上一层作用域中查找,直到作用域的最前端,没有找到就报错提示变量is not defined。这里因为在当前作用域中申明了变量status,所以不会去window环境下去查找status变量,所以是ok的,但是下面这种情况因为没有使用var进行变量的申明,所以status就会成为window下的变量,而status又是window下的一个固有属性,取值的时候只能是string类型,从而没有push方法,最终报错。

  所以,为了不给自己制造那么多麻烦,在定义变量时应该尽量避免使用javascript中的关键字、保留字和window下的固有属性进行命名,这些都是坑,实际项目中应该多注意避免。

  从以上分析中,我们看到全局的status可以设置,但是读取的时候却调用了toSting方法返回了字符串,这里我们可以利用es5提供的Object.defineProperty来模拟一下这种行为。代码如下:

  

var a = {};
Object.defineProperty(a, 'm', (function () {
var _a = 'xx';
return {
get : function () {
return _a.toString();
},
set : function (v) {
_a = v;
}
};
})());

  利用Object.defineProperty方法,可以对一个变量或者属性进行监控,当直接赋值给变量的时候就会调用set方法,当直接读取变量的时候就会将调用tostring方法将变量转化为字符串。

  

  我们看到我们模拟的行为和status默认的行为一模一样。

  一年前遇到的bug今天才豁然开朗,这让我意识到针对任何一个小的bug都不应该放过,而要报着打破沙锅问到底的态度去探究,这样才可以看到别样的风景以及让自己更加专业。

一次关于使用status作为变量引发的bug及思考的更多相关文章

  1. unity3d之public变量引发错误

    public变量引发错误 在vs ide中怎么更改也无效 后来发现public里面的值一直不改变,手动改之.

  2. shell脚本中定义路径变量出现的BUG

    =========================================================================== if 语句中的定义路径变量 引发命令的PATH路 ...

  3. QByteArray引发的bug

    QByteArray引发的bug 在接收UDP数据的函数里,有如下代码片段 if(0x10 == data.size() && 0xCA == (unsigned char)data. ...

  4. Spring 循环引用(一)一个循环依赖引发的 BUG

    Spring 循环引用(一)一个循环依赖引发的 BUG Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html) Spring 循环 ...

  5. 安卓微信overflow-x overflow-y引发的bug

    今天xgo文章图片页上线用微信扫页面发现一个bug,页面可以双击放大缩小. 找了半天原因,发现是图片描述设置了overflow-y引发的bug. 建议在微信场景里满屏显示不能滚动的页面里慎用overf ...

  6. [Linux][C][gcc][tips] 在头文件中定义变量引发的讨论

    概述 本人的原创文章,最先发表在github-Dramalife-note中.转载请注明出处. Define variable(s) in header file referenced by mult ...

  7. 记录一个i变量引发的事故

    概述 近期开发中遇到一个特别的问题,觉得很有必要与你下来.就是由于在开发中一个很小的疏忽,导致了很大的问题,是什么呢? 现象 我的程序突然引发了v8内部的错误,提示都是c++的,如下.程序一启动就直接 ...

  8. [BUG]自己的bug自己解,记一次在变量使用过程引发的bug

    [实现的功能要求]在短信编辑界面,将所有的emoji表情全部插入到编辑区域,其中表情共有5页,每遍历完一页时需要自动翻页重新获取表情并插入,在第5页中只有10个表情 下面先看看这段代码,大家能否看出有 ...

  9. windows环境变量引发的血案

    最近重装了系统,决心使用Anaconda来管理python包和虚拟环境.在完成一系列配置后,运行程序,发现老是报错 D:\Anaconda3\envs\jobnote>python E:\wor ...

随机推荐

  1. UOJ147 斗地主

    题目描述 牛牛最近迷上了一种叫斗地主的扑克游戏.斗地主是一种使用黑桃.红心.梅花.方片的A到K加上大小王的共54张牌来进行的扑克牌游戏.在斗地主中,牌的大小关 系根据牌的数码表示如下:3<4&l ...

  2. 用WebBrowser采集渲染后的HTML页面

    主要用于微信的文章采集,如果用单纯的HttpClient采集,会出现页面无法渲染的情况: 经过研究发觉公众文章html页面是经过渲染之后显示的,那么思路来了,如果在dotnet的环境下,用WebBro ...

  3. Jenkins配置的邮件无法发送的问题

    一.不要使用系统默认的插件,请使用[Extended E-mail Notification] 二.一下的选项要和你发邮件人填写的保持一致

  4. 深入分析ConcurrentHashMap

      术语定义 哈希算法 hash algorithm 是一种将任意内容的输入转换成相同长度输出的加密方式,其输出被称为哈希值. 哈希表 hash table 根据设定的哈希函数H(key)和处理冲突方 ...

  5. 你所知道好玩有趣的 iOS URL schemes 有哪些?

    QQ的url是 mqq:// 微信是weixin:// 淘宝taobao:// 点评dianping:// dianping://search 微博 sinaweibo:// 名片全能王camcard ...

  6. BZOJ2157: 旅游

    传送门 先讲一个悲伤地故事 RunID User Problem Result Memory Time Language Code_Length Submit_Time 1635823 Cydiate ...

  7. shell 中 &&和||的方法

    Shell && 和 || shell 在执行某个命令的时候,会返回一个返回值,该返回值保存在 shell 变量 $? 中.当 $? == 0 时,表示执行成功:当 $? == 1 时 ...

  8. javascript序列化

    JSON.stringify() JSON.parse() toJSON() var person={"cardId":"546651353435"}; con ...

  9. Linux防火墙:iptables禁IP与解封IP常用命令

    在Linux服务器被攻击的时候,有的时候会有几个主力IP.如果能拒绝掉这几个IP的攻击的话,会大大减轻服务器的压力,说不定服务器就能恢复正常了. 在Linux下封停IP,有封杀网段和封杀单个IP两种形 ...

  10. Python MySQLdb在Linux下的快速安装

    在家里windows环境下搞了一次 见   python MySQLdb在windows环境下的快速安装.问题解决方式 http://blog.csdn.NET/wklken/article/deta ...