您好,登錄后才能下訂單哦!
本文實例講述了javascript 原型與原型鏈的理解。分享給大家供大家參考,具體如下:
javascript中一切皆對象,但是由于沒有Class類的概念,所以就無法很好的表達對象與對象之間的關系了。
比如對象A與對象B之間,它們兩個是相對獨立的個體,互不干擾,對象A修改自身的屬性不會影響到對象B。
雖然這很好,但是有一個問題,如果對象A與對象B都有一個方法 run() ,并且代碼也一樣,那對象A與對象B各自都獨立擁有一份 run() 方法的完整代碼,這是需要資源去保存的。
一旦我們程序中應用的對象過多,那這種資源消耗會是巨大的。那有沒有一種方法可以讓對象A與對象B擁有一些公共的屬性和方法,讓它們之前有某種聯系?
我們設想一下,會不會存在一個 common對象(公共對象),common對象上保存著公共的屬性和方法,而對象A與對象B里面有一個prototype屬性指向這個 common對象,
當然我們調用對象A或對象B的屬性和方法時,如果在自身對象中沒有找到,就去prototype這個屬性指向的對象上面去找。
而common對象本身也有一個prototype屬性指向更上一級的common對象,然后一直往上找啊找,直到為null,就停止。
這種不斷的從下往上找的這種路徑,就像鏈條一樣,我們稱它為 原型鏈,而那個common對象,我們稱它為 原型對象。
我們來看一個構造函數
function Base(name) { this.name = name; } let A = new Base('A'); let B = new Base('B'); //每一個函數都有一個prototype屬性,指向該函數的原型對象 console.log(Base.prototype); //當然原型對象也是一個對象,它也有一個constructor,指向構造函數 console.log(Base.prototype.constructor === Base); //每一個實例對象的constructor都指向創建它們的構造函數 console.log(A.constructor === Base); console.log(B.constructor === Base); //每一個實例對象都有一個__proto__屬性,該屬性指向構造函數的原型對象 console.log(A.__proto__ === Base.prototype); console.log(B.__proto__ === Base.prototype);
1、每一個函數都有一個prototype屬性,它指向該函數的原型對象。
2、原型對象也是對象,它也有自已的constructor,它指向構造函數Base()。換句話說,其實原型對象也是構造函數Base()的一個實例。只不過比較特殊,用來存放公共屬性和方法的。
3、每一個通過構造函數Base()創建的實例對象,都有一個constructor,指向創建它們的構造函數。
4、每一個對象,都有一個 __proto__ 屬性,指向構造函數Base()的 原型對象。換句話說,__proto__ 是將 原型 串聯起來形成鏈條的關鍵。不然對象A與對象B都無法找到原型對象上的公共屬性和方法。
function Base(name) { this.name = name; } //我們在原型對象上添加公共屬性 Base.prototype.status = '開始'; //我們在原型對象上添加公共方法 Base.prototype.run = function() { console.log(this.name + ' run ...'); }; let A = new Base('A'); let B = new Base('B'); A.run(); B.run(); console.log(A.status); console.log(B.status); //修改原型上的屬性,則實例對象也會跟著改變 Base.prototype.status = '停止'; console.log(A.status); console.log(B.status);
通過原型與原型鏈,讓對象與對象之間有了關聯關系。
那如何通過原型與原型鏈,讓一個構造函數繼承于另一個構造函數?
比如,我們要讓構造函數Child 繼承于 構造函數Base,只需要讓 Child 的 prototype 指向 Base的 原型對象,不就可以了?
function Base(name) { } Base.prototype.name = 'Base'; Base.prototype.run = function () { console.log(this.name + ' run ...'); }; function Child() { } Child.prototype = Base.prototype; //注意這個時候,Child.prototype對象的constructor屬性指向了Base //這就導致通過構造函數Child創建的實例對象,對象的constructor屬性會指向Base,而不是Child,這會導致混亂。 //所以我們重新設置Child.prototype.constructor指向Child Child.prototype.constructor = Child; let c = new Child(); console.log(c.name); c.run();
這樣有一個問題,Child.prototype 與 Base.prototype 指向同一個原型對象,任何對 Child.prototype 的修改都會反應到 Base.prototype 上面。
這時,Base.prototype.constructor 指向了 Child,這顯然是有問題。
我們只能通過一個中間的空構造函數,來完成原型的指向。
function Base(name) { } Base.prototype.name = 'Base'; Base.prototype.run = function () { console.log(this.name + ' run ...'); }; function Child() { } //創建一個中間的空構造函數 function Mid() { } //讓該空構造函數的prototype指向Base的原型對象 Mid.prototype = Base.prototype; //再讓Child的prototype指向該空構造函數的一個實例 Child.prototype = new Mid(); //這樣,當修改Child.prototype.constructor時,Base.prototype就不會受影響了 Child.prototype.constructor = Child; let c = new Child(); console.log(c.name); c.run(); //Base.prototype的constructor仍然指向Base,沒有受到影響 console.log(Base.prototype.constructor);
那怎么通過原型與原型鏈,讓你一對象繼承于另一個對象呢?
比如,我們要讓對象B繼承于對象A,無非就是想要拿到對象A的屬性和方法,這么一想,那通過把對象B的 __proto__ 指向 對象A,不就可以實現了?
let A = { name: 'A', run() { console.log(this.name + ' run ...'); } }; console.log(A.name); A.run(); let B = {}; //讓對象B的__proto__指向對象A B.__proto__ = A; //當對象B調用run()方法時會在自身上找,如果沒找到,則通過__proto__向上找 //由于__proto__指向對象A,所以最終會在對象A中找到run()方法 B.run(); B.__proto__.name = 'B'; console.log(A.name); console.log(B.name);
這樣有一個問題,當修改 B.__proto__.name = 'B'; 時,對象A也會受到影響。
我們可以通過ES5提供的 Object.create() 來解決此問題,Object.create()可以通過指定的 原型對象 創建一個新對象。
let A = { name: 'A', run() { console.log(this.name + ' run ...'); } }; console.log(A.name); A.run(); let B = {}; //通過Object.create()創建一個以對象A為原型對象的新對象 //讓對象B的__proto__指向該新對象 //這樣再操作B.__proto__中的屬性就與對象A無關了。 B.__proto__ = Object.create(A); B.run(); B.__proto__.name = 'B'; console.log(A.name); console.log(B.name);
感興趣的朋友可以使用在線HTML/CSS/JavaScript代碼運行工具:http://tools.jb51.net/code/HtmlJsRun測試上述代碼運行效果。
更多關于JavaScript相關內容感興趣的讀者可查看本站專題:《javascript面向對象入門教程》、《JavaScript錯誤與調試技巧總結》、《JavaScript數據結構與算法技巧總結》、《JavaScript遍歷算法與技巧總結》及《JavaScript數學運算用法總結》
希望本文所述對大家JavaScript程序設計有所幫助。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。