qileilove

          blog已經(jīng)轉(zhuǎn)移至github,大家請?jiān)L問 http://qaseven.github.io/

          一次關(guān)于使用status作為變量引發(fā)的bug及思考

           這個(gè)bug出現(xiàn)在一年前,當(dāng)時(shí)自己大學(xué)還沒畢業(yè),剛剛進(jìn)入一家公司實(shí)習(xí)。那個(gè)時(shí)候還沒有用seajs或者requirejs那樣的模塊化管理的庫,也沒有用一個(gè)自執(zhí)行的函數(shù)將要執(zhí)行的代碼包裹起來,于是bug就在這樣的一個(gè)場景下誕生了。當(dāng)時(shí)自己定位了比較久,也不知道status是window下的一個(gè)屬性,所以請了高手幫忙定位,高手也是定位了半天才定位出來,只是湊巧將status換了一個(gè)名字就正常了,后來我問高手原因,他當(dāng)時(shí)也答不出來,后來就一直沒管它了,也忘記了。就在前幾天,群里有人在討論一些bug以及要注意的一些坑,我突然想起了一年前自己遇到過的坑,于是提了出來,在各位高手的討論下終于搞懂了這個(gè)bug出現(xiàn)的原因以及原理,于是記錄下,方便那些跟我一樣做開發(fā)的同學(xué)能夠繞過這些坑,少走彎路。
            1.場景再現(xiàn)(為了方便最簡化代碼,當(dāng)時(shí)的情景不像下面這么直白的提示錯(cuò)誤):
            咦,為什么這里status是一個(gè)數(shù)組,為什么會(huì)提示它沒有push函數(shù)呢,這到底是為什么呢?這個(gè)bug對于當(dāng)時(shí)初出茅廬的我來說簡直就是一個(gè)大挑戰(zhàn),那個(gè)時(shí)候?qū)φ{(diào)試還不熟,看到bug那個(gè)小心臟頓時(shí)就有點(diǎn)受不了了,緊張啊,抓狂啊隨之而來。因?yàn)楫?dāng)時(shí)代碼量比較多,所以當(dāng)時(shí)不能一下子定位到這里的問題。
            2.討論
            我們看到就因?yàn)樽兞棵煌瑓s一個(gè)出錯(cuò)一個(gè)正常,難道不能將一個(gè)數(shù)組賦值給status嗎?
            我們看到將一個(gè)數(shù)組賦值給status是完全沒有問題的,它是數(shù)組類型。既然是數(shù)組為什么就沒有push方法呢?這個(gè)時(shí)候我們不防打印下status的類型
            我們看到我們將一個(gè)數(shù)組賦值給status,按理說應(yīng)該是object類型,可是這里卻是string類型,string類型沒有push方法,這時(shí)我們對于為什么報(bào)錯(cuò)就沒有那么疑惑了。按這樣理解的話,就是在給status賦值時(shí)確實(shí)是將一個(gè)數(shù)組賦給了它,但是就是在讀取status時(shí)瀏覽器強(qiáng)制將status轉(zhuǎn)化成了字符串。我們不防在chrome控制臺看看。
            看來我們的猜測是對的,賦值成功,在取值的時(shí)候?qū)tatus強(qiáng)制轉(zhuǎn)化為了字符串,那要是將一個(gè)對象賦值給status在讀取status的時(shí)候是不是也會(huì)將status轉(zhuǎn)化為json格式的字符串呢?
            我們看到,瀏覽器并沒有按我們的預(yù)期將它轉(zhuǎn)化為一個(gè)json格式的字符串,而是轉(zhuǎn)化為[object Object]這樣的東東,這不是我們經(jīng)常用來判斷變量的類型嗎?一般我們會(huì)調(diào)用Object.prototype.toString.call(變量)來查看變量類型,因?yàn)槭褂胻ypeof太不靠譜。于是我們猜到在將status轉(zhuǎn)化為字符串的時(shí)候是調(diào)用了toString方法。

           難道status只能是字符串嗎?想想status當(dāng)初設(shè)計(jì)出來的初衷,它就是為了臨時(shí)在狀態(tài)欄展示一些用戶信息,所以必須是字符串。這樣理解的話就順理成章了。
            所以我們看到使用status來定義變量是不可行的,除非定義的status是string類型,但是有的人就說,經(jīng)常用status,沒啥問題啊。
            看,用得挺好的,妥妥的啊。
            我們再來看另外一種情況。
            xx,報(bào)錯(cuò)了,咋回事?在項(xiàng)目中,由于我們的疏忽,有時(shí)候定義的變量忘記寫var關(guān)鍵字都是時(shí)常有的事,在一個(gè)代碼量很龐大的應(yīng)用中,定位這樣的一個(gè)bug肯定需要花費(fèi)不少時(shí)間,而且很容易讓人抓狂。
            上面那種情況將一個(gè)數(shù)組賦值給status并調(diào)用push方法為啥不出錯(cuò),這里就涉及到j(luò)avascript變量作用域的問題了,因?yàn)橐粋€(gè)自執(zhí)行函數(shù)就是一個(gè)作用域,系統(tǒng)在查找這個(gè)變量時(shí)是先在這個(gè)作用域內(nèi)進(jìn)行查找,找不到就往上一層作用域中查找,直到作用域的最前端,沒有找到就報(bào)錯(cuò)提示變量is not defined。這里因?yàn)樵诋?dāng)前作用域中申明了變量status,所以不會(huì)去window環(huán)境下去查找status變量,所以是ok的,但是下面這種情況因?yàn)闆]有使用var進(jìn)行變量的申明,所以status就會(huì)成為window下的變量,而status又是window下的一個(gè)固有屬性,取值的時(shí)候只能是string類型,從而沒有push方法,最終報(bào)錯(cuò)。
            所以,為了不給自己制造那么多麻煩,在定義變量時(shí)應(yīng)該盡量避免使用javascript中的關(guān)鍵字、保留字和window下的固有屬性進(jìn)行命名,這些都是坑,實(shí)際項(xiàng)目中應(yīng)該多注意避免。
            從以上分析中,我們看到全局的status可以設(shè)置,但是讀取的時(shí)候卻調(diào)用了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方法,可以對一個(gè)變量或者屬性進(jìn)行監(jiān)控,當(dāng)直接賦值給變量的時(shí)候就會(huì)調(diào)用set方法,當(dāng)直接讀取變量的時(shí)候就會(huì)將調(diào)用tostring方法將變量轉(zhuǎn)化為字符串。
            我們看到我們模擬的行為和status默認(rèn)的行為一模一樣。
            一年前遇到的bug今天才豁然開朗,這讓我意識到針對任何一個(gè)小的bug都不應(yīng)該放過,而要報(bào)著打破沙鍋問到底的態(tài)度去探究,這樣才可以看到別樣的風(fēng)景以及讓自己更加專業(yè)。

          posted on 2014-11-04 10:23 順其自然EVO 閱讀(241) 評論(0)  編輯  收藏 所屬分類: 測試學(xué)習(xí)專欄

          <2014年11月>
          2627282930311
          2345678
          9101112131415
          16171819202122
          23242526272829
          30123456

          導(dǎo)航

          統(tǒng)計(jì)

          常用鏈接

          留言簿(55)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 浠水县| 枣庄市| 乐山市| 肃南| 金堂县| 玉山县| 香河县| 东莞市| 凤冈县| 平凉市| 连云港市| 桃江县| 石屏县| 腾冲县| 乐山市| 偃师市| 双辽市| 阿尔山市| 邯郸市| 元朗区| 贵港市| 友谊县| 游戏| 邹平县| 峨边| 中江县| 包头市| 南雄市| 保定市| 湖口县| 思茅市| 扶余县| 新干县| 眉山市| 灵武市| 清水河县| 青州市| 嘉峪关市| 恭城| 图木舒克市| 涟源市|