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"
相關文章
相關標籤/搜索