你从未据说过的 JavaScript 早期特性

最近这些年在对 JavaScript 进行考古时,发现网景时代的 JavaScipt 实现,存在一些不为人知的特性,我从中挑选几个有趣的说一下。html

Object.prototype.eval() 方法

在 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(arrayLength) 进行特殊处理

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 的包装对象是个假值

如今咱们知道,全部对象都是真值,即使是 false 的包装对象,但在 JavaScript 1.3 以前,它是个假值:

if (new Boolean(false)) {
  // 不会执行
}

全部本地变量都同时做为 arguments 对象的属性存在

我在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 type]

如今咱们熟知,用 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

对 str.split(" ") 的特殊处理

若是传入 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 不能在全局环境使用

如今咱们都知道,this 在全局环境下指向全局对象,但在 JavaScript 1.0 里,this 只容许在函数内部使用:

这一特性没有文档说明,因此不肯定是在以后的哪一个版本中改为了能够在全局环境中使用 this。

相关文章
相关标签/搜索