原文地址: 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