當面對某些需要大量創建相似對象的場景,例如游戲場景中的角色,循環列表中的節點等,我們往往會使用享元模式(Flyweight Pattern)來避免不必要的內存開銷。而在JavaScript中,原型代理模式(Prototype Proxy Pattern)也是一個非常普遍的應用,它與享元模式存在一定的相似性。在本文中,我們將會深入探討這兩種模式的實現原理和應用場景。
享元模式
享元模式的主要思想是共享對象,通過將對象中的公共屬性和方法從對象實例中抽離出來,以單獨的方式進行維護和復用,從而減少內存的使用。
例如,在一個游戲場景中,存在大量的敵人和障礙物,這些對象通常具有相似的屬性(例如坐標、大小、顏色等),但是它們的實例數量非常巨大。如果每個對象都創建一份獨立的屬性和方法,那么將會造成非常大的內存開銷。而如果通過享元模式將這些共同的屬性和方法提取出來,以單獨的方式進行維護,這樣就可以大大減少內存的使用。
//享元模式示例代碼 var FlyWeight = function (color, size) { this.color = color; this.size = size; }; FlyWeight.prototype.getPosition = function () { return { x: Math.random() * 100, y: Math.random() * 100, } } var flyWeightFactory = (function () { var flyWeights = {}; return { getFlyWeight: function(color, size){ if(!flyWeights[color+size]){ flyWeights[color+size] = new FlyWeight(color, size); } return flyWeights[color+size]; } } })(); var objs = []; for(var i=0;i<10000;i++){ objs.push({ flyWeight:flyWeightFactory.getFlyWeight('red',50) }) }
在上面的代碼中,我們定義了一個FlyWeight類,并在其中定義了共同的屬性和方法:顏色和大小以及獲取隨機位置的方法。為了避免重復創建對象,我們使用了一個工廠函數來維護單例模式,并根據傳入的顏色和大小來判斷是否需要創建新的對象。
原型代理模式
原型代理模式是指在創建對象的過程中,通過代理來實現原型屬性和方法的繼承。這種繼承方式可以減少重復性的代碼量,以及降低內存開銷。在JavaScript中,我們可以通過使用Object.create來創建原型代理對象,并將共同的屬性和方法放在其原型鏈上。
例如,在一個圖形繪制的示例中,我們需要繪制不同的形狀,例如矩形、圓形、三角形等。這些形狀都需要具有一定的共性,例如邊框顏色、填充顏色、邊框寬度等。我們可以先創建一個原型對象,將這些共同的屬性和方法放在其原型鏈上,然后通過代理來創建具體的形狀對象。這樣,每個具體的形狀對象都可以繼承共同的屬性和方法,從而避免了重復性的代碼。
//原型代理模式示例代碼 var shapePrototype = { borderColor:'black', fillColor:'white', borderWidth:'1px', draw: function(ctx){} } var rectPrototype = Object.create(shapePrototype); rectPrototype.draw = function(ctx){ ctx.fillStyle = this.fillColor; ctx.strokeStyle = this.borderColor; ctx.lineWidth = this.borderWidth; ctx.fillRect(10,10,50,50); ctx.strokeRect(10,10,50,50); } var circlePrototype = Object.create(shapePrototype); circlePrototype.draw = function(ctx){ ctx.fillStyle = this.fillColor; ctx.strokeStyle = this.borderColor; ctx.lineWidth = this.borderWidth; ctx.beginPath(); ctx.arc(30, 30, 20, 0, 2*Math.PI); ctx.fill(); ctx.stroke(); } function drawShapes(shapes){ var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); shapes.forEach(function(shape){ shape.draw(ctx); }) } var shapes = []; for(var i=0;i<10000;i++){ if(i%2===0){ shapes.push(Object.create(rectPrototype)); }else{ shapes.push(Object.create(circlePrototype)); } } drawShapes(shapes);
在上面的代碼中,我們定義了一個shapePrototype對象,作為所有形狀對象的原型,然后我們通過Object.create方法來創建具體的形狀對象。在這些對象中,我們只需要重寫draw方法,就可以實現不同的繪制形狀。最后,我們通過循環來創建大量的形狀對象,并通過具體的形狀對象來繪制圖形。
應用場景
享元模式適用于需要頻繁創建大量對象的場景,例如游戲場景中的角色、子彈等,循環列表中的節點等。通過抽離出共同的屬性和方法,避免重復創建對象,可以有效地減少內存開銷。
而原型代理模式則適用于需要創建大量類似的對象,并且這些對象具有共同的屬性和方法的場景。通過使用原型代理對象和代理,可以有效地減少重復性的代碼,并減少內存使用。
總的來說,這兩種模式都是應對大量對象重復創建的問題的有效方式。我們可以根據具體的場景選擇合適的模式,從而使我們的代碼更加簡潔、高效。