多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國內(nèi)最全I(xiàn)T社區(qū)平臺 聯(lián)系我們 | 收藏本站
阿里云優(yōu)惠2
您當(dāng)前位置:首頁 > web前端 > htmlcss > 你不知道的JavaScript--Item14 使用prototype的幾點注意事項

你不知道的JavaScript--Item14 使用prototype的幾點注意事項

來源:程序員人生   發(fā)布時間:2016-02-29 17:04:17 閱讀次數(shù):3100次

1、在prototype上保存方法

不使用prototype進(jìn)行JavaScript的編碼是完全可行的,例如:

function User(name, passwordHash) { this.name = name; this.passwordHash = passwordHash; this.toString = function() { return "[User " + this.name + "]"; }; this.checkPassword = function(password) { return hash(password) === this.passwordHash; }; } var u1 = new User(/* ... */); var u2 = new User(/* ... */); var u3 = new User(/* ... */);

當(dāng)創(chuàng)建了多個User類型的實例時,就存在問題了:不但是name和passwordHash屬性在每一個實例上都存在,toString和checkPassword方法在每一個實例上都有1份拷貝。就像下圖表示的那樣:

這里寫圖片描述

但是,當(dāng)toString和checkPassword被定義在prototype上時,上圖就變成下面這個模樣了:

這里寫圖片描述

toString和checkPassword方法現(xiàn)在定義在了User.prototype對象上,也就意味著這兩個方法只存在1份拷貝,并被所有的User實例同享。

或許你會認(rèn)為將方法作為拷貝放在每一個實例上,會節(jié)省方法查詢的時間。(當(dāng)方法定義在prototype上時,首先會在實例本身上尋覓方法,如果沒有找到才會去prototype上繼續(xù)找)

但是在現(xiàn)代的JavaScript履行引擎中,對方法的查詢進(jìn)行了大量優(yōu)化,所以這個查詢時間幾近是不需要斟酌的,那末將方法放在prototype對象上就節(jié)省了很多內(nèi)存。

2、使用閉包來保存私有數(shù)據(jù)

JavaScript的對象系統(tǒng)從其語法上而言其實不鼓勵使用信息隱藏(Information Hiding)。由于當(dāng)使用諸如this.name,this.passwordHash的時候,這些屬性默許的訪問級別就是public的,在任何位置都能夠通過obj.name,obj.passwordHash來對這些屬性進(jìn)行訪問。

在ES5環(huán)境中,也提供了1些方法來更方便的訪問1個對象上所有的屬性,比如Object.keys(),Object.getOwnPropertyNames()。所以,1些開發(fā)人員使用1些規(guī)約來定義JavaScript對象的私有屬性,比如最典型的是使用下劃線作為屬性的前綴來告知其他開發(fā)人員和用戶這個屬性是不應(yīng)當(dāng)被直接訪問的。

但是這樣做,其實不能從根本上解決問題。其他開發(fā)人員和用戶還是能夠?qū)в邢聞澗€的屬性進(jìn)行直接訪問。對確切需要私有屬性的場合,可使用閉包進(jìn)行實現(xiàn)。

從某種意義而言,在JavaScript中,閉包對變量的訪問策略和對象的訪問策略是兩個極端。閉包中的任何變量默許都是私有的,只有在函數(shù)內(nèi)部才能訪問這些變量。比如,可以將User類型實現(xiàn)以下:

function User(name, passwordHash) { this.toString = function() { return "[User " + name + "]"; }; this.checkPassword = function(password) { return hash(password) === passwordHash; }; }

此時,name和passwordHash都沒有被保存為實例的屬性,而是通過局部變量進(jìn)行保存。然后根據(jù)閉包的訪問規(guī)則,實例上的方法可以對它們進(jìn)行訪問,而在其它地方則不能。

使用這類模式的1個缺點是,利用了局部變量的方法都需要被定義在實例本身上,不能講這些方法定義在prototype對象上。正如在Item34中討論的那樣,這樣做的問題是會增加內(nèi)存的消耗。但是在某些特別的場合下,即便將方法定義在實例上也是可行的。

3、實例狀態(tài)只保存在實例對象上

1個類型的prototype和該類型的實例之間是”1對多“的關(guān)系。那末,需要確保實例相干的數(shù)據(jù)不會被毛病地保存在prototype之上。比如,對1個實現(xiàn)了樹結(jié)構(gòu)的類型而言,將它的子節(jié)點保存在該類型的prototype上就是不正確的:

