文件夹

这两天在写语言精髓那本书的第三版,讨论到ES6跟ES5中间对“语句的值”的不同处理。正好Weibo上也有同学对这个问题有兴趣,所以专门整理了这篇。

写博客能够啰嗦点,写书就不行了。所以这篇文章跟书上能看到的还是会不一样的。

问题是:语句有值吗?

非常不幸。我们面临的的确是一门连语句都有值的语言。在JavaScript中。代码是按语句行(Statement Lists)来解释的,所以eval()本质上还是运行的语句行。比如:

  1. eval("1+2+3")

实际上并非在计算表达式,而是在解释运行“代码文本”。由于一个文本块隐含的有一个“文本/文件结束符(EOF)”,它与行结束符(EOL)一样能够等效于JavaScript的语句分隔符。所以上述代码等效于:

  1. eval("1+2+3;")

假设你不想了解得这么具体,那么记住“JavaScript是按语句行运行的”就好了。

那么……这个语句的值究竟是什么呢?非常不幸,上面这个演示样例中,语句的值和表达式值是一样的,也是6。

那么说,你骗我咯?

你看,JavaScript里面有一类语句,叫表达式语句。非常不幸,你见到的绝大多数语句都是表达式语句。比如

  1. Object.toString()

这里是一个方法调用(表达式)。你在后面加个分号(;),在语法上那就成了一个语句行,于是就成了表达式语句。由于其实JavaScript也存在单值表达式,所以一个单值也能够是一个语句。

这其实也就是函数或块首放上个”use strict”一点点也不违和的原因——它是符合JavaScript传统的语法惯例的:

  1. function foo() {
  2. "use strict";
  3. 1;
  4. true;
  5. }

上面的代码是合法的,函数foo()内有三个单值语句。函数声明本身也是一个语句。函数声明(以及全部的显式声明语句)是没有返回值的——在ECMAScript中它被定义为返回Empty。

函数调用的返回值是由于调用运算符“()”来决定的。这个运算符要求用return来返回值,假设没有则视为undefined。这就是JavaScript函数的一些特性的根源了。

“表达式运算”和“语句有值”能够解释JavaScript语法特性中的很多迷题,是这门语言在设计上的一些基本性质。

有啥米用呢?

由于eval()本质上是运行语句而不是表达式,所以语句怎样返回值就成了这个函数的终于特性。须要注意的是:不是eval()在求值,而是JavaScript代码块/语句行本身有值,而eval()不过返回这个值而已。假设没有这个特性。Ajax也就做不成了,由于我们通经常使用Ajax/JSONP从远端取个值过来,就是eval()“解析”一下,这里就是用的“运行语句并取值”的特性。

在JavaScript中,语句有值,而语句块(复合语句)的值就是这个块中最后一个有值语句的值。按ECMAScript的原文是:

The value of a StatementList is the value of the last value producing item in the StatementList. For example, the following calls to the eval function all return the value 1:

The production IfStatement : if ( Expression ) Statement else Statement is evaluated as follows:

  1. Let exprRef be the result of evaluating Expression.
  2. If ToBoolean(GetValue(exprRef)) is true, then

    a. Return the result of evaluating the first Statement.
  3. Else,
  1. eval("1;;;;;")
  2. eval("1;{}")
  3. eval("1;var a;")

value producing item”这个说法在ES5中是叫“value producing Statement”。可是,我并没有在ES5/ES6中找到一个明白的说法:哪些语句是生成值的语句呢?

研究这个是不是闲得那个啥疼?

那个啥疼不疼跟这个毛线关系也没有。

研究这个其实是非常重要的一件事情,由于以下这行代码究竟怎么解释,取决于我们这里的研究:

  1. // sourceText at remote
  2. if (x) (
  3. function aa() {}
  4. )
  5. else (
  6. function bb() {}
  7. )

我们假设上面的代码段来自于远端。然后我们在通过ajax的方式得到它。称为”sourceText”,那么以下的代码究竟是什么结果呢?

  1. x = true;
  2. foo = eval(sourceText);
  3. console.log(foo.name); // "name" property define in ES6

先解释一下当中的aa/bb函数。注意这里的两个函数在语法上不是“函数声明语句”,而是“函数表达式”——注意这里用了“()”来强制它们为表达式。

这个也不是我乱讲,在MDN(Mozilla Developer Network)官方文档上面就是这么分类的。“function statement”和“function expression”是两个不同的东西。

“函数声明语句”是无值的。而函数表达式是有值的,进而“函数表达式语句”也就是有值的。所以sourceText中。假设x是真。则if语句应该返回aa的值,否则该返回bb的值。于是。演示样例代码中的:

  1. foo = eval(sourceText);

