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

Object.prototype.eval() 方法

在 JavaScript 1.0 中,eval 和现在一样,只是个全局函数。在 JavaScript 1.1 中,eval 还变成了所有对象的一个共用方法:

var foo = 1
var bar = 2 o = new Object
o.foo = 10
o.bar = 20 eval("this.foo + bar") //
o.eval("this.foo + bar") //

官方文档描述这个 eval 方法是说它能让一段代码字符串在执行时以当前对象为上下文:

The eval method evaluates a string of JavaScript code in the context of the specified object

描述很笼统,其实这个 eval 方法和普通的 eval 函数的区别有两点:

1. this 指向当前对象而不是全局对象

2. 类似 with 语句的作用,所有变量优先作为当前对象的属性去查找

该方法在随后的 JavaScript 1.2 中被废弃。

不对 Array(arrayLength) 进行特殊处理

Array() 构造函数实现于 JavaScirpt 1.1 中,和现在一样,当时就已经对单数字参数的情况做了特殊处理:

Array(1, 2, 3, 4, 5) // 包含五个元素 1、2、3、4、5 的数组
Array(1, 2, 3) // 包含三个元素 1、2、3 的数组
Array(5) // 包含五个空元素的数组  

为了不让用户踩这个坑,ES6 里还专门新增了一个 API:Array.of()。 其实不是到了 ES6 时代才发现这是个坑的,98 年就已经发现了。在 JavaScript 1.2 中,尝试去除了对单数字参数的特殊处理:

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) //
alert(arguments.b) //
alert(arguments.c) //
} 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 || 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] //
o[1] //

1.1 删除了这个特性。

this 不能在全局环境使用

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

这一特性没有文档说明,所以不确定是在之后的哪个版本中改成了可以在全局环境中使用 this。

你从未听说过的 JavaScript 早期特性的更多相关文章

  1. Javascript面向对象特性实现封装、继承、接口详细案例——进级高手篇

    Javascript面向对象特性实现(封装.继承.接口) Javascript作为弱类型语言,和Java.php等服务端脚本语言相比,拥有极强的灵活性.对于小型的web需求,在编写javascript ...

  2. javascript高级特性

    01_javascript相关内容02_函数_Arguments对象03_函数_变量的作用域04_函数_特殊函数05_闭包_作用域链&闭包06_闭包_循环中的闭包07_对象_定义普通对象08_ ...

  3. javascript高级特性(面向对象)

    javascript高级特性(面向对象): * 面向对象: * 面向对象和面向过程的区别: * 面向对象:人就是对象,年龄\性别就是属性,出生\上学\结婚就是方法. * 面向过程:人出生.上学.工作. ...

  4. Javascript面向对象特性实现封装、继承、接口详细案例

    Javascript面向对象特性实现(封装.继承.接口) Javascript作为弱类型语言,和Java.php等服务端脚本语言相比,拥有极强的灵活性.对于小型的web需求,在编写javascript ...

  5. JavaScript高级特性-数组

    1. JavaScript中的数组 在C++.Java中,数组是一种高效的数据结构,随机访问性能特别好,但是局限性也特别明显,就是数组中存放的数据必须是同一类型的,而在JavaScript中,数组中的 ...

  6. JavaScript高级特性-创建对象的九种方式

    1. 对象字面量 通过这种方式创建对象极为简单,将属性名用引号括起来,再将属性名和属性值之间以冒号分隔,各属性名值对之后用逗号隔开,最后一个属性不用逗号隔开,所有的属性名值对用大括号括起来,像这样: ...

  7. JavaScript this特性,静态方法 和实例方法,prototype

    <script type="text/javascript"> function logs(str) { document.write(str + "< ...

  8. ES6:JavaScript 新特性

    我相信,在ECMAScript.next到来的时候,我们现在每天都在写的JavaScript代码将会发生巨大的变化.接下来的一年将会是令JavaScript开发者们兴奋的一年,越来越多的特性提案将被最 ...

  9. JavaScript高级特性-实现继承的七种方式

    声明和约定: 在C++和Java中,我们可以通过关键字class来声明一个类,在JavaScript中没有这个关键字,但我们知道可以通过new一个function创建对象,这个function类似C+ ...

随机推荐

  1. Chrome Inspect调试微信出现404,需要FQ

    要么FQ,要么买个程序 见连接 http://www.cnblogs.com/slmk/p/7591126.html

  2. c++stack类的用法

    官方解释: LIFO stack Stacks are a type of container adaptor, specifically designed to operate in a LIFO ...

  3. 2.[Andriod]Andriod Studio结合Visual Studio Emulator for Android调试Android App

    0. 工欲善其事必先利其器 上一篇博客对比了一下Android和WinPhnoe的布局容器,后续篇章重点放在Android的开发上了. 说到开发就绕不开调试程序,调试Android App我们有2种选 ...

  4. CF1120D Power Tree

    沙发~~ 题意简述 给你一棵有根树,定义叶子为度数为1的点. 你可以以$ w_x \(的代价控制\)x\(点.选择控制之后可以给它的子树里的叶子加 上\)t (t \in Z )$. 你要以最小的总代 ...

  5. The 16th Zhejiang provincial collegiate programming contest

    今天我挺有状态的,看过的题基本都给了正解(可能是昨晚cf div3打得跟屎一样,人品守恒,不好意思发题解了),自己也给队伍签了很多水题(不敢让队友写,怕出锅). 最后6题滚了,有点可惜.还差B和K没做 ...

  6. 在Bootstrap开发框架的工作流模块中实现流程完成后更新资料状态处理

    在开发查看流程表单明细的时候,在Web界面中,我们往往通过使用@RenderPage实现页面内容模块化的隔离,减少复杂度,因此把一些常用的如审批.撤销.会签.阅办等等的流程步骤都放到了通用处理的页面V ...

  7. gitignore的使用

    gitignore的作用是忽略文件的提交,被加入到gitignore中的文件不会被提交到文件服务器 通常需要添加到.gitignore的文件有: (1)缓存相关文件,编译相关文件,运行时相关文件 (2 ...

  8. servlet篇 之 跳转问题

    servlet中的跳转: 跳转本质上是对文件传输操作 进行 封装. 例如:客户端访问一个servlet,在servlet中需要把已经写好的页面hello.html返回给客户端浏览器,需要用io来实现这 ...

  9. Django+Vue打造购物网站(六)

    商品详情页功能 商品详情页和CategoryViewSet类似,只需要多继承一个类(mixins.RetrieveModelMixin)就可以了 class GoodsListViewSet(mixi ...

  10. BZOJ 3456: 城市规划 与 多项式求逆算法介绍(多项式求逆, dp)

    题面 求有 \(n\) 个点的无向有标号连通图个数 . \((1 \le n \le 1.3 * 10^5)\) 题解 首先考虑 dp ... 直接算可行的方案数 , 容易算重复 . 我们用总方案数减 ...