最近這些年在對 JavaScript 進行考古時,發現網景時代的 JavaScipt 實現,存在一些不爲人知的特性,我從中挑選幾個有趣的說一下。html
在 JavaScript 1.0 中,eval 和如今同樣,只是個全局函數。在 JavaScript 1.1 中,eval 還變成了全部對象的一個共用方法:c#
var foo = 1 var bar = 2 o = new Object o.foo = 10 o.bar = 20 eval("this.foo + bar") // 3 o.eval("this.foo + bar") // 30
官方文檔描述這個 eval 方法是說它能讓一段代碼字符串在執行時以當前對象爲上下文:數組
The eval method evaluates a string of JavaScript code in the context of the specified object瀏覽器
描述很籠統,其實這個 eval 方法和普通的 eval 函數的區別有兩點:jsp
1. this 指向當前對象而不是全局對象函數
2. 相似 with 語句的做用,全部變量優先做爲當前對象的屬性去查找this
該方法在隨後的 JavaScript 1.2 中被廢棄。lua
Array() 構造函數實現於 JavaScirpt 1.1 中,和如今同樣,當時就已經對單數字參數的狀況作了特殊處理:spa
Array(1, 2, 3, 4, 5) // 包含五個元素 一、二、三、四、5 的數組 Array(1, 2, 3) // 包含三個元素 一、二、3 的數組 Array(5) // 包含五個空元素的數組
爲了避免讓用戶踩這個坑,ES6 裏還專門新增了一個 API:Array.of()。 其實不是到了 ES6 時代才發現這是個坑的,98 年就已經發現了。在 JavaScript 1.2 中,嘗試去除了對單數字參數的特殊處理:prototype
Array(5) // 只包含一個元素 5 的數組
但並無被 ES 規範採納,以後 JavaScript 1.3 回滾了這個改動,直到今日仍如此。
在 JavaScript 中如何對兩個值進行相等性判斷毫不是一個簡單的話題,MDN 上甚至有一篇專門的文檔來說解。判斷等仍是不等爲啥這麼複雜,其中很大的一個緣由就是,JavaScirpt 裏有兩套相等運算符:==/!= 和 ===/!==。
既然如今都推薦用嚴格相等 ===,不讓用 ==,那爲啥當初不把 == 實現成嚴格相等呢。其實網景在 98 年的時候就已經發現非嚴格相等是個坑了,在 JavaScript 1.2 中,他們嘗試把 == 的強制類型轉換步驟去掉,讓它表現的就像如今的 === 同樣:
1 == "1" // false
不過在次年發佈的 ES3 裏,決定採用新增 === 運算符的方案,因此只好回滾了上面這個改動。
if、while 語句都接受一個表達式做爲是否要執行後面代碼塊的判斷條件,好比 if ( a == b)。可是不少時候人們會因手誤漏掉個 =,成了 if (a = b),致使很難發現的 bug。
在 JavaScript 1.0 中,JS 引擎會認爲你是不當心漏掉了一個 =,它會替你補上,並會發出警告信息。若是你的本意真的是想使用賦值語句(的確有人想要這麼寫),那就給賦值語句加一個額外的括號:if((a = b))。
以前我問過 Brendan Eich 本人,這個特性是他從 GCC 抄來的。我看了一下如今留存最先的 JavaScript 1.4 的代碼,實現該特性的代碼還在,只不過其實從 1.3 開始,爲了兼容 ES 規範,這段代碼實際已經不會執行了。
如今咱們知道,全部對象都是真值,即使是 false 的包裝對象,但在 JavaScript 1.3 以前,它是個假值:
if (new Boolean(false)) { // 不會執行 }
我在arguments 對象的老歷史中講過這個,在 JavaScript 1.1 和 1.2 中存在的特性,全部本地變量包括形參的名字都會成爲 arguments 對象的屬性:
function foo(a, b) { var c = 3 alert(arguments.a) // 1 alert(arguments.b) // 2 alert(arguments.c) // 3 } foo(1, 2)
如今咱們熟知,用 Object.prototype.toString 能夠判斷對象類型,好比它會返回 [object Object]。也許認爲這個特性沒啥用,在 JavaScript 1.2 中,它被改爲了返回對象的源碼形式,好比:
var o = {a:1} o.toString() // "{a:1}"
Array.prototype.toString 也和如今不同:
var arr = [1, 2, 3] arr.toString() // "[1, 2, 3]",而不是 "1,2,3"
在 1.3 中,這個方法從新起了個名字叫 toSource(Firefox 中至今存在),toString 迴歸到原來的功能。
如今咱們經常使用 obj.foo && obj.foo.bar 和 obj.foo || obj.bar 這種寫法,可是在 JavaScript 1.2 以前,&& 和 || 有個 bug:
0 && 1 // 返回 false,而不是 0 1 || 0 // 返回 true,而不是 1
之因此說 bug,而不是有意設計,是由於反過來就和如今的表現同樣了:
1 && 0 // 0 0 || 1 // 1
若是傳入 split 方法的分隔符是單個空格,那麼會進行一個特殊處理:先 trim 掉字符串兩邊的空白符(" \t\r\n"),而後以若干個連續的空白符做爲分隔符去切割這個字符串:
" 1 2 ".split(" ") // ["1", "2"]
現代瀏覽器上的話,會切割出不少空字符串元素。JavaScript 一開始也和如今同樣,但在 JavaScript 1.2 中,Breandan Eich 抄了 Perl 4 中對 split(" ") 的特殊處理,Perl 4 是從 awk 抄來的。1.3 回滾了這一改動。
在 JavaScript 1.0 裏,每一個對象的非索引屬性都會自動關聯一個索性屬性:
var o = new Object o.foo = 1 o.bar = 2 o[0] // 1 o[1] // 2
1.1 刪除了這個特性。
如今咱們都知道,this 在全局環境下指向全局對象,但在 JavaScript 1.0 裏,this 只容許在函數內部使用:
這一特性沒有文檔說明,因此不肯定是在以後的哪一個版本中改爲了能夠在全局環境中使用 this。