提到 ECMAScript,可能不少 Web 开发人员会以为比较陌生。可是提到 JavaScript,你们应该都比较熟悉。实际上,ECMAScript 是标准化组织 ECMA发布的脚本语言规范。如今你们常见的 JavaScript、微软的 JScript 以及 Adobe 的 ActionScript 等语言都是遵循这个规范的,属于 ECMAScript 语言的变体。每一个 ECMAScript 规范的变体语言均可能增长本身额外的功能特性。理解 ECMAScript 规范自己,就能够对不少 JavaScript 语言中的复杂特性有比较深刻的了解。程序员
初次听到ES五、ES6,我有点儿懵了。好吧!我认可我不是一个合格的程序员。因此,我开始从新认识ES五、ES6。web
1、什么是ES? json
ES全称为:ECMAScript,是一种由Ecma国际(前身为欧洲计算机制造商协会,英文名称是European Computer Manufacturers Association)经过ECMA-262标准化的脚本程序设计语言,至今为止有六个版本。这种语言在万维网上应用普遍,它每每被称为JavaScript或JScript,但实际上后二者是ECMA-262标准的实现和扩展。后端
2、ECMAScript关键字的完整列表:数组
Undefined
、Null
、Boolean
、Number
、String
和 Object
。在此,我就不对每一个类型进行赘叙了。
命名属性有本身的特性(attribute)来定义该属性自己的行为。对于命名数据属性来讲,特性 [[Value]]
表示该属性的值,能够是任何 ECMAScript 语言中定义的类型的值;[[Writable]]
表示该属性的值是否为只读的;[[Enumerable]]
表示该属性是否能够被枚举。能够被枚举的属性能够经过 for-in
循环来获取到;[[Configurable]]
表示该属性是否能够被配置。若是 [[Configurable]]
的值为 true
,则该属性能够被删除、能够被转换为访问器属性、还能够改变除了 [[Value]]
以外的其余特性的值。对于命名访问器属性来讲,这类属性没有[[Value]]
和 [[Writable]]
特性,取而代之的是进行属性值获取和设置的 [[Get]]
和 [[Set]]
特性。若是 [[Get]]
和 [[Set]]
特性的值不是 undefined
,那么就必须是一个函数。属性的获取和设置是经过调用这两个函数来完成的。命名访问器属性一样有[[Enumerable]]
和 [[Configurable]]
特性,其含义与命名数据属性的对应特性的含义相同。命名属性能够在 ECMAScript 代码中进行处理。浏览器
内部属性的做用是定义 ECMAScript 中的对象在不一样状况下的行为。不一样类型的对象所包含的内部属性也不尽相同。每一个对象都有内部属性[[Prototype]]
,用来引用另一个对象。被引用的对象的 [[Prototype]]
属性又能够引用另一个对象。对象之间经过这种引用关系组成一个链条,称为原型链条(prototype chain)。ECMAScript 经过原型链条的方式来实现属性的继承。当尝试获取一个对象中的命名数据属性时,若是在当前对象中没有相应名称的属性,会沿着原型链条往上查找,直到找到该属性或到达原型链条的末尾;当设置命名数据属性时,若是当前对象中不存在相应名称的属性,会在当前对象中建立新的属性。命名访问器属性则始终是继承的。当设置一个命名访问器属性的值时,所设置的是原型链条上定义该属性的对象上的值。服务器
内部属性 [[Class]]
用来声明对象的类别,其做用相似于 Java 语言中对象的类名。经过 Object.prototype.toString
函数能够获取到[[Class]]
属性的值。当须要判断一个对象是否为数组时,可使用代码 Object.prototype.toString.apply(obj) === '[object Array]'
。app
var obj = {}; Object.defineProperty(obj, 'val', {}); // 建立一个新属性,特性为默认值 obj.val = 1; Object.defineProperty(obj, 'CONSTANT', {value : 32, writable : false}); // 建立一个只读属性 obj.CONSTANT = 16; // 对属性的修改是无效的,可是不会抛出错误 Object.defineProperty(obj, "newVal", {enumerable: true}); for (var key in obj) { console.log(key); // 能够枚举出 newVal } var initValue = 0; Object.defineProperty(obj, "initValue", { get : function() { return initValue; }, set : function(val) { if (val > 0) { initValue = val; } } });
var obj = {val : 1}; obj.newVal = "Hello"; Object.seal(obj); Object.defineProperty(obj, 'anotherVal', {}); // 抛出 TypeError 错误
数组
数组是 ECMAScript 中很是重要的一个内置对象。在 ECMAScript 代码中能够看到大量对数组的使用。Array
对象用来表示数组。在 ECMAScript 规范第三版中并无为 Array
对象提供比较多的实用函数来对数组进行操做。不少 JavaScript 框架对 ECMAScript 规范中的Array
对象进行加强。ECMAScript 规范第五版中对 Array
对象进行了加强,所以不少功能能够直接依靠运行环境的实现。框架
Array
对象自己是一个构造函数,能够用来建立新的数组实例。当 Array
对象自己做为一个函数来使用时,其做用至关于做为构造函数来使用。所以“Array(1,2,3)
”的结果与“new Array(1,2,3)
”是相同的。新建立的 Array
对象实例的内部属性 [[Prototype]]
的值是内置的Array
原型对象,即 Array.prototype
。经过 Array.isArray
函数能够判断一个对象是否为数组。dom
var array = [1, 2, 3, 4, 5]; array.indexOf(3); // 值为 2 array.lastIndexOf(4); // 值为 3 array.every(function(value, index, arr) { return value % 2 === 0; }); // 值为 false array.some(function(value, index, arr) { return value % 2 === 0; }); // 值为 true array.forEach(function(value, index, arr) { console.log(value); }); array.map(function(value, index, arr) { return value * 2; }); // 值为 [2, 4, 6, 8, 10] array.filter(function(value, index, arr) { return value % 2 === 0; }); // 值为 [2, 4] array.reduce(function(preValue, value, index, arr) { return preValue + value; }); // 值为 15 array.reduceRight(function(preValue, value, index, arr) { return preValue * value; }); // 值为 120
实际上,Array.prototype
中的函数并不限制只能对数组对象来使用。这些函数自己是通用的。比较典型的是在函数中对 arguments
对象的处理。arguments
对象自己不是数组类型的,可是同样可使用 Array.prototype
的函数来进行处理。
在 ECMAScript 代码中,常常会须要与 JSON 格式的数据进行交换。JSON 也一般被用来做为客户端与服务器端之间的数据传输格式。这主要是由于在 ECMAScript 代码中处理 JSON 格式很是天然。JSON 格式数据通过解析以后,能够直接当成 ECMAScript 中的对象来使用。在使用 JSON 格式时的一个重要问题是如何在 ECMAScript 中的对象与文本形式之间进行互相转换。从服务器端经过 HTTP 协议获取的 JSON 文本须要通过解析以后,才能在 ECMAScript 代码中来使用;当须要向服务器端发送数据时,须要先把 ECMAScript 中的对象转换成文本格式。在 ECMAScript 规范第三版中并无对 JSON 格式数据的转换进行规范,大多数程序都依靠 JavaScript 框架来提供相关的支持。
var jsonStr = '{"a":1, "b":2, "c":3}'; JSON.parse(jsonStr); JSON.parse(jsonStr, function(key, value) { return typeof value === 'number' ? value * 2 : value; }); // 结果为 {a:2, b:4, c:6} JSON.parse(jsonStr, function(key, value) { return typeof value === 'number' && value % 2 === 0 ? undefined : value; }); // 结果为 {a:1, b:3}
var user = { name : 'Alex', password : 'password', email : 'alex@example.org' }; JSON.stringify(user); JSON.stringify(user, ['name']); // 输出结果为“{"name":"Alex"}” JSON.stringify(user, function(key, value) { if (key === 'email') { return '******'; } if (key === 'password') { return undefined; } return value; }); // 输出结果为“{"name":"Alex","email":"******"}” JSON.stringify(user, null, 4);
ECMAScript 代码的执行由运行环境来完成。不一样的运行环境可能采起不一样的执行方式,但基本的流程是相同的。如浏览器在解析 HTML 页面中遇到 <script>
元素时,会下载对应的代码来运行,或直接执行内嵌的代码。在代码中经过 eval
函数也能够指定一段须要执行的代码。代码的基本执行方式是从上到下,顺序执行。在调用函数以后,代码的执行会进入一个执行上下文之中。因为在一个函数的执行过程当中会调用其余的函数,执行过程当中的活动执行上下文会造成一个堆栈结构。在栈顶的是当前正在执行的代码。当函数返回时,会退出当前的执行上下文,而回到以前的执行上下文中。若是代码执行中出现异常,则可能从多个执行上下文中退出。
var name = "alex"; function outer() { var age = 30; function inner(salutation) { return "Age of " + salutation + name + " is " + age; } return inner("Mr."); } outer();
4、ES5中新增的Array方法
ES5中新增的很多东西,好比数组这块,咱们可能就不须要去有板有眼地循环了。for
ES5中新增了写数组方法,以下:
浏览器支持
对于让人失望不少次的IE6-IE8浏览器,Array原型扩展能够实现以上所有功能,例如forEach
方法:
// 对于古董浏览器,如IE6-IE8 if (typeof Array.prototype.forEach != "function") { Array.prototype.forEach = function () { /* 实现 */ }; }
下面,我就选取其中一个方法进行示范:
forEach
是Array新方法中最基本的一个,就是遍历,循环。例以下面这个例子:forEach
[1, 2 ,3, 4].forEach(alert);
等同于下面这个传统的for
循环:
var array = [1, 2, 3, 4]; for (var k = 0, length = array.length; k < length; k++) { alert(array[k]); }
Array在ES5新增的方法中,参数都是function
类型,默认有传参,这些参数分别是?见下面:
[1, 2 ,3, 4].forEach(console.log); // 结果: // 1, 0, [1, 2, 3, 4] // 2, 1, [1, 2, 3, 4] // 3, 2, [1, 2, 3, 4] // 4, 3, [1, 2, 3, 4]
显而易见,forEach
方法中的function
回调支持3个参数,第1个是遍历的数组内容;第2个是对应的数组索引,第3个是数组自己。
所以,咱们有:
[].forEach(function(value, index, array) { // ... });
对比jQuery中的$.each
方法:
$.each([], function(index, value, array) { // ... });
会发现,第1个和第2个参数正好是相反的,你们要注意了,不要记错了。后面相似的方法,例如$.map
也是如此。
如今,咱们就可使用forEach
卖弄一个稍显完整的例子了,数组求和:
var sum = 0; [1, 2, 3, 4].forEach(function (item, index, array) { console.log(array[index] == item); // true sum += item; }); alert(sum); // 10
再下面,更进一步,forEach
除了接受一个必须的回调函数参数,还能够接受一个可选的上下文参数(改变回调函数里面的this
指向)(第2个参数)。
array.forEach(callback,[ thisObject])
例子更能说明一切:
var database = { users: ["张含韵", "江一燕", "李小璐"], sendEmail: function (user) { if (this.isValidUser(user)) { console.log("你好," + user); } else { console.log("抱歉,"+ user +",你不是本家人"); } }, isValidUser: function (user) { return /^张/.test(user); } }; // 给每一个人法邮件 database.users.forEach( // database.users中人遍历 database.sendEmail, // 发送邮件 database // 使用database代替上面标红的this ); // 结果: // 你好,张含韵 // 抱歉,江一燕,你不是本家人 // 抱歉,李小璐,你不是本家
若是这第2个可选参数不指定,则使用全局对象代替(在浏览器是为window
),严格模式下甚至是undefined
.
另外,forEach不会遍历纯粹“占着官位吃空饷”的元素的,例以下面这个例子:
var array = [1, 2, 3]; delete array[1]; // 移除 2 alert(array); // "1,,3" alert(array.length); // but the length is still 3 array.forEach(alert); // 弹出的仅仅是1和3
综上所有规则,咱们就能够对IE6-IE8进行仿真扩展了,以下代码:
// 对于古董浏览器,如IE6-IE8 if (typeof Array.prototype.forEach != "function") { Array.prototype.forEach = function (fn, context) { for (var k = 0, length = this.length; k < length; k++) { if (typeof fn === "function" && Object.prototype.hasOwnProperty.call(this, k)) { fn.call(context, this[k], k, this); } } }; }
5、ES6新特性
ES6(ECMAScript 6)是即将到来的新版本JavaScript语言的标准。
若是你会C#或者Java,你确定知道lambda表达式,ES6中新增的箭头操做符=>便有殊途同归之妙。它简化了函数的书写。操做符左边为输入的参数,而右边则是进行的操做以及返回的值Inputs=>outputs。
咱们知道在JS中回调是常常的事,而通常回调又以匿名函数的形式出现,每次都须要写一个function,甚是繁琐。当引入箭头操做符后能够方便地写回调了。请看下面的例子。
var array = [1, 2, 3]; //传统写法 array.forEach(function(v, i, a) { console.log(v); }); //ES6 array.forEach(v = > console.log(v));
ES6中添加了对类的支持,引入了class关键字(其实class在JavaScript中一直是保留字,目的就是考虑到可能在之后的新版本中会用到,如今终于派上用场了)。JS自己就是面向对象的,ES6中提供的类实际上只是JS原型模式的包装。如今提供原生的class支持后,对象的建立,继承更加直观了,而且父类方法的调用,实例化,静态方法和构造函数等概念都更加形象化。
下面代码展现了类在ES6中的使用。
//类的定义 class Animal { //ES6中新型构造器 constructor(name) { this.name = name; } //实例方法 sayName() { console.log('My name is '+this.name); } } //类的继承 class Programmer extends Animal { constructor(name) { //直接调用父类构造器进行初始化 super(name); } program() { console.log("I'm coding..."); } } //测试咱们的类 var animal=new Animal('dummy'), wayou=new Programmer('wayou'); animal.sayName();//输出 ‘My name is dummy’ wayou.sayName();//输出 ‘My name is wayou’ wayou.program();//输出 ‘I'm coding...’
对象字面量被加强了,写法更加简洁与灵活,同时在定义对象的时候可以作的事情更多了。具体表如今:
这样一来,对象字面量与前面提到的类概念更加吻合,在编写面向对象的JavaScript时更加轻松方便了。
//经过对象字面量建立对象 var human = { breathe() { console.log('breathing...'); } }; var worker = { __proto__: human, //设置此对象的原型为human,至关于继承human company: 'freelancer', work() { console.log('working...'); } }; human.breathe();//输出 ‘breathing...’ //调用继承来的breathe方法 worker.breathe();//输出 ‘breathing...’
字符串模板相对简单易懂些。ES6中容许使用反引号 ` 来建立字符串,此种方法建立的字符串里面能够包含由美圆符号加花括号包裹的变量${vraible}。若是你使用过像C#等后端强类型语言的话,对此功能应该不会陌生。
//产生一个随机数 var num=Math.random(); //将这个数字输出到console console.log(`your num is ${num}`);
自动解析数组或对象中的值。好比若一个函数要返回多个值,常规的作法是返回一个对象,将每一个值作为这个对象的属性返回。但在ES6中,利用解构这一特性,能够直接返回一个数组,而后数组中的值会自动被解析到对应接收该值的变量中。
var [x,y]=getVal(),//函数返回值的解构 [name,,age]=['wayou','male','secrect'];//数组解构 function getVal() { return [ 1, 2 ]; } console.log('x:'+x+', y:'+y);//输出:x:1, y:2 console.log('name:'+name+', age:'+age);//输出: name:wayou, age:secrect
如今能够在定义函数的时候指定参数的默认值了,而不用像之前那样经过逻辑或操做符来达到目的了。
function sayHello(name){ //传统的指定默认参数的方式 var name=name||'dude'; console.log('Hello '+name); } //运用ES6的默认参数 function sayHello2(name='dude'){ console.log(`Hello ${name}`); } sayHello();//输出:Hello dude sayHello('Wayou');//输出:Hello Wayou sayHello2();//输出:Hello dude sayHello2('Wayou');//输出:Hello Wayou
不定参数是在函数中使用命名参数同时接收不定数量的未命名参数。这只是一种语法糖,在之前的JavaScript代码中咱们能够经过arguments变量来达到这一目的。不定参数的格式是三个句点后跟表明全部不定参数的变量名。好比下面这个例子中,…x表明了全部传入add函数的参数。
//将全部参数相加的函数 function add(...x){ return x.reduce((m,n)=>m+n); } //传递任意个数的参数 console.log(add(1,2,3));//输出:6 console.log(add(1,2,3,4,5));//输出:15
拓展参数则是另外一种形式的语法糖,它容许传递数组或者类数组直接作为函数的参数而不用经过apply。
var people=['Wayou','John','Sherlock']; //sayHello函数原本接收三个单独的参数人妖,人二和人三 function sayHello(people1,people2,people3){ console.log(`Hello ${people1},${people2},${people3}`); } //可是咱们将一个数组以拓展参数的形式传递,它能很好地映射到每一个单独的参数 sayHello(...people);//输出:Hello Wayou,John,Sherlock //而在之前,若是须要传递数组当参数,咱们须要使用函数的apply方法 sayHello.apply(null,people);//输出:Hello Wayou,John,Sherlock
能够把let当作var,只是它定义的变量被限定在了特定范围内才能使用,而离开这个范围则无效。const则很直观,用来定义常量,即没法被更改值的变量。
for (let i=0;i<2;i++)console.log(i);//输出: 0,1 console.log(i);//输出:undefined,严格模式下会报错
咱们都知道for in 循环用于遍历数组,类数组或对象,ES6中新引入的for of循环功能类似,不一样的是每次循环它提供的不是序号而是值。
var someArray = [ "a", "b", "c" ]; for (v of someArray) { console.log(v);//输出 a,b,c }