Beauty will fade but love will stay.
ECMA-262 通过 ECMAScript 描述了 JavaScript 的所有基本概念。在了解 JavaScript 这门语言最基本的工作原理之前,让我们再回顾一下 ECMAScript 发展历史:
1)JavaScript 与 ECMAScript 是什么关系? JavaScript 的含义比 ECMAScript 多得多,一个完整的 JavaScript 实现由 ECMAScript、DOM 和 BOM 三个部分组成,其中 ECMAScript 是 JavaScript 的核心。 2)ECMAScript 是干什么的? 在早期 JavaScript 有两个不同版本:Netscape Navigator 中的 JavaScript 和 Internet Explorer 3 中的 JScript,因此 ECMA 推出了 ECMAScript。ECMAScript 规定了 JavaScript 的基本语法和特性,自此各浏览器开发商开始将 ECMAScript 作为各自 JavaScript 实现的基础。ECMAScript 是规范化的脚本(script)语言。 3)ECMA-262是干什么的? ECMA-262 是 ECMAScript 语言的标准定义,与 Web 浏览器没有依赖关系,也不包含输入输出定义。因为 ECMA-262 定义的 ECMAScript 只是 JavaScript 的基础内容,所以各开发商可以在此基础之上构建出更完善的脚本语言。(1)ECMA-262 第1版:本质与 Netscape 的 JavaScript 1.1 相同,去除了对针对浏览器的特性,支持 Unicode 标准(多语言开发),对象也变成了和平台无关的。(1997年6月)
(2)ECMA-262 第2版:与 ISO/IEC-16262(即 ECMAScript)保持严格一致,没有新增、修改和删除。(1998年6月) (3)ECMA-262 第3版:它标志着 ECMAScript 成为了一门真正的编程语言。在此之后 ECMAScript 沉寂了许多年,直到 Ajax 流行起来后标准工作才再次起步。(1999年12月) (4)ECMA-262 第4版:于发布前被废弃, 但是它的大部分内容被 ES 6 继承了。 (5)ECMA-262 第5版:力求澄清第 3 版中的歧义,并添加了新的功能。(2009年12月) (6)ECMA-262 第5.1版:与 ISO/IEC 16262:2011 第三版保持严格一致。(2011年6月) (7)ECMA-262 第6版:增添了许多必要的特性(2015年6月) (8)ECMA-262 第7版:又被称为 ECMAScript 2016,增加了两个新的特性。(2016年6月) (9)ECMA-262 第8版:又被称为 ECMAScript 2017,增加了字符处理、错误处理和一些新函数。(2017年6月)虽然 ECMAScript 的新标准不断地被推出来了,目前市场上大多数浏览器也都能兼容ECMAScript 5 了,但工作中常常要求兼容 IE 的低版本,所以 ECMA-262 第 3 版中定义的 ECMAScript 依然是各浏览器实现最多的一个版本。
1.语法
ECMAScript的语法大量借鉴了 C 和其他类 C 语言的语法。
区分大小写
ECMAScript中的一切(变量、函数名和操作符)都区分大小写。
标识符
标识符就是指变量、函数、属性的名字,或者函数的参数,它由符合以下条件的一个或多个字符组成:
- 第一个字符必须是一个字母、下划线或者一个美元符号
- 其他字符可以是字母、下划线、美元符号或数字
其中的字母可以包含扩展的 Unicode 字母字符(因为保存标识符时采用的就是Unicode编码),所以理论上你可以使用 UTF-8 中的任意字符(例如中文字符)来组成标识符,只是我们不推荐这样做。
按照惯例,ECMAScript 标识符采用驼峰命名法(首字母小写,后面单词的首字母大写)。 关键字、保留字、true、false、null 不能用作标识符。undefined 是全局对象 window 的一个属性,这个属性是不可写(writable:false)的,也就是说在全局作用域内不能重新赋值(赋值无效但不会报错);
又因为 undefined 不是 js 的关键字或保留字,所以它可以被当作一个标识符,在除了全局作用域以外的任何作用域中(例如在方法中)使用。注释
ECMAScript使用 C 风格的注释,包括单行注释(//)和块级注释(/* */)。
/* * 这是多行注释,此行左边的星号无特殊意义,仅用于提高注释可读性 */
严格模式
严格模式在 ECMAScript 5被引入,为 JavaScript 定义了一种不同的解析与执行模型,在此模式下 ECMAScript 3 中的一些不确定行为将得到处理,某些不安全操作也会抛出错误。
要在整个脚本中启用严格模式,可以在脚本顶部添加如下代码:"use strict";
这行代码是一个编译指示,用于告诉 JavaScript 引擎切换到严格模式。
类似的,要在函数中启用严格模式,可以在函数内部的上方包含这条编译指令。语句
ECMAScript 中的语句以一个分号结尾;如果省略分号则由解析器确定语句结尾,推荐在任何时候都不要省略分号。推荐在控制语句中使用代码块(即{}),即使代码块中只有一行语句。
2.关键字和保留字
ECMA-262 描述了一组具有特定用途的关键字,这些关键字可用于表示控制语句的开始或结束,或者用于执行特定操作等。关键字不能用作标识符。以下是ECMAScript 的全部关键字:
break do instanceof typeofcase else new varcatch finally return voidcontinue for switch whiledebugger* function this withdefault if throw delete in try
其中 debugger 是 ECMAScript 5 新增的关键字,在此之前它是一个保留字。
ECMA-262 描述了一组不能用作标识符的保留字,尽管目前这些保留字在 ECMAScript 中没有任何用途,但它们将来可能被用作关键字。以下是 ECMAScript 第 3 版定义的全部保留字(即 Java 的所有关键字):
abstract enum int shortboolean export interface staticbyte extends long superchar final native synchronizedclass float package throwsconst goto private transientdebugger implements protected volatiledouble import public
ECMAScript 第 5 版在非严格模式下运行时的保留字缩减为以下几个:
class enum extends superconst export import
这意味着非严格模式下开发商得以使用那些被释放的保留字来作为标识符。
ECMAScript 第 5 版在严格模式下运行时的保留字缩减为以下几个:
class enum extends superconst export import implementinterface static let yieldpackage private protected public
在这里,let 和 yield 是 ECMAScript 第 5 版新增的保留字。ECMAScript 第 5 版在严格模式中还对 eval 和 arguments 施加了限制,它们不能用作标识符或属性名,但它们并不完全是保留字。
为了保证兼容性,通常会将第3版的保留字外加 let 和 yield,即目前出现过的所有保留字作为编程时的参考。
JavaScript 预定义了很多全局变量和函数,应当避免把它们的名字用做变量名和函数名。每一种特定的 JavaScript 运行环境(客户端、服务器端等)都有自己的一个全局属性列表,我们可以通过 Window 对象来了解客户端 JavaScript 中定义的全局变量和函数列表。在实现 ECMAScript 第 3 版的 JavaScript 引擎中,使用关键字作为标识符会导致“Identifier Expected”错误,但是使用保留字作为标识符不一定会导致相同错误,这取决于不同的引擎。
ECMAScript 第 5 版修改了关键字和保留字的规则:关键字和保留字不能用作标识符,但可以用作对象的属性名。我们不建议这样做,以便保持代码的兼容性。
3.变量
ECMAScript 的变量是松散类型的(即可以保存任意类型的数据),每个变量只是一个用于保存值的占位符。
定义变量,可以使用var操作符,例如:
var message;
像这样未初始化的变量,会保存一个特殊值 undefined。var message = "hello";
像这样初始化变量不会把它标记为字符串类型,仅仅是赋值而已,因此我们可以修改一个变量的类型,只是这种做法并不推荐。 使用 var 来定义的变量通常是局部变量,仅在定义该变量的作用域中有效,如果作用域是整个脚本,那么它就是全局变量。 可以通过省略 var 操作符,定义一个全局变量:
message = "hello";
使用这种方式定义的变量始终是一个全局变量,即使该语句在一个方法内。 在局部作用域中的全局变量难以维护,并且在一个作用域执行前其内的全局变量还没有定义,所以应该慎重的使用全局变量。 在严格模式下,不允许给一个未声明的变量(即全局变量)赋值,否则会抛出 ReferenceError 错误。 4.语句
if语句
语法:if (条件) 语句1 else 语句2
条件不一定是布尔值,ECMAScript 会自动调用 Boolean() 将其转化为布尔值 当条件成立时执行语句1,否则执行语句2do-while语句
语法:do { 语句 } while (条件)
是一种后测试循环语句,即先执行一次语句再进行判断,条件成立继续执行语句,否则退出循环while语句
语法:while (条件) 语句
是一种前测试循环语句,即先进行判断,再根据判断结果执行语句或退出循环for语句
语法:for (初始化语句;条件;额外语句) 语句
也是一种前测试循环语句,它可以在循环开始前定义初始化变量,在每次循环结束时执行一行额外语句 for循环与while循环作用相同,它只是把与循环有关的代码集中在一起for-in语句
语法:for (变量 in 对象) 语句
是一种精准的迭代语句,可以用来枚举对象的属性 每次循环,都会将对象中的一个属性赋值给变量,所有属性都被遍历一遍时停止循环 如果待遍历的对象为 null 或 undefined,则不执行语句,直接结束循环label语句
语法:标签:语句
定义的标签可以由 break 或 continue 语句引用break和continue语句
用于在循环中精确的控制代码执行,break 会立刻退出整个循环,而 continue 会退出当前这一次循环,然后从循环顶部继续执行
with语句
语法:with (对象) 语句
用于将代码的作用域设置到一个特定的对象中 例如:var a = local.name;var b = local.address;
可以简写为:
with (local) { var a = name; var b = address;}
严格模式下不允许使用 with 语句
switch语句
语法:
switch (条件) { case 情况1:语句 break; case 情况2:语句 break; default:语句}
本质是一种简化的if语句
它的判断条件可以使用任何数据类型,case 的值也不一定是常量 switch 在比较值时使用全等于操作符,不会发生类型转换5.函数
ECMAScript 通过函数封装多条语句,在任何地方任何时候调用执行
基本语法:function 函数名 (参数列表) { 语句}
通过函数名加上圆括号来调用函数,括号中可以传入适当的参数,多个参数间用逗号隔开
ECMAScript 中的函数在定义时不必指定返回值类型,函数在任何时候都可以通过 return 语句实现返回值 return 语句如果不带有任何返回值,或者函数没有 return 语句,函数将返回 undefined 值 严格模式下,函数和参数不能命名为 eval 或 arguments,参数也不能同名理解参数
ECMAScript 不介意传递参数的个数和数据类型,因为参数在内部是用一个数组来表示的,解析器不会验证参数列表,可见命名的参数只是方便阅读而并非必需的,通过 arguments 对象可以访问这个参数数组
arguments 对象的 length 属性表示有多少个参数传递给了函数,利用这个可以让函数接收任意数量的参数并实现适当的功能(例如重载) 例如:function add(num1,num2) { // num1 和 num2 是命名参数 return arguments[0] + arguments[1];}
arguments 中参数的值总是与对应的命名参数的值(如果有命名参数)保持同步,但他们并非访问的同一个内存空间
如果只传入一个参数,则只有 arguments[0] 而没有 arguments[1],但是没有传递值的命名参数 num2 将被自动赋予 undefined 值 严格模式下,arguments 的值不允许重写- 按值传递:函数的形参是实参的副本。修改形参的值并不会影响实参。由于每次都需要克隆副本,对一些复杂类型性能较低。
- 按引用传递:函数的形参是实参的隐式引用。形参的值如果被修改,实参也会被修改。使函数调用的追踪更加困难。
ECMAScript 在的所有参数传递都是值传递,不可能通过引用传递参数,因为对象变量存储的值是这个对象在堆内存中的内存地址。
- 原始值:存储在栈中的简单类型数据段,变量访问的位置就是值存储的位置。
- 引用值:存储在堆中的对象,变量首先访问的是存储在栈中的值,得到的是一个指针(堆中的内存地址值),然后根据指针得到存储在堆中的对象
JS 的基本类型是按值传递的。
JS 的对象的传递是按共享传递的,函数接受对象实参引用的这个形参,既不是按值传递的对象副本,也不是按引用传递的隐式引用。 在函数内对对象的属性进行操作,实际就是对其指向对象的属性进行操作,因为此时形参和实参都指向同一个引用值,这里共享传递和引用传递的表现是一致的。 但是,如果对形参的整体进行操作,实际上是新定义了一个对象,形参存储在栈中的值引用地址修改为了新的对象的引用地址,此时形参和实参相互独立。没有重载
ECMAScript 函数不能重载,因为没有签名(接受的参数个数和类型),但可以通过一些方法模仿方法重载。
如果定义两个同名函数,则后者会覆盖前者。 ECMAScript 函数参数是一个含零个或多个值的数组,通过检查传入参数个数,可以近似的实现函数的重载。