javascript 原型、原型鏈、對象複製等原理和樣本分析(下),javascript樣本

來源:互聯網
上載者:User

javascript 原型、原型鏈、對象複製等原理和樣本分析(下),javascript樣本

原型
原型是 JavaScript 物件導向特性中重要的概念,也是大家太熟悉的概念。因為在絕大多
數的物件導向語言中,對象是基於類的(例如 Java 和 C++ ) ,對象是類執行個體化的結果。而在
JavaScript 語言中,沒有類的概念
① ,對象由對象執行個體化。打個比方來說,基於類的語言中類
就像一個模具,對象由這個模具澆注產生,而基於原型的語言中,原型就好像是一件藝術品
的原件,我們通過一台 100% 精確的機器把這個原件複製出很多份。
前面小節的例子中都沒有涉及原型,僅僅通過建構函式和 new 語句產生類,讓我們看
看如何使用原型和建構函式共同產生對象。
function Person() {}
Person.prototype.name = ‘BYVoid’;
Person.prototype.showName = function() {
console.log(this.name);
};
var person = new Person();
person.showName();
上面這段代碼使用了原型而不是建構函式初始化對象。 這樣做與直接在建構函式內定義
屬性有什麼不同呢?
**建構函式內定義的屬性繼承方式與原型不同, 子物件需要顯式調用父物件才能繼承構
造函數內定義的屬性。
建構函式內定義的任何屬性, 包括函數在內都會被重複建立, 同一個建構函式產生的
兩個對象不共用執行個體。**
建構函式內定義的函數有運行時閉包的開銷, 因為建構函式內的局部變數對其中定義
的函數來說也是可見的。
下面這段代碼可以驗證以上問題:
function Foo() {
var innerVar = ‘hello’;
this.prop1 = ‘BYVoid’;
this.func1 = function() {
innerVar = ”;
};
}
Foo.prototype.prop2 = ‘Carbo’;
Foo.prototype.func2 = function() {
console.log(this.prop2);
};
var foo1 = new Foo();
var foo2 = new Foo();
console.log(foo1.func1 == foo2.func1); // 輸出 false
console.log(foo1.func2 == foo2.func2); // 輸出 true
儘管如此,並不是說在建構函式內建立屬性不好,而是兩者各有適合的範圍。那麼我們
什麼時候使用原型,什麼時候使用建構函式內定義來建立屬性呢?
**除非必須用建構函式閉包,否則盡量用原型定義成員函數,因為這樣可以減少開銷。
盡量在建構函式內定義一般成員, 尤其是對象或數組, 因為用原型定義的成員是多個
執行個體共用的。**
接下來,我們介紹一下JavaScript中的原型鏈機制。
原型鏈
JavaScript 中有兩個特殊的對象: Object 與 Function ,它們都是建構函式,用於生
成對象。 Object.prototype 是所有對象的祖先, Function.prototype 是所有函數的原
型,包括建構函式。我把 JavaScript 中的對象分為三類,
一類是使用者建立的對象,
一類是建構函式對象,
一類是原型對象。
使用者建立的對象,即一般意義上用 new 語句顯式構造的對象。
建構函式對象指的是普通的建構函式,即通過 new 調用產生普通對象的函數。
原型對象特指建構函式 prototype 屬性指向的對象。
這三類對象中每一類都有一個 proto 屬性,它指向該對象的原型,從任何對象沿著它開始遍曆都可以追溯到 Object.prototype 。
建構函式對象有 prototype 屬性,指向一個原型對象,通過該建構函式建立對象時,被建立對象的 proto 屬性將會指向建構函式的 prototype 屬性。
原型對象有 constructor屬性,指向它對應的建構函式。讓我們通過下面這個例子來理解原型:

