摘要:類(lèi)是由一個(gè)構(gòu)造函數(shù)和一個(gè)關(guān)聯(lián)的原型組成的一種設(shè)計(jì)模式。該模式的一個(gè)缺點(diǎn)是,為了讓構(gòu)造函數(shù)中的變量在使用它們的方法的作用域內(nèi),這些方法必須放置于實(shí)例對(duì)象中,這會(huì)導(dǎo)致方法副本的擴(kuò)散。
參考書(shū)籍:《Effective JavaScript》
對(duì)象和原型 理解prototype、getPrototypeOf和__proto__之間的不同原型包括三個(gè)獨(dú)立但相關(guān)的訪問(wèn)器。
C.prototype用于建立由new C()創(chuàng)建的對(duì)象的原型。
Object.getPrototypeOf(obj)是ES5中用來(lái)獲取obj對(duì)象的原型對(duì)象的標(biāo)準(zhǔn)方法。
obj.__proto__是獲取obj對(duì)象的原型對(duì)象的非標(biāo)準(zhǔn)方法。
function User(name, passwordHash) { this.name = name; this.passwordHash = passwordHash; } User.prototype.toString = function () { return "[User " + this.name + "]"; }; User.prototype.checkPassword = function (password) { return hash(password) === this.passwordHash; } var u = new User("sfalken", "0ef33ae791068ec64b502d6cb0191387");
User函數(shù)帶有一個(gè)默認(rèn)的prototype屬性,其包含一個(gè)開(kāi)始幾乎為空的對(duì)象。當(dāng)我們使用new操作符創(chuàng)建User的實(shí)例時(shí),產(chǎn)生的對(duì)象u得到了自動(dòng)分配的原型對(duì)象,該原型對(duì)象被存儲(chǔ)在User.prototype中。
Object.getPrototypeOf(u) === User.prototype; // true
u.__proto__ === User.prototype; // true
提示:
C.prototype屬性是new C()創(chuàng)建的對(duì)象的原型。
Object.getPrototypeOf(obj)是ES5中檢索對(duì)象原型的標(biāo)準(zhǔn)函數(shù)。
Obj.__proto__是檢索對(duì)象原型的非標(biāo)準(zhǔn)函數(shù)。
類(lèi)是由一個(gè)構(gòu)造函數(shù)和一個(gè)關(guān)聯(lián)的原型組成的一種設(shè)計(jì)模式。
使用Object.getPrototypeOf函數(shù)而不要使用__proto__屬性__proto__屬性提供了Object.getPrototypeOf方法所不具備的額外能力,即修改對(duì)象原型鏈接的能力。這種能力會(huì)造成嚴(yán)重的影響,應(yīng)當(dāng)避免使用,原因如下:
可移植性:并不是所有的平臺(tái)都支持改變對(duì)象原型的特性,所以無(wú)法編寫(xiě)可移植的代碼。
性能問(wèn)題:現(xiàn)代的JavaScript引擎痘深度優(yōu)化了獲取和設(shè)置對(duì)象屬性的行為,如更改了對(duì)象的內(nèi)部結(jié)構(gòu)(如添加或刪除該對(duì)象或其原型鏈中的對(duì)象的屬性)會(huì)使一些優(yōu)化失效。
可預(yù)測(cè)性:修改對(duì)象的原型鏈會(huì)影響對(duì)象的整個(gè)繼承層次結(jié)構(gòu),在某些情況下這樣的操作可能有用,但是保持繼承層次結(jié)構(gòu)的相對(duì)穩(wěn)定是一個(gè)基本的準(zhǔn)則。
可以使用ES5中的Object.create函數(shù)來(lái)創(chuàng)建一個(gè)具有自定義原型鏈的新對(duì)象。
提示::
始終不要修改對(duì)象的__proto__屬性。
使用Object.create函數(shù)給新對(duì)象設(shè)置自定義的原型。
使構(gòu)造函數(shù)與new操作符無(wú)關(guān)function User(name, passwordHash) { this.name = name; this.passwordHash = passwordHash; } var u = User("baravelli", "d8b74df393528d51cd19980ae0aa028e"); u; // undefined this.name; // baravelli this.passwordHash; // d8b74df393528d51cd19980ae0aa028e
如果調(diào)用者忘記使用new關(guān)鍵字,該函數(shù)不但會(huì)返回?zé)o意義的undefined,而且會(huì)創(chuàng)建(如果這些全局變量已經(jīng)存在則會(huì)修改)全局變量name和passwordHash。
如果將User函數(shù)定義為ES5的嚴(yán)格代碼,那么它的接收者默認(rèn)為undefined。
function User(name, passwordHash) { "use strict"; this.name = name; this.passwordHash = passwordHash; } var u = User("baravelli", "d8b74df393528d51cd19980ae0aa028e"); // Uncaught TypeError: Cannot set property "name" of undefined
一個(gè)更為健壯的方式是提供一個(gè)不管怎么調(diào)用都工作如構(gòu)造函數(shù)的函數(shù)。
function User(name, passwordHash) { if (!this instanceof User) { return new User(name, passwordHash); } this.name = name; this.passwordHash = passwordHash; } var x = User("baravelli", "d8b74df393528d51cd19980ae0aa028e"); var y = new User("baravelli", "d8b74df393528d51cd19980ae0aa028e"); x instanceof User; // true y instanceof User; // true
上述模式的一個(gè)缺點(diǎn)是它需要額外的函數(shù)調(diào)用,且難適用于可變參數(shù)函數(shù),因?yàn)闆](méi)有一種模擬apply方法將可變參數(shù)函數(shù)作為構(gòu)造函數(shù)調(diào)用的方式。
一種更為奇異的方式是利用ES5的Object.create函數(shù)。
function User(name, passwordHash) { var self = this instanceof User ? this : Object.create(User.prototype); self.name = name; self.passwordHash = passwordHash; return self; }
Object.create需要一個(gè)原型對(duì)象作為參數(shù),并返回一個(gè)繼承自原型對(duì)象的新對(duì)象。
多虧了構(gòu)造函數(shù)覆蓋模式,使用new操作符調(diào)用上述User函數(shù)的行為與以函數(shù)調(diào)用它的行為是一樣的,這能工作完全得益于JavaScript允許new表達(dá)式的結(jié)果可以被構(gòu)造函數(shù)的顯示return語(yǔ)句所覆蓋。
提示:
通過(guò)使用new操作符或Object.create方法在構(gòu)造函數(shù)定義中調(diào)用自身使得該構(gòu)造函數(shù)與調(diào)用語(yǔ)法無(wú)關(guān)。
當(dāng)一個(gè)函數(shù)期望使用new操作符調(diào)用時(shí),清晰地文檔化該函數(shù)。
在原型中存儲(chǔ)方法JavaScript完全有可能不借助原型進(jìn)行編程。
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(/* ... */);
上述代碼中的每個(gè)實(shí)例都包含toString和checkPassword方法的副本,而不是通過(guò)原型共享這些方法。
將方法存儲(chǔ)在原型,使其可以被所有的實(shí)例使用,而不需要存儲(chǔ)方法實(shí)現(xiàn)的多個(gè)副本,也不需要給每個(gè)實(shí)例對(duì)象增加額外的屬性。
同時(shí),現(xiàn)代的JavaScript引擎深度優(yōu)化了原型查找,所以將方法復(fù)制到實(shí)例對(duì)象并不一定保證查找的速度有明顯的提升,而且實(shí)例方法比起原型方法肯定會(huì)占用更多的內(nèi)存。
提示:
將方法存儲(chǔ)在實(shí)例對(duì)象中會(huì)創(chuàng)建該函數(shù)的多個(gè)副本,因?yàn)槊總€(gè)實(shí)例對(duì)象都有一份副本。
將方法存儲(chǔ)于原型中優(yōu)于存儲(chǔ)在實(shí)例對(duì)象中。
使用閉包存儲(chǔ)私有數(shù)據(jù)任意一段程序都可以簡(jiǎn)單地通過(guò)訪問(wèn)JavaScript對(duì)象的屬性名來(lái)獲取相應(yīng)地對(duì)象屬性,例如for in循環(huán)、ES5的Object.keys函數(shù)和Object.getOwnPropertyNames函數(shù)。
一些程序員使用命名規(guī)范給私有屬性前置或后置一個(gè)下劃線字符_。
然而實(shí)際上,一些程序需要更高程度的信息隱藏。
對(duì)于這種情形,JavaScript為信息隱藏提供了閉包。閉包將數(shù)據(jù)存儲(chǔ)到封閉的變量中而不提供對(duì)這些變量的直接訪問(wèn),獲取閉包內(nèi)部結(jié)構(gòu)的唯一方式是該函數(shù)顯式地提供獲取它的途徑。
利用這一特性在對(duì)象中存儲(chǔ)真正的私有數(shù)據(jù)。不是將數(shù)據(jù)作為對(duì)象的屬性來(lái)存儲(chǔ),而是在構(gòu)造函數(shù)中以變量的方式存儲(chǔ)它。
function User(name, passwordHash) { this.toString = function () { return "[User " + name + "]"; }; this.checkPassword = function (password) { return hash(password) === passwordHash; } }
上述代碼的toString和checkPassword方法是以變量的方式來(lái)引用name和passwordHash變量的,而不是以this屬性的方式來(lái)引用,User的實(shí)例不包含任何實(shí)例屬性,因此外部的代碼不能直接訪問(wèn)User實(shí)例的name和passwordHash變量。
該模式的一個(gè)缺點(diǎn)是,為了讓構(gòu)造函數(shù)中的變量在使用它們的方法的作用域內(nèi),這些方法必須放置于實(shí)例對(duì)象中,這會(huì)導(dǎo)致方法副本的擴(kuò)散。
提示:
閉包變量是私有的,只能通過(guò)局部的引用獲取。
將局部變量作為私有數(shù)據(jù)從而通過(guò)方法實(shí)現(xiàn)信息隱藏。
只將實(shí)例狀態(tài)存儲(chǔ)在實(shí)例對(duì)象中一種錯(cuò)誤的做法是不小心將每個(gè)實(shí)例的數(shù)據(jù)存儲(chǔ)到了其原型中。
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]
每次調(diào)用addChild方法,都會(huì)將值添加到Tree.prototype.children數(shù)組中。
實(shí)現(xiàn)Tree類(lèi)的正確方式是為每個(gè)實(shí)例對(duì)象創(chuàng)建一個(gè)多帶帶的children數(shù)組。
function Tree(x) { this.value = x; this.children = []; // instance state } Tree.prototype = { addChild: function(x) { this.children.push(x); } };
一般情況下,任何不可變的數(shù)據(jù)可以被存儲(chǔ)在原型中從而被安全地共享。有狀態(tài)的數(shù)據(jù)原則上也可以存儲(chǔ)在原型中,只要你真正想共享它。然而迄今為止,在原型對(duì)象中最常見(jiàn)的數(shù)據(jù)是方法,而每個(gè)實(shí)例的狀態(tài)都存儲(chǔ)在實(shí)例對(duì)象中。
提示:
共享可變數(shù)據(jù)可能會(huì)出問(wèn)題,因?yàn)樵褪潜黄渌械膶?shí)例共享的。
將可變的實(shí)例狀態(tài)存儲(chǔ)在實(shí)例對(duì)象中。
認(rèn)識(shí)到this變量的隱式綁定問(wèn)題編寫(xiě)一個(gè)簡(jiǎn)單的、可定制的讀取CSV(逗號(hào)分隔型取值)數(shù)據(jù)的類(lèi)。
function CSVReader(separators) { this.separators = separators || [","]; this.regexp = new RegExp(this.separators.map(function (sep) { return "" + sep[0]; }).join("|")); }
實(shí)現(xiàn)一個(gè)簡(jiǎn)單的read方法可以分為兩步來(lái)處理。第一步,將輸入的字符串分為按行劃分的數(shù)組。第二步,將數(shù)組的每一行再分為按單元格劃分的數(shù)組。結(jié)果獲得一個(gè)二維的字符串?dāng)?shù)組。
CSVReader.prototype.read = function (str) { var lines = str.trim().split(/ /); return lines.map(function (line) { return line.split(this.regexp); }); }; var reader = new CSVReader(); reader.read("a, b, c d, e, f "); // [["a, b, c"], ["d, e, f"]]
上述代碼的bug是,傳遞給line.map的回調(diào)函數(shù)引用的this指向的是window,因此,this.regexp產(chǎn)生undefined值。
備注:"a, b, c".split(undefined)返回["a, b, c"]。
幸運(yùn)的是,數(shù)組的map方法可以傳入一個(gè)可選的參數(shù)作為其回調(diào)函數(shù)的this綁定。
CSVReader.prototype.read = function (str) { var lines = str.trim().split(/ /); return lines.map(function (line) { return line.split(this.regexp); }, this); }; var reader = new CSVReader(); reader.read("a, b, c d, e, f "); // [["a", "b", "c"], ["d", "e", "f"]]
但是,不是所有基于回調(diào)函數(shù)的API都考慮周全。另一種解決方案是使用詞法作用域的變量來(lái)存儲(chǔ)這個(gè)額外的外部this綁定的引用。
CSVReader.prototype.read = function (str) { var lines = str.trim().split(/ /); var self = this; // save a reference to outer this-binding return lines.map(function (line) { return line.split(this.regexp); }); }; var reader = new CSVReader(); reader.read("a, b, c d, e, f "); // [["a", "b", "c"], ["d", "e", "f"]]
在ES5的環(huán)境中,另一種有效的方法是使用回調(diào)函數(shù)的bind方法。
CSVReader.prototype.read = function (str) { var lines = str.trim().split(/ /); return lines.map(function (line) { return line.split(this.regexp); }.bind(this)); // bind to outer this-binding }; var reader = new CSVReader(); reader.read("a, b, c d, e, f "); // [["a", "b", "c"], ["d", "e", "f"]]
提示:
this變量的作用域總是由其最近的封閉函數(shù)所確定。
使用一個(gè)局部變量(通常命名為self、me或that)使得this綁定對(duì)于內(nèi)部函數(shù)是可用的。
在子類(lèi)的構(gòu)造函數(shù)中調(diào)用父類(lèi)的構(gòu)造函數(shù)場(chǎng)景圖(scene graph)是在可視化的程序中(如游戲或圖形仿真場(chǎng)景)描述一個(gè)場(chǎng)景的對(duì)象集合。一個(gè)簡(jiǎn)單的場(chǎng)景包含了在該場(chǎng)景中的所有對(duì)象(稱為角色),以及所有角色的預(yù)加載圖像數(shù)據(jù)集,還包含一個(gè)底層圖形顯示的引用(通常被稱為context)。
function Scene(context, width, height, images) { this.context = context; this.width = width; this.height = height; this.images = images; this.actors = []; } Scene.prototype.register = function (actor) { this.actors.push(actor); }; Scene.prototype.unregister = function (actor) { var i = this.actors.indexOf(actor); if (i >= 0) { this.actors.splice(i, 1); } }; Scene.prototype.draw = function () { this.context.clearRect(0, 0, this.width, this.height); for (var a = this.actors, i = 0, n = a.length; i < n; i++) { a[i].draw(); } };
場(chǎng)景中的所有角色都繼承自基類(lèi)Actor。
function Actor(scene, x, y) { this.scene = scene; this.x = x; this.y = y; scene.register(this); } Actor.prototype.moveTo = function (x, y) { this.x = x; this.y = y; this.scene.draw(); }; Actor.prototype.exit = function() { this.scene.unregister(this); this.scene.draw(); }; Actor.prototype.draw = function () { var image = this.scene.images[this.type]; this.scene.context.drawImage(image, this.x, this.y); }; Actor.prototype.width = function () { return this.scene.images[this.type].width; }; Actor.prototype.height = function () { return this.scene.images[this.type].height; };
我們將角色的特定類(lèi)型實(shí)現(xiàn)為Actor的子類(lèi)。例如,在街機(jī)游戲中太空飛船就會(huì)有一個(gè)拓展自Actor的SpaceShip類(lèi)。
為了確保SpaceShip的實(shí)例能作為角色被正確地初始化,其構(gòu)造函數(shù)必須顯式地調(diào)用Actor的構(gòu)造函數(shù)。通過(guò)將接收者綁定到該新對(duì)象來(lái)調(diào)用Actor可以達(dá)到此目的。
function SpaceShip(scene, x, y) { Actor.call(this, scene, x, y); this.points = 0; }
調(diào)用Actor的構(gòu)造函數(shù)能確保Actor創(chuàng)建的所有實(shí)例屬性都被添加到了新對(duì)象(SpaceShip實(shí)例對(duì)象)中。為了使SpaceShip成為Actor的一個(gè)正確地子類(lèi),其原型必須繼承自Actor.prototype。做這種拓展的最好的方式是使用ES5提供的Object.create方法。
SpaceShip.prototype = Object.create(Actor.prototype);
一旦創(chuàng)建了SpaceShip的原型對(duì)象,我們就可以向其添加所有的可被實(shí)例共享的屬性。
SpaceShip.prototype.type = "spaceShip"; SpaceShip.prototype.scorePoint = function () { this.points++; }; SpaceShip.prototype.left = function () { this.moveTo(Math.max(this.x - 10, 0), this.y); }; SpaceShip.prototype.right = function () { var maxWidth = this.scene.width - this.width(); this.moveTo(Math.min(this.x + 10, maxWidth), this.y); };
提示:
在子類(lèi)構(gòu)造函數(shù)中顯示地傳入this作為顯式地接收者調(diào)用父類(lèi)構(gòu)造函數(shù)。
使用Object.create函數(shù)來(lái)構(gòu)造子類(lèi)的原型對(duì)象以避免調(diào)用父類(lèi)的構(gòu)造函數(shù)。
不要重用父類(lèi)的屬性名function Actor(scene, x, y) { this.scene = scene; this.x = x; this.y = y; this.id = ++Actor.nextID; scene.register(this); } Actor.nextID = 0;
function Alien(scene, x, y, direction, speed, strength) { Actor.call(this, scene, x, y); this.direction = direction; this.speed = speed; this.strength = strength; this.damage = 0; this.id = ++Alien.nextID; // conflicts with actor id! } Alien.nextID = 0;
Alien類(lèi)與其父類(lèi)Actor類(lèi)都視圖給實(shí)例屬性id寫(xiě)數(shù)據(jù)。如果在繼承體系中的兩個(gè)類(lèi)指向相同的屬性名,那么它們指向的是同一個(gè)屬性。
該例子顯而易見(jiàn)的解決方法是對(duì)Actor標(biāo)識(shí)數(shù)和Alien標(biāo)識(shí)數(shù)使用不同的屬性名。
function Actor(scene, x, y) { this.scene = scene; this.x = x; this.y = y; this.actorID = ++Actor.nextID; // distinct from alienID scene.register(this); } Actor.nextID = 0; function Alien(scene, x, y, direction, speed, strength) { Actor.call(this, scene, x, y); this.direction = direction; this.speed = speed; this.strength = strength; this.damage = 0; this.alienID = ++Alien.nextID; // distinct from actorID } Alien.nextID = 0;
提示:
留意父類(lèi)使用的所有屬性名。
不要在子類(lèi)中重用父類(lèi)的屬性名。
避免繼承標(biāo)準(zhǔn)類(lèi)一個(gè)操作文件系統(tǒng)的庫(kù)可能希望創(chuàng)建一個(gè)抽象的目錄,該目錄繼承了數(shù)組的所有行為。
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
遺憾的是,這種方式破壞了數(shù)組的length屬性的預(yù)期行為。
var dir = new Dir("/tmp/mysite", ["index.html", "script.js", "style.css"]); dir.length; // 0
失敗的原因是length屬性只對(duì)在內(nèi)部標(biāo)記為“真正的”數(shù)組的特殊對(duì)象起作用。ECMAScript標(biāo)準(zhǔn)規(guī)定它是一個(gè)不可見(jiàn)的內(nèi)部屬性,稱為[[Class]]。
數(shù)組對(duì)象(通過(guò)Array構(gòu)造函數(shù)或[]語(yǔ)法創(chuàng)建)被加上了值為“Array”的[[Class]]屬性,函數(shù)被加上了值為“Function”的[[Class]]屬性。
事實(shí)證明,length的行為只被定義在內(nèi)部屬性[[Class]]的值為“Array”的特殊對(duì)象中。對(duì)于這些對(duì)象,JavaScript保持length屬性與該對(duì)象的索引屬性的數(shù)量同步。
但當(dāng)我們拓展Array類(lèi)時(shí),子類(lèi)的實(shí)例并不是通過(guò)new Array()或字面量[]語(yǔ)法創(chuàng)建的。所以,Dir的實(shí)例[[Class]]屬性值為“Object”。
更好的實(shí)現(xiàn)是定義一個(gè)entries數(shù)組的實(shí)例屬性。
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); };
提示:
繼承標(biāo)準(zhǔn)類(lèi)往往由于一些特殊的內(nèi)部屬性(如[[Class]])而被破壞。
使用屬性委托優(yōu)于繼承標(biāo)準(zhǔn)類(lèi)。
將原型視為實(shí)現(xiàn)細(xì)節(jié)原型是一種對(duì)象行為的實(shí)現(xiàn)細(xì)節(jié)。
JavaScript提供了便利的內(nèi)省機(jī)制(introspection mechanisms)來(lái)檢查對(duì)象的細(xì)節(jié)。Object.prototype.hasOwnProperty方法確定一個(gè)屬性是否為對(duì)象“自己的”屬性(即一個(gè)實(shí)例屬性),而完全忽略原型繼承機(jī)構(gòu)。Object.getPrototypeOf和__proto__特性允許程序員遍歷對(duì)象的原型鏈并多帶帶查詢其原型對(duì)象。
檢查實(shí)現(xiàn)細(xì)節(jié)(即使沒(méi)有修改它們)也會(huì)在程序的組件之間創(chuàng)建依賴。如果對(duì)象的生產(chǎn)者修改了實(shí)現(xiàn)細(xì)節(jié),那么依賴于這些對(duì)象的使用者就會(huì)被破壞。
提示:
對(duì)象是接口,原型是實(shí)現(xiàn)。
避免檢查你無(wú)法控制的對(duì)象的原型結(jié)構(gòu)。
避免檢查實(shí)現(xiàn)在你無(wú)法控制的對(duì)象內(nèi)部的屬性。
避免使用輕率的猴子補(bǔ)丁由于對(duì)象共享原型,因此每一個(gè)對(duì)象都可以增加、刪除或修改原型的屬性,這個(gè)有爭(zhēng)議的實(shí)踐通常被稱為猴子補(bǔ)?。╩onkey-patching)。
猴子補(bǔ)丁的吸引力在于它的強(qiáng)大,數(shù)組缺少一個(gè)有用的方法,你自己就可以增加它。
Array.prototype.split = function (i) { // alternative #1 return [this.slice(0, 1), this.slice(i)]; };
但是當(dāng)多個(gè)庫(kù)以不兼容的方式給同一個(gè)原型打猴子補(bǔ)丁時(shí),問(wèn)題就出現(xiàn)了。
Array.prototype.split = function (i) { // alternative #2 var i = Math.floor(this.length / 2); return [this.slice(0, 1), this.slice(i)]; };
現(xiàn)在,任一對(duì)數(shù)組split方法的使用都大約有50%的機(jī)會(huì)被破壞。
一個(gè)方法可以將這些修改置于一個(gè)函數(shù)中,用戶可以選擇調(diào)用或忽略。
function addArrayMethods() { Array.prototype.split = function (i) { return [this.slice(0, 1), this.slice(i)]; } }
盡管猴子補(bǔ)丁很危險(xiǎn),但是有一種特別可靠而且有價(jià)值的使用場(chǎng)景:polyfill。
if (typeof Array.prototype.map !== "function") { Array.prototype.map = function (f, thisArg) { var result = []; for (var i = 0, n = this.length; i < n; i++) { result[i] = f.call(thisArg, this[i], i); } return result; }; }
提示:
避免使用輕率的猴子補(bǔ)丁。
記錄程序庫(kù)所執(zhí)行的所有猴子補(bǔ)丁。
考慮通過(guò)將修改置于一個(gè)到處函數(shù)中,使猴子補(bǔ)丁稱為可選的。
使用猴子補(bǔ)丁為缺失的標(biāo)準(zhǔn)API提供polyfills。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/107678.html
摘要:但實(shí)例化該構(gòu)造函數(shù)仍然得到的是的實(shí)例?;蛘?,為了避免在所有查找屬性的地方都插入這段樣本代碼,我們可以將該模式抽象到的構(gòu)造函數(shù)中。該構(gòu)造函數(shù)封裝了所有在單一數(shù)據(jù)類(lèi)型定義中編寫(xiě)健壯字典的技術(shù)細(xì)節(jié)。 參考書(shū)籍:《Effective JavaScript》 數(shù)組和字典 對(duì)象是JavaScript中最萬(wàn)能的數(shù)據(jù)結(jié)構(gòu)。取決于不同的環(huán)境,對(duì)象可以表示一個(gè)靈活的鍵值關(guān)聯(lián)記錄,一個(gè)繼承了方法的面向?qū)ο髷?shù)...
摘要:五不要增加內(nèi)置的原型增加內(nèi)置構(gòu)造函數(shù)如,和等的原型屬性是一個(gè)增強(qiáng)功能性的強(qiáng)大的方法,但這可能會(huì)嚴(yán)重影響可維護(hù)性,因?yàn)檫@種做法將使代碼變得更加不可預(yù)測(cè)。推薦使用后者,這樣根據(jù)名字就能明顯地區(qū)分出變量函數(shù)和基本常量等。 一、盡量少用全局變量 減少全局名字空間污染,最理想的情況是一個(gè)應(yīng)用程序僅有一個(gè)全局變量。 二、單一var模式 var a = 1, b = 2, sum =...
摘要:訪問(wèn)全局對(duì)象在瀏覽器中,全局對(duì)象可以通過(guò)屬性在代碼的任何位置訪問(wèn)除非你做了些比較出格的事情,像是聲明了一個(gè)名為的局部變量。 前言 才華橫溢的Stoyan Stefanov,在他寫(xiě)的由O’Reilly初版的新書(shū)《JavaScript Patterns》(JavaScript模式)中,我想要是為我們的讀者貢獻(xiàn)其摘要,那會(huì)是件很美妙的事情。具體一點(diǎn)就是編寫(xiě)高質(zhì)量JavaScript的一些要素...
摘要:前言月份開(kāi)始出沒(méi)社區(qū),現(xiàn)在差不多月了,按照工作的說(shuō)法,就是差不多過(guò)了三個(gè)月的試用期,準(zhǔn)備轉(zhuǎn)正了一般來(lái)說(shuō),差不多到了轉(zhuǎn)正的時(shí)候,會(huì)進(jìn)行總結(jié)或者分享會(huì)議那么今天我就把看過(guò)的一些學(xué)習(xí)資源主要是博客,博文推薦分享給大家。 1.前言 6月份開(kāi)始出沒(méi)社區(qū),現(xiàn)在差不多9月了,按照工作的說(shuō)法,就是差不多過(guò)了三個(gè)月的試用期,準(zhǔn)備轉(zhuǎn)正了!一般來(lái)說(shuō),差不多到了轉(zhuǎn)正的時(shí)候,會(huì)進(jìn)行總結(jié)或者分享會(huì)議!那么今天我就...
閱讀 3677·2023-04-25 14:57
閱讀 2647·2021-11-22 14:56
閱讀 2287·2021-09-29 09:45
閱讀 1854·2021-09-22 15:53
閱讀 3455·2021-08-25 09:41
閱讀 988·2019-08-29 15:22
閱讀 3381·2019-08-29 13:22
閱讀 3214·2019-08-29 13:08