- 資訊首頁(yè) > 開(kāi)發(fā)技術(shù) >
- Javascript中怎么實(shí)現原型和原型鏈
今天就跟大家聊聊有關(guān)Javascript中怎么實(shí)現原型和原型鏈,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。
原型
Javascript中有一句話(huà),叫一切皆是對象,當然這句話(huà)也不嚴謹,比如null和undefined就不是對象,除了這倆完全可以說(shuō)Javascript一切皆是對象。而Javascript對象都有一個(gè)叫做原型的公共屬性,屬性名是_proto_。這個(gè)原型屬性是對另一個(gè)對象的引用,通過(guò)這個(gè)原型屬性我們就可以訪(fǎng)問(wèn)另一個(gè)對象所有的屬性和方法。比如:
let numArray = [1, 2, -8, 3, -4, 7];
Array對象就有一個(gè)原型屬性指向Array.prototype,變量numArray繼承了Array.prototype對象所有的屬性和方法。
這就是為什么可以直接調用像sort()這種方法:
console.log(numArray.sort()); // -> [-4, -8, 1, 2, 3, 7]
也就是說(shuō):
numArray.__proto__ === Array.prototype // true
對于其他對象(函數)也是一樣(比如Date(),Function(), String(),Number()等);
當一個(gè)構造函數被創(chuàng )建后,實(shí)例對象會(huì )繼承構造函數的原型屬性,這是構造函數的一個(gè)非常重要的特性。在Javascript中使用new關(guān)鍵字來(lái)對構造函數進(jìn)行實(shí)例化??聪旅娴睦樱?/p>
const Car = function(color, model, dateManufactured) { this.color = color; this.model = model; this.dateManufactured = dateManufactured; }; Car.prototype.getColor = function() { return this.color; }; Car.prototype.getModel = function() { return this.model; }; Car.prototype.carDate = function() { return `This ${this.model} was manufactured in the year ${this.dateManufactured}` } let firstCar = new Car('red', 'Ferrari', '1985'); console.log(firstCar); console.log(firstCar.carDate());
上面的例子中,方法getColor,carDate,getModel都是對象(函數)Car的方法,而Car的實(shí)例對象firstCar可以繼承Car原型上的一切方法和屬性。
結論:每一個(gè)實(shí)例對象都有一個(gè)私有屬性_proto_,指向它的構造函數的原型對象(prototype)。
原型鏈
在Javascript中如果訪(fǎng)問(wèn)一個(gè)對象本身不存在的屬性或是方法,就首先在它的原型對象上去尋找,如果原型對象上也不存在,就繼續在原型對象的原型對象上去尋找,直到找到為止。那么原型對象有盡頭么?所有對象的原型盡頭是Object.prototype,那么Object.prototype這個(gè)對象的_proto_指向啥呢?答案是null。我們日常開(kāi)發(fā)中用到的絕大多數對象的_proto_基本不會(huì )直接指向Object.prototype,基本都是指向另一個(gè)對象。比如所有的函數的_proto_都會(huì )指向Function.prototype,所有數組的_proto_都會(huì )指向Array.prototype。
let protoRabbit = { color: 'grey', speak(line) { console.log(`The ${this.type} rabbit says ${line}`); } }; let killerRabbit = Object.create(protoRabbit); killerRabbit.type = "assassin"; killerRabbit.speak("SKREEEE!");
上面代碼中變量protoRabbit設置為所有兔子對象的公有屬性對象集,killerRabbit這只兔子通過(guò)Object.create方法繼承了protoRabbit的所有屬性和方法,然后給killerRabbit賦值了一個(gè)type屬性,再看下面的代碼:
let mainObject = { bar: 2 }; // create an object linked to `anotherObject` let myObject = Object.create( mainObject ); for (let k in myObject) { console.log("found: " + k); } // found: bar ("bar" in myObject);
如上變量myObject本身并沒(méi)有bar屬性,但這里會(huì )循著(zhù)原型鏈一層一層往上找,直到找到或者原型鏈結束為止。如果到原型鏈盡頭還是沒(méi)找到該屬性,那么訪(fǎng)問(wèn)該屬性的時(shí)候就會(huì )返回undefined了。
使用for...in關(guān)鍵字對對象進(jìn)行迭代的過(guò)程,和上面訪(fǎng)問(wèn)某個(gè)屬性循著(zhù)原型鏈查找類(lèi)似,會(huì )去遍歷所有原型鏈上的屬性(不論屬性是否可枚舉)。
let protoRabbit = { color: 'grey', speak(line) { console.log(`The ${this.type} rabbit says ${line}`); } }; let killerRabbit = Object.create(protoRabbit); killerRabbit.type = "assassin"; killerRabbit.speak("SKREEEE!");
上面的代碼中訪(fǎng)問(wèn)speak的效率很高,但如果我們想創(chuàng )建很多個(gè)Rabbit對象,就必須要重復寫(xiě)很多代碼。而這正是原型和構造函數的真正用武之地。
let protoRabbit = function(color, word, type) { this.color = color; this.word = word; this.type = type; }; protoRabbit.prototype.getColor = function() { return this.color; } protoRabbit.prototype.speak = function() { console.log(`The ${this.type} rabbit says ${this.word}`); } let killerRabbit = new protoRabbit('grey', 'SKREEEEE!', 'assassin'); killerRabbit.speak();
如上代碼,使用構造函數的方式就可以節省很多的代碼。
結論:每一個(gè)實(shí)例對象都有一個(gè)私有屬性_proto_,指向它的構造函數的原型對象(prototype)。原型對象也有自己的_proto_,層層向上直到一個(gè)對象的原型對象為null。這一層層原型就是原型鏈。
附贈一張原型鏈的圖:
創(chuàng )建對象的四種方法
字面量對象
這是比較常用的一種方式:
let obj = {};
構造函數創(chuàng )建
構造函數創(chuàng )建的方式更多用來(lái)在Javascript中實(shí)現繼承,多態(tài),封裝等特性。
function Animal(name) { this.name = name; } let cat = new Animal('Tom');
class創(chuàng )建
class關(guān)鍵字是ES6新引入的一個(gè)特性,它其實(shí)是基于原型和原型鏈實(shí)現的一個(gè)語(yǔ)法糖。
class Animal { constructor(name) { this.name = name; } } let cat = new Animal('Tom');
擴展原型鏈的四種方法
構造函數創(chuàng )建
上面例子有用到使用構造函數創(chuàng )建對象的例子,我們再來(lái)看一個(gè)實(shí)際的例子:
function Animal(name) { this.name = name; } Animal.prototype = { run() { console.log('跑步'); } } let cat = new Animal('Tom'); cat.__proto__ === Animal.prototype; // true Animal.prototype.__proto__ === Object.prototype; // true
優(yōu)點(diǎn):支持目前以及所有可想象到的瀏覽器(IE5.5都可以使用). 這種方法非???,非常符合標準,并且充分利用JIST優(yōu)化。
缺點(diǎn):為使用此方法,這個(gè)問(wèn)題中的函數必須要被初始化。另外構造函數的初始化,可能會(huì )給生成對象帶來(lái)并不想要的方法和屬性。
Object.create
ECMAScript 5 中引入了一個(gè)新方法: Object.create()??梢哉{用這個(gè)方法來(lái)創(chuàng )建一個(gè)新對象。新對象的原型就是調用 create 方法時(shí)傳入的第一個(gè)參數:
var a = {a: 1}; // a ---> Object.prototype ---> null var b = Object.create(a); b.__proto__ === a; // true
優(yōu)點(diǎn): 支持當前所有非微軟版本或者 IE9 以上版本的瀏覽器。允許一次性地直接設置 __proto__ 屬性,以便瀏覽器能更好地優(yōu)化對象。同時(shí)允許通過(guò) Object.create(null) 來(lái)創(chuàng )建一個(gè)沒(méi)有原型的對象。
缺點(diǎn):不支持 IE8 以下的版本;這個(gè)慢對象初始化在使用第二個(gè)參數的時(shí)候有可能成為一個(gè)性能黑洞,因為每個(gè)對象的描述符屬性都有自己的描述對象。當以對象的格式處理成百上千的對象描述的時(shí)候,可能會(huì )造成嚴重的性能問(wèn)題。
Object.setPrototypeOf
語(yǔ)法:
Object.setPrototypeOf(obj, prototype)
參數:
var a = { n: 1 }; var b = { m : 2 }; Object.setPrototypeOf(a, b); a.__proto__ === b; // true
優(yōu)點(diǎn):支持所有現代瀏覽器和微軟IE9+瀏覽器。允許動(dòng)態(tài)操作對象的原型,甚至能強制給通過(guò) Object.create(null) 創(chuàng )建出來(lái)的沒(méi)有原型的對象添加一個(gè)原型。
缺點(diǎn):這個(gè)方式表現并不好,應該被棄用;動(dòng)態(tài)設置原型會(huì )干擾瀏覽器對原型的優(yōu)化;不支持 IE8 及以下的瀏覽器版本。
_proto_
var a = { n: 1 }; var b = { m : 2 }; a.__proto__ = b; a.__proto__ === b; // true
使用_proto_也可以動(dòng)態(tài)設置對象的原型。
優(yōu)點(diǎn):支持所有現代非微軟版本以及 IE11 以上版本的瀏覽器。將 __proto__ 設置為非對象的值會(huì )靜默失敗,并不會(huì )拋出錯誤。
缺點(diǎn):應該完全將其拋棄因為這個(gè)行為完全不具備性能可言;干擾瀏覽器對原型的優(yōu)化;不支持 IE10 及以下的瀏覽器版本。
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自互聯(lián)網(wǎng)轉載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權請聯(lián)系QQ:712375056 進(jìn)行舉報,并提供相關(guān)證據,一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容。
Copyright ? 2009-2021 56dr.com. All Rights Reserved. 特網(wǎng)科技 特網(wǎng)云 版權所有 珠海市特網(wǎng)科技有限公司 粵ICP備16109289號
域名注冊服務(wù)機構:阿里云計算有限公司(萬(wàn)網(wǎng)) 域名服務(wù)機構:煙臺帝思普網(wǎng)絡(luò )科技有限公司(DNSPod) CDN服務(wù):阿里云計算有限公司 中國互聯(lián)網(wǎng)舉報中心 增值電信業(yè)務(wù)經(jīng)營(yíng)許可證B2
建議您使用Chrome、Firefox、Edge、IE10及以上版本和360等主流瀏覽器瀏覽本網(wǎng)站