JavaScript大杂烩13 - 总结ECMAScript 5新功能

  虽然说这个标准已经出来好久了,全部的主流浏览器的最新版本也都支持了这些特性,可是不少的教程中并无包含这个部分,这一节咱们专门来总结一下这个标准中的新功能。
Object的新方法
  在最新的JavaScript规范(ECMAScript 5)中,Object扩展了不少不错的静态方法,下面来简单看一下:
1. create/getPrototypeOf方法 - 干净的原型链
  先说简单的getPrototypeOf方法,这个方法统一了获取对象原型的方式,使用这个对象能够获取到对象的原型,这个很少说了。
  在ECMAScript5中,Object添加了静态方法create去串接原型链 - 这样就获得干净的不带冗余成员的原型链。先看一下小例子:
var Dog = {
  name : 'dog',
  speak : function () { alert('Woof!'); }
};
var dog = Object.create(Dog);
dog.speak();

  这里就是使用了这个方法建立了Dog的一个实例dog,这种方式的本质其实仍是与咱们以前讨论的使用空对象挂接原型对象是同样的,代码基本就像这样:编程

Object.create = function (o) {
    function F() {}
    F.prototype = o;
    return new F();
};

  使用这种方式较之使用构造函数加new的方式来讲更加简洁,可是不能设置私有和公开成员,并且实例成员在各个实例之间不共享,因此也有必定的局限性。数组

  这里咱们再回过头来看一下这个方法的定义:
Object.create(prototype, descriptors)
参数
prototype:必需。 要用做原型的对象。 能够为 null,通常也能够在须要的时候使用getPrototypeOf方法获取。
descriptors: 可选。 定义的一组属性的对象字面量,该对象包含了每一个属性的描述对象。
属性描述对象称为属性的attributes对象,其含有以下成员:
value:表示该属性的值,默认为undefined。
writable:表示该属性的值(value)是否能够改变,默认为true。这个值若是是false的话,即便是继承的子类的话,也没法修改属性的值,这个也是讲得通的。
enumerable: 表示该属性是否可枚举,默认为true,也就是该属性会出如今for...in和Object.keys()等操做中。
configurable:表示“可配置性”,默认为true。若是设为false,表示没法删除该属性,也不得改变attributes对象(value属性除外),也就是configurable属性控制了attributes对象自己的可写性。
get:表示该属性的取值函数(getter),默认为undefined。
set:表示该属性的存值函数(setter),默认为undefined。
返回值
一个具备指定的内部原型且包含指定的属性(若是有)的新对象。

  毫无疑问,这个函数最有意义的应该就是第二个参数:一组属性描述对象,这个在别的语言中很是有用的特性姗姗来迟,虽然在JavaScript中能够直接使用dog.age=2这种方式定义和修改属性,可是这种数据成员终究不知足封装性的需求,并且不知足属性本质上是函数这种惯例,在这个版本中知足封装性的属性(本质上是函数)终于出如今你们视野中了,咱们看一个例子来了解一下如何定义一组属性:浏览器

var Shape = { twoDimensional: true, color: 'Red'};
var Square = Object.create(Shape, {
  // 简单的属性,不包含任何的其余逻辑的属性
  type: { value: 'square', enumerable: true, writable: true },
  // 复杂一点的属性,包含有一些逻辑的属性
  size: {
    get: function() { return this.__size__;},
    set: function(pSize) {
      if (pSize > 10) alert('Too large...');
      // 其余的处理逻辑,例如简单的保存一下
      this.__size__ = pSize;
    },
    enumerable: true
  }
});
var square = Object.create(Square);
square.type = 'rectangle';
alert(square.type);
square.size = 9;
alert(square.size);

使用起来仍是很简单的,可是有一点不太优雅的代码就是this.__size__这个保存属性值的成员,为了知足封装性,咱们得使用一下闭包的特性了:闭包

var Square = Object.create(Shape, (function () {
    var m_size;
 
    return {
      // 简单的属性,不包含任何的其余逻辑的属性
      type: { value: 'square', enumerable: true, writable: true },
      // 复杂一点的属性,包含有一些逻辑的属性
      size: {
        get: function() { return m_size;},
        set: function(pSize) {
          // 常见的条件检查
          if (pSize > 10) alert('Too large...');
          // 其余的处理逻辑,例如简单的保存一下
          m_size = pSize;
        },
        enumerable: true
      }
    };
}()));

  把Square定义的那段代码换成上面这段,就有点样子了。app

  这里有一点须要注意,像上面例子中定义type那样的简单属性,若是不强行把writable不设为true的话,则赋值会失败,这个不知是浏览器的默认设定仍是JavaScript的Bug,不论是哪一个,虽然那些属性的默认值都是true,你们须要的话仍是写上吧,特别是writable这个属性。
2. defineProperty, defineProperties方法 - 迟到的属性
  上面咱们看到了在create方法中定义属性的例子,若是是现有的对象,咱们仍然能够给它定义新的属性,这是经过使用defineProperty, defineProperties方法实现的,前者定义一个属性,后者定义一组属性,让咱们改写上面的例子中定义的type:
