Understanding Common JavaScript Design Patterns: Constructor, Facade, Proxy, Strategy, Bridge, and State
This article explains six fundamental JavaScript design patterns—Constructor, Facade, Proxy, Strategy, Bridge, and State—illustrating their concepts, advantages, drawbacks, and providing clear code examples that demonstrate how each pattern can improve code organization, reuse, and maintainability in front‑end development.
Constructor Pattern
Overview
In JavaScript, a constructor function creates instances using the new keyword, with this referring to the newly created object. The article shows a simple LOL constructor that stores map, heroes, soldier, and monster data, and defines a start method directly on each instance.
function LOL(maps, heros, soldier, monster) {
this.maps = maps;
this.heros = heros;
this.soldier = soldier;
this.monster = monster;
this.start = function() {
return '地图:' + this.maps + '\n对战英雄:' + this.heros.join() + '\n小兵类型:' + this.soldier + '\n野怪:' + this.monster + '\n';
};
}
var game1 = new LOL('召唤师峡谷', ['影流之主', '诡术妖姬'], '超级兵', '红buff');
var game2 = new LOL('大乱斗', ['影流之主', '诡术妖姬'], '超级兵', '红buff');
console.log(game1.start());
console.log(game2.start());Placing the method on each instance wastes memory. By moving start to the prototype, all instances share a single function.
function LOL(maps, heros, soldier, monster) {
this.maps = maps;
this.heros = heros;
this.soldier = soldier;
this.monster = monster;
}
LOL.prototype.start = function() {
return '地图:' + this.maps + '\n对战英雄:' + this.heros.join() + '\n小兵类型:' + this.soldier + '\n野怪:' + this.monster + '\n';
};The article also demonstrates creating objects without new by using call or enforcing new inside the constructor.
Facade Pattern
Overview
The Facade pattern provides a simple entry point that hides complex inner workings. The example builds a YaSuo hero by attaching skill methods and a skin via separate functions, then exposing a single CreateYasuo function that returns a fully configured instance.
function YaSuo() {}
function Qskill(hero) { hero.prototype.Qskill = function() { console.log('hasaki!!'); } }
function Wskill(hero) { hero.prototype.Wskill = function() { console.log('风墙'); } }
function Eskill(hero) { hero.prototype.Eskill = function() { console.log('快乐'); } }
function Rskill(hero) { hero.prototype.Rskill = function() { console.log('痛里唉该痛'); } }
function Skin(hero, skin) { hero.prototype.skin = skin; }
function CreateYasuo() {
Qskill(YaSuo);
Wskill(YaSuo);
Eskill(YaSuo);
Rskill(YaSuo);
Skin(YaSuo, 'originSkin');
return new YaSuo();
}
CreateYasuo(); // Facade creates a complete Yasuo objectThe Facade simplifies usage while keeping the underlying implementations modular.
Proxy Pattern
Overview
The Proxy pattern lets one object control access to another. The article shows a Walk function that returns a walking behavior, a Kalisita hero that proxies another hero's walk method, and a generic getPrivateProps utility that uses ES6 Proxy to filter property access.
function Walk(hero) { return function() { console.log(hero + ' is walk'); }; }
function Kalisita() { this.walk = Walk('Kalisita'); this.Rskill = function(hero) { this.walk = function() { Walk('Kalisita')(); hero.walk(); }; } }
function HeroA() { this.walk = Walk('heroA'); }
var k = new Kalisita();
var a = new HeroA();
k.Rskill(a); // k now proxies a's walk
k.walk(); // both Kalisita and heroA walkA more advanced example creates a getPrivateProps proxy that blocks access to properties starting with an underscore.
function getPrivateProps(obj, filterFunc) {
return new Proxy(obj, {
get(obj, prop) {
if (!filterFunc(prop)) {
let value = Reflect.get(obj, prop);
if (typeof value === 'function') value = value.bind(obj);
return value;
}
},
set(obj, prop, value) {
if (filterFunc(prop)) throw new TypeError(`Cant set property ${prop}`);
return Reflect.set(obj, prop, value);
},
has(obj, prop) { return filterFunc(prop) ? false : Reflect.has(obj, prop); },
ownKeys(obj) { return Reflect.ownKeys(obj).filter(p => !filterFunc(p)); },
getOwnPropertyDescriptor(obj, prop) { return filterFunc(prop) ? undefined : Reflect.getOwnPropertyDescriptor(obj, prop); }
});
}
function propFilter(prop) { return prop.indexOf('_') === 0; }Strategy Pattern
Overview
The Strategy pattern encapsulates interchangeable algorithms. The article builds a validator object whose types map holds individual validation strategies (non‑empty, no‑number, existence, length). The validate method iterates over requested types, executes each strategy, and collects error messages.
var validator = {
types: {},
validate: function(str, types) {
this.messages = [];
for (var i in types) {
var type = types[i];
var checker = this.types[type];
if (!checker) throw { name: "ValidationError", message: "No handler for type " + type };
if (!checker.validate(str)) this.messages.push("Invalid value for " + type + ", " + checker.instructions);
}
return this.hasErrors();
},
hasErrors: function() { return this.messages.length !== 0; }
};
validator.types.isNonEmpty = { validate: v => v !== "", instructions: "传入的值不能为空" };
validator.types.isNoNumber = { validate: v => isNaN(v), instructions: "传入的值不能是纯数字" };
validator.types.isExist = { validate: v => true, instructions: "给定的值已经存在" };
validator.types.isLength = { validate: v => { var l = v.toString().length; return l > 2 && l < 10; }, instructions: "长度不合理,请长度在2-10个字符内" };
var types = ['isExist','isLength','isNoNumber','isNonEmpty'];
function check(name, types) {
validator.validate(name, types);
if (validator.hasErrors()) console.log(validator.messages.join('\n'));
else console.log('验证通过!');
}
check('okckokckokck', types);
check('老faker', types);
check('00001', types);Bridge Pattern
Overview
The Bridge pattern separates abstraction from implementation. The example defines GameMessage that holds a reference to either a Victory or Defeat object, delegating show calls. The getResult function randomly decides which concrete implementation to use, keeping the two layers independent.
function GameMessage(type) { this.fn = type ? new Victory() : new Defeat(); }
GameMessage.prototype.show = function() { this.fn.show(); };
function Defeat() { this.show = function() { console.log('im loser'); }; }
function Victory() { this.show = function() { console.log('im winner'); }; }
function getResult() { var switchVD = Math.ceil(Math.random()*10) > 5; return new GameMessage(switchVD); }
var r1 = getResult(); var r2 = getResult(); r1.show(); r2.show();State Pattern
Overview
The State pattern lets an object change its behavior when its internal state changes. The article implements a YasuoState object that stores a list of current actions and provides changeState and YasuoActions methods to modify and execute the actions.
function YasuoState() {
this.currentstate = [];
this.Actions = {
walk: function() { console.log('walk'); },
attack: function() { console.log('attack'); },
magic: function() { console.log('magic'); },
backhome: function() { console.log('backhome'); }
};
}
YasuoState.prototype.changeState = function() {
this.currentstate = [];
Object.keys(arguments).forEach(i => this.currentstate.push(arguments[i]));
return this;
};
YasuoState.prototype.YasuoActions = function() {
this.currentstate.forEach(k => this.Actions[k] && this.Actions[k]());
return this;
};
var yasuoState = new YasuoState();
yasuoState.changeState('walk','attack').YasuoActions().changeState('walk').YasuoActions();Summary
The article covered six JavaScript design patterns—Constructor, Facade, Proxy, Strategy, Bridge, and State—explaining their purpose, benefits, drawbacks, and providing practical code snippets to help front‑end developers write cleaner, more maintainable code.
Sohu Tech Products
A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.