才有意义,而终于控制台才会输出”aa”。表明foo函数来自于aa()函数表达式。如今看来,以下这句话是真的实用了吧:

if语句的值。是其then/else分支中的statementList最后一个有值语句的值。

ES5/ES6有什么差异呢?

这两天写书的时候发现一点跟此前理解的不同的地方(正好我又用了Nodejs中旧版的V8)。所以实在搞不清楚ECMAScript的定义出了问题,还是V8的实现出了问题。

于是乎在微信上抓了Hax要讨论。无奈乎那个家伙不理我——所以我决定这周去上海找他算账。此话容后再讲。

有什么不同呢?

问题出在ES5中说。假设then/else分支中没有语句,也就是statementList为empty,那么if语句结果也就为空。他的定义非常easy,是这么写的:

The production IfStatement :

​ if ( Expression ) Statement else Statement

is evaluated as follows:

  1. Let exprRef be the result of evaluating Expression.

  2. If ToBoolean(GetValue(exprRef)) is true, then

    a. Return the result of evaluating the first Statement.

  3. Else,

    a. Return the result of evaluating the second Statement.

假定“first Statement”为emptyStatement。结果当然就是empty。而对于empty,JavaScript会忽略这个“语句的值”。

这个意思是说:

  1. 1;
  2. {};
  3. ;

上面三个语句中,第2、3两行实际都是空语句——它们的值是empty。被忽略。所以整个代码文本会返回1。

那么依照这个规则。以下的代码:

  1. 1; if (true);
  2. // 或
  3. 1; if (false);

这两种情况都应该返回1。这个就是在ES5中的情况了。

然而在ES6里面,这段规范被写成以下这样:

// 4~5: let stmtCompletion be the result of first/second Statement

// 6: ReturnIfAbrupt(stmtCompletion).

7: If stmtCompletion.[[value]] is not empty, return stmtCompletion.

8: Return NormalCompletion(undefined).

这里的意思是说:假设then/else的结果不是empty那么就返回它们,否则。就得返回“undefined”。

于是以下这种演示样例:

  1. 1; if (true);

就该返回undefined了。

我当然一早就读明白了ES6,我当时的问题在于。我用了Nodejs中的旧版V8,以及firefox/chrome的旧版本号来做測试——它们声明支持了ES6。然而在这项特性上表现出来的,仍然是ES5的那个样子。

于是我就懞逼了:这些声称支持ES6的引擎错了。还是标准没写对呢?

正是由于对了解标准比了解指掌还要多的Hax没有如期出现。所以一向觉得

“标准都是人写的。是人写的就会错”

的我选择了相信…… 相同也是一堆人(以及也是相同一堆人)写的ES5。

——假设ES5是对的,那么就是ES6写错了。

结论是:ES6是改了规则,但更合理

验证这个结论的方法是:Chrome的新版中的新V8引擎,以及Firefox的新版本号都採用了ES6中的规范。当然,非常不幸,你假设用Nodejs来測试。至少当前版本号(4.4.2/5.10.1)中还是错误的、依照ES5的规范来实现的。

那么为什么我终于会觉得ES6就“更合理”一点呢?

还是得回到“语句该不该有值”这个根本问题上来讨论。

首先,ECMAScript是承认语句有值的,并且也同一时候承认“某些语句是没有值/不产生值”的。比如说,空语句就不产生值,函数声明、变量声明等等也不产生值 。

——对于成批的语句来说,不产生值则在代码上下文中对结果值无影响,产生值则影响结果。所以明白”哪些有值,哪些没有值“是非常重要的。而ES5中,这个问题导致if语句的结果有不确定性。

既然:

假设then/else中的语句有值,则if有值。假设无值,则if无值。

那么以下的代码就是不确定语义的:

  1. // sourceText at remote
  2. "hello";
  3. if (x) (
  4. function aa() {}
  5. )

当x是true时,if语句有意义。当x为false时,if语句在上下文中就没意义了——它对结果值没有影响。而

【ES5】if语句对结果值的影响存在不确定性

这个结果在语义设计上就是非常失败的。

而到了ES6中:

【ES6】if语句总是有结果值的,要么是then/else的结果,要么是undefined

这就使得if有着确定的语义了。

最后。不不过if语句

写ECMAScript 262的那票人真不是吃闲饭的(除了写4th的时候),有些问题人家是真想得清楚。比方还是这个语句的值的问题,根本上来说不是“if语句怎么回事”,而是“怎样处理语句的值”的问题。

我昨晚基本的工作就是整理了全部这些语句在值上的效果,if/for/while/try等等语句在值的处理上惊人的一致。除了这些语句和表达式语句之外,就唯独return/yield/throw用来显式地返回结果了。

