初识ES五、ES6

  提到 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标准化的脚本程序设计语言,至今为止有六个版本。这种语言在万维网上应用普遍,它每每被称为JavaScriptJScript,但实际上后二者是ECMA-262标准的实现和扩展。后端

 

2、ECMAScript关键字的完整列表:数组

   break else new var typeof
  case finally return void
  catch for switch while
  default if throw delete
  in try do instanceof
  若是把关键字用做变量名或函数名,可能获得诸如“Indetifier expected”(应该有标识符)这样的错误信息。
第3版中保留字的完整列表以下:
  abstract enum int short
  boolean export interface static
  byte extends long super char final
  native synchronized class float package
  throws const goto private transient debugger
  implements protected volatile double
  import public
  若是将保留字用做变量名或函数名,那么除非未来的浏览器实现了该保留字,不然极可能收不到任何错误消息。当浏览器将其实现后,该单词被看做关键字,如此将出现关键字错误。
 
3、细说ECMAScript
 
   类型
  ECMAScript 语言中的实际类型共有六种,分别是 UndefinedNullBooleanNumberString 和 Object。在此,我就不对每一个类型进行赘叙了。
 
  对象和属性
  ECMAScript 语言中的对象的含义比较简单,只是属性(property)的集合。属性一共分红三类,分别是命名数据属性、命名访问器属性和内部属性。前两类属性能够在代码中直接使用,然后面一种属性是规范使用的内部表示。对于前两类属性,能够有获取和设置属性值这两种不一样的操做。内部属性则用来描述对象在特定状况下的行为。 

  命名属性有本身的特性(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

 Object.defineProperty 函数的使用示例
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

 Array.prototype 中函数的使用示例
 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 的函数来进行处理。

  JSON

  在 ECMAScript 代码中,常常会须要与 JSON 格式的数据进行交换。JSON 也一般被用来做为客户端与服务器端之间的数据传输格式。这主要是由于在 ECMAScript 代码中处理 JSON 格式很是天然。JSON 格式数据通过解析以后,能够直接当成 ECMAScript 中的对象来使用。在使用 JSON 格式时的一个重要问题是如何在 ECMAScript 中的对象与文本形式之间进行互相转换。从服务器端经过 HTTP 协议获取的 JSON 文本须要通过解析以后,才能在 ECMAScript 代码中来使用;当须要向服务器端发送数据时,须要先把 ECMAScript 中的对象转换成文本格式。在 ECMAScript 规范第三版中并无对 JSON 格式数据的转换进行规范,大多数程序都依靠 JavaScript 框架来提供相关的支持。

JSON 对象的 parse 函数的使用示例
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}
 
     
JSON 对象中 stringify 函数的使用示例
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中新增了写数组方法,以下:

  1. forEach (js v1.6)
  2. map (js v1.6)
  3. filter (js v1.6)
  4. some (js v1.6)
  5. every (js v1.6)
  6. indexOf (js v1.6)
  7. lastIndexOf (js v1.6)
  8. reduce (js v1.8)
  9. reduceRight (js v1.8)

浏览器支持

  • Opera 11+
  • Firefox 3.6+
  • Safari 5+
  • Chrome 8+
  • Internet Explorer 9+

对于让人失望不少次的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...’
 
         

加强的对象字面量

对象字面量被加强了,写法更加简洁与灵活,同时在定义对象的时候可以作的事情更多了。具体表如今:

  • 能够在对象字面量里面定义原型
  • 定义方法能够不用function关键字
  • 直接调用父类方法

这样一来,对象字面量与前面提到的类概念更加吻合,在编写面向对象的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与const 关键字

能够把let当作var,只是它定义的变量被限定在了特定范围内才能使用,而离开这个范围则无效。const则很直观,用来定义常量,即没法被更改值的变量。

for (let i=0;i<2;i++)console.log(i);//输出: 0,1
console.log(i);//输出:undefined,严格模式下会报错

 

for of 值遍历

咱们都知道for in 循环用于遍历数组,类数组或对象,ES6中新引入的for of循环功能类似,不一样的是每次循环它提供的不是序号而是值。

var someArray = [ "a", "b", "c" ];
 
for (v of someArray) {
    console.log(v);//输出 a,b,c
}
相关文章
相关标签/搜索