function Tree(x) { this.value = x; } Tree.prototype = { children: [], // should be instance state! addChild: function(x) { this.children.push(x); } }; var left = new Tree(2); left.addChild(1); left.addChild(3); var right = new Tree(6); right.addChild(5); right.addChild(7); var top = new Tree(4); top.addChild(left); top.addChild(right); top.children; // [1, 3, 5, 7, left, right]

當(dāng)狀態(tài)被保存到了prototype上時,所有實例的狀態(tài)都會被集中地保存,在上面這類場景中明顯是不正確的:本來屬于每一個實例的狀態(tài)被毛病地同享了。以下圖所示:

這里寫圖片描述

正確的實現(xiàn)應(yīng)當(dāng)是這樣的:

function Tree(x) { this.value = x; this.children = []; // instance state } Tree.prototype = { addChild: function(x) { this.children.push(x); } };

此時,實例狀態(tài)的存儲以下所示:

這里寫圖片描述

可見,當(dāng)本屬于實例的狀態(tài)被同享到prototype上時,或許會產(chǎn)生問題。在需要在prototype上保存狀態(tài)屬性前,1定要確保該屬性是能夠被同享的。

整體而言,當(dāng)1個屬性是不可變(無狀態(tài))的屬性時,就可以將它保存在prototype對象上(比如方法能夠被保存在prototype對象上就是由于這1點)。固然,有狀態(tài)的屬性也能夠被放在prototype對象上,這要取決于具體的利用場景,典型的比如用來記錄1個類型實例數(shù)量的變量。使用Java語言作為類比的話,這類能夠存儲在prototype對象上的變量就是Java中的類變量(使用static關(guān)鍵字修飾)。

4、避免繼承標(biāo)準(zhǔn)類型

ECMAScript標(biāo)準(zhǔn)庫不大,但是提供了1些重要的類型如Array,F(xiàn)unction和Date。在1些場合下,你或許會斟酌繼承其中的某個類型來實現(xiàn)特定的功能,但是這類做法其實不被鼓勵。

比如為了操作1個目錄,可讓目錄類型繼承Array類型以下:

function Dir(path, entries) { this.path = path; for (var i = 0, n = entries.length; i < n; i++) { this[i] = entries[i]; } } Dir.prototype = Object.create(Array.prototype); // extends Array var dir = new Dir("/tmp/mysite", ["index.html", "script.js", "style.css"]); dir.length; // 0

但是可以發(fā)現(xiàn),dir.length的值是0,而不是期待中的3。

產(chǎn)生這類現(xiàn)象的緣由在于:只有當(dāng)對象是真實的Array類型時,length屬性才會起作用。

在ECMAScript標(biāo)準(zhǔn)中,定義了1個不可見的內(nèi)部屬性被稱為 [[class]]。該屬性的值只是1個字符串,所以不要被誤導(dǎo)認(rèn)為JavaScript也實現(xiàn)了自己的類型系統(tǒng)。所以,對Array類型,這個屬性的值就是“Array”;對Function類型,這個屬性的值就是“Function”。下表是ECMAScript定義的所有[[class]] 值:

那末當(dāng)對象的類型確切是Array時,length屬性的特別的地方就在于:length的值會和該對象中被索引的屬性個數(shù)保持1致。比如對1個數(shù)組對象arr,arr[0]和arr[1]就表示該對象有兩個被索引的屬性,那末length的值就是2。當(dāng)添加了arr[2]的時候,length的值會被自動同步成3。一樣地,當(dāng)設(shè)置length值為2時,arr[2]會被自動設(shè)置成undefined。

但是當(dāng)繼承Array類型并創(chuàng)建實例時,該實例的 [[class]] 屬性其實不是Array,而是Object。因此length屬性不能正確的工作。

在JavaScript中,也提供了用于查詢 [[class]] 屬性的方法,即便用Object.prototype.toString方法:

var dir = new Dir("/", []); Object.prototype.toString.call(dir); // "[object Object]" Object.prototype.toString.call([]); // "[object Array]"

因此,更好的實現(xiàn)方法是使用組合而不是繼承:

function Dir(path, entries) { this.path = path; this.entries = entries; // array property } Dir.prototype.forEach = function(f, thisArg) { if (typeof thisArg === "undefined") { thisArg = this; } this.entries.forEach(f, thisArg); };

以上代碼將不再使用繼承,而是將1部份功能代理給內(nèi)部的entries屬性來實現(xiàn),該屬性的值是1個Array類型對象。

ECMAScript標(biāo)準(zhǔn)庫中,大部份的構(gòu)造函數(shù)都會依賴內(nèi)部屬性值如 [[class]] 來實現(xiàn)正確的行動。對繼承這些標(biāo)準(zhǔn)類型的子類型,沒法保證它們的行動是正確的。因此,不要繼承ECMAScript標(biāo)準(zhǔn)庫中的類型如:
Array, Boolean, Date, Function, Number,RegExp,String

生活不易,碼農(nóng)辛苦
如果您覺得本網(wǎng)站對您的學(xué)習(xí)有所幫助,可以手機掃描二維碼進(jìn)行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關(guān)閉
程序員人生
主站蜘蛛池模板: 毛片网此 | 空姐一级毛片 | 91刘亦菲精品福利在线 | 久久性久久性久久久爽 | 日韩午夜网站 | 亚洲国产精品综合久久一线 | 国产福利第一视频 | 九色 在线 | 久久se精品一区精品二区 | 国内久久久久久久久久 | 精品久久久久久中文字幕一区 | 亚洲综合亚洲国产尤物 | 午夜色影院 | 性欧美video另类hd | 一二三四视频免费视频 | 国产精品嫩草影院99av视频 | 亚洲一区二区三区高清视频 | 在线观看视频中文字幕 | 国产亚洲一区二区三区不卡 | 国产精品视频第一页 | 久久国产精品亚洲一区二区 | 国产亚洲综合成人91精品 | 美女教师一级毛片 | 美国爱爱片视频在线观看 | 国产亚洲精品国产福利在线观看 | www.欧美xxx| 亚洲成人一区二区 | 91久久精品国产免费一区 | www干| 香蕉狠狠再啪线视频 | 亚洲地址一地址二地址三 | 国产日韩精品一区二区在线观看播放 | 国产最新精品视频 | 国产福利精品在线观看 | 免费在线观看亚洲 | 中文一区在线观看 | 婷婷成人亚洲 | 亚洲国产高清人在线 | 美女被h| 久久精品一区二区三区日韩 | 最近免费中文字幕高清大全 |