var Shape = { twoDimensional: true, color: 'Red'};
var square = Object.create(Shape);
Object.defineProperty(square, 'type', {
    value : 'square',
    enumerable : true,
    writable: true
});
square.type = 'rectangle';
alert(square.type);

  使用defineProperties方法就与create中的第二个参数一致了,参见:函数式编程

var Shape = { twoDimensional: true, color: 'Red'};
var square = Object.create(Shape);
Object.defineProperties(square, (function () {
    var m_size;
 
    return {
      // 简单的属性,不包含任何的其余逻辑的属性
      type: { value: 'square', enumerable: true, writable: true },
      // 复杂一点的属性,包含有一些逻辑的属性
      size: {
        get: function() { return m_size;},
        set: function(pSize) {
          // 常见的条件检查
          if (pSize > 10) alert('Too large...');
          // 其余的处理逻辑,例如简单的保存一下
          m_size = pSize;
        },
        enumerable: true
      }
    };
}()));
square.type = 'rectangle';
alert(square.type);
square.size = 9;
alert(square.size);

  注意defineProperty/defineProperties这两个函数的返回值就是修改后的对象,有时候会使用这个返回值。函数

  很强大的属性功能吧,下面还有另一种定义的方式,那就是使用get/set关键字,看个例子:
var Shape = {
  get type() { return this.__type__;},
  set type(value) { this.__type__ = value;}
};
var square = Object.create(Shape);
square.type = 'rectangle';
alert(square.type);

  简单吧,很简洁,很是强大。测试

3. keys, getOwnPropertyNames方法 - 获取全部的属性
  这两个方法获得对象上面直接定义的(非原型链上的)全部属性,只不过keys只获得可枚举的属性,而getOwnPropertyNames方法能够获得全部属性,无论属性定义的时候enumerable是否为true。看一个例子:
var o = { name: 'frank'};
Object.defineProperties(o, {
        p1: { value: 1, enumerable: true },
        p2: { value: 2, enumerable: false }
});
alert(Object.keys(o));
alert(Object.getOwnPropertyNames(o));

  注意name这种原始的属性是可枚举的,之前经过for/in结合hasOwnProperty方法才能完成枚举对象自身定义的属性的功能如今直接能够经过遍历keys完成了。this

4. preventExtensions/isExtensible,seal/isSealed,freeze/isFrozen - 禁止扩展对象
  这3组方法用于控制对象的状态:
preventExtensions方法禁止对象添加新属性,isExtensible检测对象是否使用了preventExtensions方法。
seal方法禁止对象添加新属性和禁止删除已有属性,isSealed检测对象是否使用了seal方法。
freeze方法禁止对对象作任何修改,不能添加新属性和删除已有属性,也不能修改对象的属性值,isFrozen检测对象是否使用了freeze方法。

  看到了吧,一个比一个严格,等到了使用freeze方法的时候,对象彻底变成了只读对象,这个在不少场合下仍是颇有用的。看个简单的例子:spa

var o = { name: 'frank'};
Object.preventExtensions(o);
o.age = 10;
alert(o.age); //undefined,表明没法添加新属性
Object.seal(o);
delete o.name;
alert(o.name); // 删除不了
Object.freeze(o);
o.name = 'dong';
alert(o.name); // 没法修改

 

Array的新方法
  这里的新方法主要是新的实例方法(扩展了原型链对象)。
1. indexOf/lastIndexOf
  这一组方法经过方法名就知道其用途了,返回元素在集合中首次/最后一次出现的索引值,不存在的话返回-1。
var a = ['a','b','c','b'];
alert(a.indexOf('b')); // 1
alert(a.indexOf('y')); // -1
alert(a.lastIndexOf('b')); // 3

2. every/some/forEach/filter/map/reduce/reduceRight

  这一组方法都与时髦的函数式编程紧密相关,这些方法的参数是一个函数,这个做为参数的函数自己能够接收三个参数:数组的当前元素item、该元素的位置index和整个数组arr。另外,上下文对象(context)能够做为第二个参数,传入forEach(), every(), some(), filter(), map()方法中,用来绑定函数运行时的上下文。
  下面来看看这些方法的意图:
map/forEach方法 - 遍历元素执行指定的操做
  map方法对全部元素依次调用一个函数,根据函数结果返回一个新数组。
[1, 2, 3].map(function(item, index, arr){
    return item * item;
});
// [1, 4, 9]

  经过函数的call方法,map方法能够用于字符串。

[].map.call('abc', function (x) { return x.toUpperCase() })
// [ 'A', 'B', 'C' ]
// 或者
'abc'.split('').map(function (x) { return x.toUpperCase() })
// [ 'A', 'B', 'C' ]

  forEach方法对全部元素依次执行一个函数,它与map的区别在于不返回新数组,而是对原数组的成员执行某种操做,甚至可能改变原数组的值。

[1, 2, 3].forEach(function(item, index, arr){
    console.log("array[" + index + "] = " + item);
});
// array[0] = 1
// array[1] = 2
// array[2] = 3

  从上面代码能够看到,map和forEach的参数格式是同样的,都是一个函数。该函数接受三个参数,分别是当前元素、当前元素的位置(从0开始)、整个数组。

  这两个方法均可以接受第二个参数,用来绑定函数中的this关键字。
