擴充套件javascript原生物件

世有因果知因求果發表於2016-01-19

原文地址: http://javascript.info/tutorial/native-prototypes

 

原生的javascript 物件在prototypes裡面儲存他們可用的方法。

比如,當我們建立一個新的物件時, 它並未包含任何東西,它怎麼知道能夠有個toString方法可供使用呢?

   var obj={};
   alert(obj.toString())

那是因為obj={}是下面語句

obj = new Object()

的縮寫,而Object是原生的javascript 建構函式,而當我們使用new Object()來建立一個新的物件時,有一個重要的特性就是:產出物件的__proto__指標將指向建構函式的prototype物件,這裡就是Object.prototype物件。也就是說obj.__proto__ == Object.prototype,而Object.prototype又是一個包含toString方法的原生物件

。注意: __proto__是隨瀏覽器的,有可能命名並不相同

而根據javascript的原型鏈查詢特性,所有在這個鏈上的屬性和方法都是可讀的(注意當寫一個在原型鏈上的屬性時,會自動建立一個ownProperty,而不會去更改原型上的屬性值!!),也正因為此,所以toString方法自然可用了。

上面的故事同樣適用於Array,Function和其他物件。他們的方法分別在Function.prototype, Array.prototype上面定義的、

當一個物件的屬性被訪問或者方法被呼叫時,javascript引擎通過以下演算法和順序來檢索:

1. 在物件自身上面找;

2. 如果1.找不到,就到物件的__proto__上面找;

3. 如果2.找不到,就到__proto__.__proto__上面找

上面這個原型鏈從物件本身開始持續查詢,直到屬性被找到(注意這時this為子物件,而非原型),或者直到下一個__proto__ == null(也就是到鏈底)了而返回undefined. 唯一一個其__proto__屬性為 null的物件是 Object.prototype,所有的鏈最終都指向Object.prototype,換句話說,所有的物件都從Object繼承

更改原生prototypes新增新的方法

原生的prototypes是可以被修改的,比如我們可以建立新的方法到原型物件上去。

我們可以寫一個方法來多次重複一個字串

String.prototype.repeat = function(times) {
  return new Array(times+1).join(this)
}

alert( "a".repeat(3) ) // aaa

再例如,我們可以在Object.prototype物件上新增一個each(func)方法,以便我們在每一個屬性上來應用func函式。

Object.prototype.each = function(f) {
  for(var prop in this) {
if (Object.prototype.hasOwnProperty(prop)) continue // filter
    var value = this[prop]
    f.call(value, prop, value) 
  }
}

// Now correct
var obj = { name: 'John', age: 25 }
obj.each(function(prop, val) {  
  alert(prop) // name -> age 
})

通過上面的方法修改原生物件從一開始就招致激烈的評論,有人說不安全,暴露太多到global空間可能造成命名汙染,有人說很好用。但無論如何,它在做相容低版本瀏覽器的js功能時,用處廣泛:

比如,如果沒有Object.create支援,而我們又必須相容,那麼一個可行的方法是:

if (!Object.create) {
  Object.create = function(proto) {
    function F() {}
    F.prototype = proto
    return new F
  }
}

從原生物件繼承

原生物件可以用來被繼承,比如,Array.prototype為new Array()建立的物件保持了所有的方法

如果我們希望這些方法可在我們的myArray中被訪問,那麼myArray.__proto__ == Array.prototype就有用武之地了。

// constructor
function MyArray() { 
  this.stringify = function() {
    return this.join(', ')
  }
}
// Make sure that (new MyArray) has the right __proto__ 
MyArray.prototype = Array.prototype

// Test
var myArr = new MyArray()
myArr.push(1)
myArr.push(2)
alert( myArr.stringify() ) // 1, 2
alert( myArr.length ) // 2 in all except IE

方法借用

如果你僅僅想用Array的幾個方法的功能,那麼是不必要繼承Array的你可以borrow a method,然後apply it without inhereting, 比如

var join = Array.prototype.join  或者更短一點 var join = [].join

隨後使用一個非標準的this 來呼叫它:

var obj = {
  0: 'first',
  1: 'second',
  length: 2
}

alert( [].join.call(obj, ', ') ) // first, second

上例中, Array.prototype.join在ES-5標準中描述,他並不檢查物件type。它做的事情是一個對所有的屬性來做joining loop。Array的方法常常用來被借用以便操作array-like物件。

 

更改原生prototypes“修改”已經存在的原生方法

很多時候,你希望通過修改原生的方法,獲得你希望得到的特性,比如我們希望extend Array物件的join()方法,以便在將控制返回給原生join()方法之前增加一些log資訊。

Array.prototype.join = (function(_super) {

    // return our new `join()` function
    return function() {
        console.log("Hey, you called join!");
        return _super.apply(this, arguments);
    };         // Pass control back to the original join()
               // by using .apply on `_super`

})(Array.prototype.join);
//                            
// Pass the original function into our
// immediately invoked function as `_super`
// which remains available to our new
// function thanks to JavaScript closures.

 借用原生程式碼簡化重構自用的函式

有時候,我們會嫌直接使用原生函式方法太過囉嗦冗長,一個有效的辦法是借雞生蛋,重新包裝。比如我們常常需要就正規表示式來對一個字串做test,可行的辦法就是借用Function.bind返回一個有待執行的函式,後面直接簡單呼叫:

var testForA = RegExp.prototype.test.bind(/[aA]/);
var testForDigit = RegExp.prototype.test.bind(/[0-9]/);
  // function () { [native code] }

  testForA('hello');
  // false

  testForA('hella');
  // true

  ['hello', 'hella', 'holla'].filter(testForA);
  // ['hella', 'holla']

  testForDigit('this has no digits');  //false
testForDigit('this has 123 digits');  //true

 

http://prototypejs.org/

http://sugarjs.com/native

 

相關文章