深入理解javascript之原型
理解原型
原型是一個對象。其它對象能夠通過它實現屬性繼承。
不論什麽一個對象都能夠成為繼承,全部對象在默認的情況下都有一個原型。由於原型本身也是對象,所以每一個原型自身又有一個原型。
不論什麽一個對象都有一個prototype的屬性。記為:__proto__。
每當我們定義一個對象,其__proto__屬性就指向了其prototype。示比例如以下:
var foo = { x: 10, y: 20 };
即使我們不指定prototype,該屬性也會預留。假設我們有明白指向的話。那麽鏈表就連起來了。
須要註意的是,prototype自己也有指向,就是最高級的object.prototype。
示比例如以下:
var a = { x: 10, calculate: function (z) { return this.x + this.y + z } }; var b = { y: 20, __proto__: a }; var c = { y: 30, __proto__: a }; // call the inherited method b.calculate(30); // 60
使用原型
理解了原型的原理之後,怎樣使用原型呢?或者說原型有什麽作用呢?
一般的剛開始學習的人。在剛剛學習了主要的javascript語法後,都是通過面向函數來編程的。
例如以下代碼:
var decimalDigits = 2, tax = 5; function add(x, y) { return x + y; } function subtract(x, y) { return x - y; } //alert(add(1, 3));
通過運行各個函數來得到最後的結果。
可是利用原型,我們能夠優化一些我們的代碼,使用構造函數:
首先。函數本體中僅僅存放變量:
var Calculator = function (decimalDigits, tax) { this.decimalDigits = decimalDigits; this.tax = tax; };
其詳細的方法通過prototype屬性來設置:
Calculator.prototype = { add: function (x, y) { return x + y; }, subtract: function (x, y) { return x - y; } }; //alert((new Calculator()).add(1, 3));
這樣就能夠通過實例化對象後進行對應的函數操作。
這也是一般的js框架採用的方法。
原型另一個作用就是用來實現繼承。
首先。定義父對象:
var BaseCalculator = function() { this.decimalDigits = 2; }; BaseCalculator.prototype = { add: function(x, y) { return x + y; }, subtract: function(x, y) { return x - y; } };
然後定義子對象。將子對象的原型指向父元素的實例化:
var Calculator = function () { //為每一個實例都聲明一個稅收數字 this.tax = 5; }; Calculator.prototype = new BaseCalculator();
我們能夠看到Calculator的原型是指向到BaseCalculator的一個實例上,目的是讓Calculator集成它的add(x,y)和subtract(x,y)這2個function,另一點要說的是,因為它的原型是BaseCalculator的一個實例,所以無論你創建多少個Calculator對象實例,他們的原型指向的都是同一個實例。
上面的代碼。執行以後,我們能夠看到由於Calculator的原型是指向BaseCalculator的實例上的,所以能夠訪問他的decimalDigits屬性值,那假設我不想讓Calculator訪問BaseCalculator的構造函數裏聲明的屬性值,那怎麽辦呢?僅僅須要將Calculator指向BaseCalculator的原型而不是實例即可了。代碼例如以下:
var Calculator = function () { this.tax= 5; }; Calculator.prototype = BaseCalculator.prototype;
在使用第三方庫的時候。有時候他們定義的原型方法不能滿足我們的須要,我們就能夠自己加入一些方法。代碼例如以下:
//覆蓋前面Calculator的add() function Calculator.prototype.add = function (x, y) { return x + y + this.tax; }; var calc = new Calculator(); alert(calc.add(1, 1));
原型鏈
對象的原型指向對象的父。而父的原型又指向父的父,這樣的原型層層的關系。叫做原型鏈。
在查找一個對象的屬性時。javascript會向上遍歷原型鏈,直到找到給定名稱的屬性為止。當查找到達原型鏈的頂部,也即是Object.prototype。仍然沒有找到指定的屬性。就會返回undefined。
示比例如以下:
function foo() { this.add = function (x, y) { return x + y; } } foo.prototype.add = function (x, y) { return x + y + 10; } Object.prototype.subtract = function (x, y) { return x - y; } var f = new foo(); alert(f.add(1, 2)); //結果是3。而不是13 alert(f.subtract(1, 2)); //結果是-1
我們能夠發現,subtrace是依照向上找的原則,而add則出了意外。原因就是,屬性在查找的時候是先查找自身的屬性,假設沒有再查找原型。
說到Object.prototype,就不得不提它的一個方法。hasOwnProperty。
它能推斷一個對象是否包括自己定義屬性而不是原型鏈上的屬性,它是javascript中唯一一個處理屬性可是不查找原型鏈的函數。使用代碼例如以下:
// 改動Object.prototype Object.prototype.bar = 1; var foo = {goo: undefined}; foo.bar; // 1 'bar' in foo; // true foo.hasOwnProperty('bar'); // false foo.hasOwnProperty('goo'); // true
而為了推斷prototype對象和某個實例之間的關系,又不得不介紹isPrototyleOf方法,演演示樣例如以下:
alert(Cat.prototype.isPrototypeOf(cat2)); //true
深入理解javascript之原型