所以说。语句在“产生值(value producing)”上面的行为,在ES6中得到了统一。

前端要给力之:语句在JavaScript中的值的更多相关文章

  1. 前端学习 第六弹: javascript中的函数与闭包

    前端学习 第六弹:  javascript中的函数与闭包 当function里嵌套function时,内部的function可以访问外部function里的变量 function foo(x) {   ...

  2. javascript中可变值与不可变值(原始值)

    字符串原始值修改不了1 var str = "abc"; 2 str[0] = "d"; 3 console.log(str[1]="f") ...

  3. JavaScript中的值和引用

    JavaScript5中有6种基本数据类型:undefined.null.布尔值(Boolean).字符串(String).数值(Number).对象(Object) ES6中新引入一种原始数据类型: ...

  4. JavaScript中Unicode值转字符

    在JavaScript中,将Unicode值转字符的方法: <!DOCTYPE html> <html> <head> <meta charset=" ...

  5. JavaScript中Object值合并方法

    原文:https://www.cnblogs.com/fullstack-yang/p/8085206.html ------------------------------------ 前言:在日常 ...

  6. Web前端面试指导(二十):JavaScript中如何翻转一个字符串?

    题目点评 字符串作在程序中是非常常见的,因为程序中绝大部分的数据都可以当作字符串来处理.需要对字符的处理方法比较熟悉,在回答的时候尽量能够说出多种解决方法更好! 字符串翻转的方法 1)使用字符串函数 ...

  7. Javascript中null值,特别注意的两点

    null 是一个javascript字面量,表示空值,就是没有对象被呈现.他是javascript原始值之一.null值常被放在期望一个对象上,但是不引用任何对象的参数位置,也就是说对象的初始化. 我 ...

  8. javascript中false值及其常见比较运算

    1. ture或者false的值 if判断中会被隐式转换成false的boolean类型的值有 false, 0, undefined , null , '', NaN(not a number) 除 ...

  9. javascript中的值如何传递到django下的views.py中或者数据库中?

    用Ajax,Ajax有很多种写法,包括JQuery和JS,这里贴一个用JQuery写的最通用的Ajax,POST方法传递JSON格式数据: $.ajax({ url: "your url&q ...

随机推荐

  1. hdu 4908(思路题)

    BestCoder Sequence Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Other ...

  2. 使用PyQt4制作一个正则表达式测试小工具

    最近在做一些网络爬虫的时候,会经常用到正则表达式.为了写出正确的正则表达式,我经常在这个网站上进行测试:Regex Tester.这个页面上面一个输入框输入正则表达式,下面一个输入框输入测试数据,上面 ...

  3. 用Gson解析没有数据头的纯数组json字符串

    无数据头Json字符串 Json字符串只有数组,而次数组没有名字,如下 [ { "name": "zhangsan", "age": &qu ...

  4. (6)python tkinter-容器、子窗体

    Frame f = tkinter.Frame(width=380, height=270, bg='white').pack() LabelFrame f = tkinter.LabelFrame( ...

  5. 洛谷——1968 美元汇率(DP)

    题目背景 此处省略maxint+1个数 题目描述 在以后的若干天里戴维将学习美元与德国马克的汇率.编写程序帮助戴维何时应买或卖马克或美元,使他从100美元开始,最后能获得最高可能的价值. 输入输出格式 ...

  6. rsync 备份 CENTOS 系统!

    rsync是一个非常优秀的文件同步工具,从它的名字可以看出,它支持远程同步.当然,在备份我的桌面系统时,只需要用到它的本地同步功能就行了.之所以选择rsync,是因为它具有如下优点:在备份还原过程中, ...

  7. Python: Write UTF-8 characters to csv file

    To use codecs, we can write UTF-8 characters into csv file import codecs with open('ExcelUtf8.csv', ...

  8. OpenSSL使用2(SSL,X.509,PEM,DER,CRT,CER,KEY,CSR,P12概念说明)(转)

    SSL SSL - Secure Sockets Layer,现在应该叫"TLS",但由于习惯问题,我们还是叫"SSL"比较多.http协议默认情况下是不加密内 ...

  9. React 入门之路

    React React简介 是由Facebook公司推广的一套框架,已经应用instagram等产品 React就是为了提供应用程序性能而设计的一套框架 在angular中,对dom提供了一些指令,让 ...

  10. Android性能优化第(二)篇---Memory Monitor检测内存泄露

    上篇说了一些性能优化的理论部分,主要是回顾一下,有了理论,小平同志又讲了,实践是检验真理的唯一标准,对于内存泄露的问题,现在通过Android Studio自带工具Memory Monitor 检测出 ...