var out = [];
[1, 2, 3].map(function(item, index, arr){
    this.push(item * item);
}, out);
out // [1, 4, 9]

  上面代码表示,若是提供一个数组做为第二个参数,则函数内部的this关键字就指向这个数组。

filter方法 - 选取知足指定条件的元素
  filter方法依次对全部数组成员调用一个测试函数,返回结果为true的成员组成一个新数组返回。
[1,2,3,4,5].filter(function(item){
    return (item>3);
})
// [4,5]

  上面代码将大于3的原数组成员,做为一个新数组返回。

  filter方法的参数必须是一个返回布尔值的函数。该函数的第一个参数是当前数组成员的值,这是必需的,后两个参数是可选的,分别是当前数组成员的位置和整个数组。
[1, 2, 3, 4, 5].filter(function(item, index, arr){
    return index % 2 === 0;
});
// [1, 3, 5]

  上面代码返回原数组偶数位置的成员组成的新数组。

some/every方法 - 检查集合是否知足指定的条件
  这两个方法用来判断数组成员是否符合某种条件。
  some方法对全部元素调用一个测试函数,只要有一个元素经过该测试,就返回true,不然返回false。
[1, 2, 3, 4, 5].some(function(item, index, arr){
    return item >= 3;
});
// 返回true

  上面代码表示,若是存在大于等于3的数组成员,就返回true。

  every方法对全部元素调用一个测试函数,只有全部元素经过该测试,才返回true,不然返回false。
[1, 2, 3, 4, 5].every(function(item, index, arr){
    return item >= 3;
});
// 返回false

  上面代码表示,只有全部数组成员大于等于3,才返回true。

  从上面的代码能够看到,some和every的使用方法与map和forEach是一致的,参数彻底如出一辙。也就是说,它们也可使用第二个参数,用来绑定函数中的this关键字。
reduce/reduceRight方法 - 累计集合中的指定的元素
  reduce和reduceRight方法的做用,是依次处理数组的每一个元素,最终累计为一个值。这两个方法的差异在于,reduce对数组元素的处理顺序是从左到右,reduceRight则是从右到左,其余地方彻底同样。
reduce方法的第一个参数是一个处理函数。该函数接受四个参数,分别是:
用来累计的变量(即当前状态)
数组的当前元素
当前元素在数组中的序号(从0开始)
原数组

  这四个参数之中,只有前两个是必须的,后两个则是可选的。

[1, 2, 3, 4, 5].reduce(function(x, y){
    return x+y;
});
// 15

  上面代码的参数x表示累计变量,默认为0,y则是数组的当前元素。reduce方法依次将每一个数组元素加入x,最终返回它们的总和15。

  利用reduce方法,能够写一个数组求和的sum方法。
Array.prototype.sum = function (){
    return this.reduce(function (partial, value){
        return partial + value;
    })
};
[3,4,5,6,10].sum()
// 28

  若是要对累计变量指定初值,能够把它放在reduce方法的第二个参数。

[1, 2, 3, 4, 5].reduce(function(x, y){
    return x+y;
}, 10);
// 25

  上面代码指定参数x的初值为10,因此数组元素从10开始累加,最终结果为25。

  因为reduce方法依次处理每一个元素,因此实际上还能够用它来搜索某个元素。好比,下面代码是找出长度最长的数组元素。
function findLongest(entries) {
  return entries.reduce(function (longest, entry) {
    return entry.length > longest.length ? entry : longest;
  }, '');
}

 

静态方法 - isArray
  这个方法不用多说,就是判断一个对象是不是数组,直接看个例子:
var names = ['Collis', 'Cyan'];
Array.isArray(names); // true

 

Function的新实例方法 - bind方法
  这个方法与apply/call相似,只不过apply/call是直接调用了方法,而bind只是把this绑定到了指定的参数上,而并不执行方法,看个例子:
// 仅仅绑定了this,还须要主动的经过对象去调用
tooltip.show = showText.bind(tooltip);
tooltip.show();
overlay.show = showText.bind(overlay);
overlay.show();
// 直接调用的apply/call形式
showText.call(tooltip);
showText.call(overlay);

 

Date的新方法
静态方法 - now方法
  now方法返回当前距离1970年1月1日 00:00:00 UTC的毫秒数(Unix时间戳乘以1000)。看个例子:
Date.now()
// 1364026285194相似的数字

  若是须要更精确的时间,可使用window.performance.now()。它提供页面加载到命令运行时的已通过去的时间,单位是浮点数形式的毫秒。

window.performance.now()
// 21311140.415相似的数字

  这一组方法可用于生成一种惟一的id标识。

实例方法 - toJSON方法
  本身写也不难,不过内置了也不错,看个例子:
alert(new Date().toJSON());

 

String的新实例方法 - 迟到的trim方法
  该方法用于去除字符串两端的空格,终于内置了,看个简单的例子:
" hello world ".trim()
// "hello world"
相关文章
相关标签/搜索