function Foo() {}
Object.prototype.name = ‘My Object’;
Foo.prototype.name = ‘Bar’;
var obj = new Object();
var foo = new Foo();
console.log(obj.name); // 輸出 My Object
console.log(foo.name); // 輸出 Bar
console.log(foo.proto.name); // 輸出 Bar
console.log(foo.proto.proto.name); // 輸出 My Object
console.log(foo.proto.constructor.prototype.name); // 輸出 Bar

我們定義了一個叫做 Foo () 的建構函式,產生了對象 foo 。同時我們還分別給 Object和 Foo 產生原型對象。
解析了它們之間錯綜複雜的關係。

對象的複製
JavaScript 和 Java 一樣都沒有像C語言中一樣的指標,所有物件類型的變數都是指向對
象的引用,兩個變數之間賦值傳遞一個對象並不會對這個對象進行複製,而只是傳遞引用。
有些時候我們需要完整地複製一個對象,這該如何做呢? Java 語言中有 clone 方法可以實
現對象複製,但 JavaScript 中沒有這樣的函數。因此我們需要手動實現這樣一個函數,一個
簡單的做法是複製對象的所有屬性:

Object.prototype.clone = function() {
var newObj = {};
for (var i in this) {
newObj[i] = this[i];
}
return newObj;
}
var obj = {
name: ‘byvoid’,
likes: [‘node’]
};
var newObj = obj.clone();
obj.likes.push(‘python’);
console.log(obj.likes); // 輸出 [ ‘node’, ‘python’ ]
console.log(newObj.likes); // 輸出 [ ‘node’, ‘python’ ]

上面的代碼是一個對象淺拷貝(shallow copy)的實現,即只複製基本類型的屬性,而
共用物件類型的屬性。 淺拷貝的問題是兩個對象共用物件類型的屬性, 例如上例中 likes 屬
性指向的是同一個數組。
實現一個完全的複製,或深拷貝(deep copy)並不是一件容易的事,因為除了基本資料
類型,還有多種不同的對象,對象內部還有複雜的結構,因此需要用遞迴的方式來實現:

Object.prototype.clone = function() {
var newObj = {};
for (var i in this) {
if (typeof(this[i]) == ‘object’ || typeof(this[i]) == ‘function’) {
newObj[i] = this[i].clone();
} else {
newObj[i] = this[i];
}
}
return newObj;
};
Array.prototype.clone = function() {
var newArray = [];
for (var i = 0; i < this.length; i++) {
if (typeof(this[i]) == ‘object’ || typeof(this[i]) == ‘function’) {
newArray[i] = this[i].clone();
} else {
newArray[i] = this[i];
}
}
return newArray;

};
Function.prototype.clone = function() {
var that = this;
var newFunc = function() {
return that.apply(this, arguments);
};
for (var i in this) {
newFunc[i] = this[i];
}
return newFunc;
};
var obj = {
name: ‘byvoid’,
likes: [‘node’],
display: function() {
console.log(this.name);
},
};
var newObj = obj.clone();
newObj.likes.push(‘python’);
console.log(obj.likes); // 輸出 [ ‘node’ ]
console.log(newObj.likes); // 輸出 [ ‘node’, ‘python’ ]
console.log(newObj.display == obj.display); // 輸出 false

上面這個實現看起來很完美,它不僅遞迴地複製了對象複雜的結構,還實現了函數的深
拷貝。這個方法在大多數情況下都很好用,但有一種情況它卻無能為力,例如下面的代碼:
var obj1 = {
ref: null
};
var obj2 = {
ref: obj1
};
obj1.ref = obj2;
這段代碼的邏輯非常簡單,就是兩個相互引用的對象。當我們試圖使用深拷貝來複製
obj1 和 obj2 中的任何一個時,問題就出現了。因為深拷貝的做法是遇到對象就進行遞迴
複製,那麼結果只能無限迴圈下去。對於這種情況,簡單的遞迴已經無法解決,必須設計一
套**圖論演算法, 分析對象之間的依賴關係, 建立一個拓撲結構圖, 然後分別依次複製每個頂點,
並重新構建它們之間的依賴關係**。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.