Паттерны JavaScript
1) Class
function Car (model) {
this.model = model;
this.year = 2012;
}
Car.prototype.getInfo = function () {
return this.model + ' ' + this.year;
};
var myCar = new Car('Ford');
myCar.year = 2010;
console.log(myCar.getInfo());
2) Mixin
var Person = function (firstName , lastName) {
this.firstName = firstName;
this.lastName = lastName;
this.gender = 'male';
};
var Superhero = function (firstName, lastName , powers) {
// Invoke the superclass constructor on the new object
// then use .call() to invoke the constructor as a method of
// the object to be initialized.
Person.call(this, firstName, lastName);
// Finally, store their powers, a new array of traits not found in a normal "Person"
this.powers = powers;
};
// или
function applyMixins(derivedCtor, baseCtors) {
baseCtors.forEach(function (baseCtor) {
Object.getOwnPropertyNames(baseCtor.prototype).forEach(function (name) {
derivedCtor.prototype[name] = baseCtor.prototype[name];
});
});
}
// Disposable Mixin
var Disposable = (function () {
function Disposable() {}
Disposable.prototype.dispose = function () {this.isDisposed = true;};
return Disposable;
})();
// Activatable Mixin
var Activatable = (function () {
function Activatable() {}
Activatable.prototype.activate = function () {this.isActive = true;};
Activatable.prototype.deactivate = function () {this.isActive = false;};
return Activatable;
})();
var SmartObject = (function () {
function SmartObject() {
var _this = this;
this.isDisposed = false; // Disposable
this.isActive = false; // Activatable
setInterval(function () {return console.log(_this.isActive + " : " + _this.isDisposed);}, 500);
}
SmartObject.prototype.interact = function () {this.activate();};
return SmartObject;
})();
applyMixins(SmartObject, [Disposable, Activatable]);
var smartObj = new SmartObject();
3) Extend
var extend = this.extend || function (childClass, parentClass) {
for (var key in parentClass) {
if (parentClass.hasOwnProperty(key)) {
childClass[key] = parentClass[key];
}
}
function F () {this.constructor = childClass;}
F.prototype = parentClass.prototype;
childClass.prototype = new F();
};
var ParentClass = (function () {
// Constructor
function ParentClass(message) {
// Constructor body
this.greeting = message;
this.x = 1;
this.y = 2;
this.z = 3;
}
// Public function
ParentClass.prototype.greet = function () {
return "Hello, " + this.greeting;
};
return ParentClass;
})();
var ChildClass = (function (parentClass) {
// Extend
extend(ChildClass, parentClass);
// Constructor
function ChildClass(message) {
parentClass.apply(this, arguments); // или можно сделать частично parentClass.call(this, message);
this.greeting = message;
}
// Public variable
ChildClass.prototype.name = 'Parent';
// Public function
ChildClass.prototype.greet = function () {
return 'Hello, ' + this.greeting;
};
// Public function
ChildClass.prototype.hey = function () {
return 'Hey! ' + privateFunc();
};
// Private variable
var privateVar = 'Hello';
// Private function
function privateFunc () {
return privateVar + ' my friend!';
}
// Static variable
ChildClass.staticVar = 'I am static variable';
// Static function
function staticFunc () {
return 'I am static function';
}
return ChildClass;
})(ParentClass);
var SubClass = (function (parentClass) {
extend(SubClass, parentClass);
function SubClass(message) {
parentClass.apply(this, arguments);
this.greeting = message;
}
SubClass.prototype.greet = function () {
return 'Hello, ' + this.greeting;
};
SubClass.prototype.hey = function () {
return 'Hey!';
};
return SubClass;
})(ChildClass);
var parentObject = new ParentClass('world');
var childObject = new ChildClass();
var subObject = new SubClass();
console.log(parentObject.greet());
console.log(childObject.hey());
console.log(subObject.hey());
4) Module
var module = (function($){
var privateVar = 1
, publicVar = 2 + privateVar;
function Boat (name) {
this.name = name;
this.year = 2012;
}
Boat.prototype.getInfo = function () {
return this.name + ' ' + this.year;
};
function Plane (name) {
this.name = name;
this.year = 2012;
}
Plane.prototype.getInfo = function () {
return this.name + ' ' + this.year;
};
function insertHTML (tag, HTMLcode) {
$(tag).html(HTMLcode);
}
return {
publicVar: publicVar
, Boat: Boat
, Plane: Plane
, insertHTML: insertHTML
};
})(jQuery);
console.log(model.publicVar);
var myBoat = new model.Boat('Ann');
myBoat.year = 2010;
console.log(myBoat.getInfo());
model.insertHTML('div', '<h1>Hello!</h1>');
// Using CommonJS, AMD or browser globals to create a module
(function (root, factory) {
if (typeof exports === 'object') {factory(exports, require('b')); // CommonJS
} else if (typeof define === 'function' && define.amd) {define(['exports', 'b'], factory); // AMD. Register as an anonymous module.
} else {factory((root.commonJsStrict = {}), root.b); // Browser globals
}
}(this, function (exports, b) {
// use b in some fashion.
// attach properties to the exports object to define
// the exported module properties.
exports.action = function () {};
}));
5) Namespace
var myNamespace = myNamespace || {};
myNamespace.someVariable = 1;
myNamespace.someFunction = function () {alert(myNamespace.someVariable);};
// Namespace injection
var myApp = myApp || {};
myApp.utils = {};
(function () {
var val = 5;
this.getValue = function () {return val;};
this.setValue = function(newVal) {val = newVal;}
// also introduce a new sub-namespace
this.tools = {};
}).apply(myApp.utils);
(function () {
this.diagnose = function(){return "diagnosis";}
}).apply(myApp.utils.tools);
console.log(myApp); // Outputs: 5
console.log(myApp.utils.getValue());
myApp.utils.setValue(25);
console.log(myApp.utils.getValue());
console.log(myApp.utils.tools.diagnose());
6) Singleton
var singleton = (function(){
var instance;
function init () {
var privateVariable = 'I am private variable.';
function privatMethod () {
console.log('I am private method.');
}
return {
publicProperty: 'I am public property.'
, publicMethod: function () {
console.log('I am public method.');
}
};
}
return {
getInstance: function () {
if (!instance) {
instance = init();
}
return instance;
}
}
})();
var mySingleton1 = singleton.getInstance()
, mySingleton2 = singleton.getInstance();
console.log(mySingleton1 === mySingleton2); // true
console.log(mySingleton1.publicProperty);
mySingleton1.publicMethod();
7) Observer
function Observer () {
this.events = [];
}
Observer.prototype.on = function (eventName, callbackFunction) {
return this.events.push({
eventName: eventName
, callbackFunction: callbackFunction
});
};
Observer.prototype.off = function (eventName) {
var eventsLength = this.events.length;
for (;eventsLength--;) {
if (this.events[eventsLength].eventName === eventName) {
this.events.splice(eventsLength, 1);
}
}
};
Observer.prototype.trigger = function (eventName) {
var eventsLength = this.events.length;
for (;eventsLength--;) {
if (this.events[eventsLength].eventName === eventName) {
this.events.callbackFunction();
}
}
}
Observer.prototype.count = function () {
return this.events.length;
};
8) Command
var carManager = {
requestInfo: function (model, id) {
return "The information for " + model + " with ID " + id + " is foobar";
}
, buyVehicle: function (model, id) {
return "You have successfully purchased Item " + id + ", a " + model;
}
, arrangeViewing: function (model, id) {
return "You have successfully booked a viewing of " + model + " ( " + id + " ) ";
}
};
var car = {
model: 'Ford'
, id: 52738
};
console.log(carManager.call(car));
// или
carManager.execute = function (name) {
return carManager[name] && carManager[name].apply(null, [].slice.call(arguments, 1));
};
console.log(carManager.execute('buyVehicle', 'Ford Escort', '34232'));
9) Facade
var addMyEvent = function (element, eventName, callbackFunction) {
if (element.addEventListener) {element.addEventListener(eventName, callbackFunction, false);
} else if (element.attachEvent) {element.attachEvent('on' + eventName, callbackFunction);
} else {element['on' + eventName] = callbackFunction;
}
};
addMyEvent(document.getElementById('myButton'), 'click', function(){alert('OK');});
10) Factory
function Car (options) {
this.doors = options.doors || 4;
this.state = options.state || 'brand new';
this.color = options.color || 'silver';
}
function Truck (options) {
this.state = options.state || 'used';
this.wheelSize = options.wheelSize || 'large';
this.color = options.color || 'blue';
}
function VehicleFactory() {}
VehicleFactory.prototype.vehicleClass = Car;
VehicleFactory.prototype.createVehicle = function (options) {
switch(options.vehicleType){
case 'car':this.vehicleClass = Car; break;
case 'truck':this.vehicleClass = Truck; break;
}
return new this.vehicleClass(options);
};
var carFactory = new VehicleFactory();
var car = carFactory.createVehicle( {
vehicleType: 'car'
, color: 'yellow',
, doors: 6
});
var movingTruck = carFactory.createVehicle( {
vehicleType: 'truck'
, state: 'like new'
, color: 'red'
, wheelSize: 'small'
});
11) Decorator
function door () {
return 'metal door';
}
function decorator (fn) {
return function(){
return 'black ' + fn();
}
}
door = decorator(door);
console.log(door());
12) Iterator
function forEachInArrayDo (array, func) {
for (var i = 0, len = array.length; i < len; i++) {
func(array[i]);
}
}
var arr = [1,2,3,4,5];
forEachInArrayDo(arr, function(element){console.log(element);});
13) Curry
function curry (func) {
return function (args) {
return func (args);
};
}
['11','11','11','11'].map(parseInt) // [11, NaN, 3, 4]
['11','11','11','11'].map(curry(parseInt)); // [11, 11, 11, 11]
function curry2(fun) {
return function(secondArg) {
return function(firstArg) {
return fun(firstArg, secondArg);
};
};
}
function div(n, d) {return n / d;}
var div10 = curry2(div)(10);
div10(50); // 5
function curry3(fun) {
return function(last) {
return function(middle) {
return function(first) {
return fun(first, middle, last);
};
};
};
};
var songsPlayed = curry3(_.uniq)(false)(songToString);
songsPlayed(plays);
// [{artist: "Burial", track: "Archangel"},
// {artist: "Ben Frost", track: "Stomp"},
// {artist: "Emeralds", track: "Snores"}]
14) Proxy
$('button').on('click', function () {
setTimeout($.proxy(function () {
$(this).addClass('active'); // "this" теперь ссылается на внешний "this" функции "click"
}, this), 500); // <-- Этот "this" передается из функции "click" во внутреннюю функцию внутри "proxy"
});
// jQuery's implementation of jQuery.proxy() can be found below:
// Bind a function to a context, optionally partially applying any
// arguments.
proxy: function(fn, context) {
if (typeof context === "string") {
var tmp = fn[context];
context = fn;
fn = tmp;
}
// Quick check to determine if target is callable, in the spec
// this throws a TypeError, but we will just return undefined.
if (!jQuery.isFunction( fn ) ) {return undefined;}
// Simulated bind
var args = slice.call( arguments, 2 ),
proxy = function() {
return fn.apply( context, args.concat( slice.call( arguments ) ) );
};
// Set the guid of unique handler to the same of original handler, so it can be removed
proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
return proxy;
}
15) jQuery plugin
/*!
* jQuery lightweight plugin boilerplate
* Original author: @ajpiano
* Further changes, comments: @addyosmani
* Licensed under the MIT license
*/
// the semi-colon before the function invocation is a safety
// net against concatenated scripts and/or other plugins
// that are not closed properly.
;(function ( $, window, document, undefined ) {
// undefined is used here as the undefined global
// variable in ECMAScript 3 and is mutable (i.e. it can
// be changed by someone else). undefined isn't really
// being passed in so we can ensure that its value is
// truly undefined. In ES5, undefined can no longer be
// modified.
// window and document are passed through as local
// variables rather than as globals, because this (slightly)
// quickens the resolution process and can be more
// efficiently minified (especially when both are
// regularly referenced in our plugin).
// Create the defaults once
var pluginName = "defaultPluginName",
defaults = {
propertyName: "value"
};
// The actual plugin constructor
function Plugin( element, options ) {
this.element = element;
// jQuery has an extend method that merges the
// contents of two or more objects, storing the
// result in the first object. The first object
// is generally empty because we don't want to alter
// the default options for future instances of the plugin
this.options = $.extend( {}, defaults, options) ;
this._defaults = defaults;
this._name = pluginName;
this.init();
}
Plugin.prototype.init = function () {
// Place initialization logic here
// We already have access to the DOM element and
// the options via the instance, e.g. this.element
// and this.options
};
// A really lightweight plugin wrapper around the constructor,
// preventing against multiple instantiations
$.fn[pluginName] = function ( options ) {
return this.each(function () {
if ( !$.data(this, "plugin_" + pluginName )) {
$.data( this, "plugin_" + pluginName,
new Plugin( this, options ));
}
});
}
})( jQuery, window, document );
$("#elem").defaultPluginName({
propertyName: "a custom value"
});
16) jQuery UI widget factory
/*!
* jQuery UI Widget-factory plugin boilerplate (for 1.8/9+)
* Author: @addyosmani
* Further changes: @peolanha
* Licensed under the MIT license
*/
;(function ( $, window, document, undefined ) {
// define our widget under a namespace of your choice
// with additional parameters e.g.
// $.widget( "namespace.widgetname", (optional) - an
// existing widget prototype to inherit from, an object
// literal to become the widget's prototype );
$.widget( "namespace.widgetname" , {
//Options to be used as defaults
options: {
someValue: null
},
//Setup widget (e.g. element creation, apply theming
// , bind events etc.)
_create: function () {
// _create will automatically run the first time
// this widget is called. Put the initial widget
// setup code here, then we can access the element
// on which the widget was called via this.element.
// The options defined above can be accessed
// via this.options this.element.addStuff();
},
// Destroy an instantiated plugin and clean up
// modifications the widget has made to the DOM
destroy: function () {
// this.element.removeStuff();
// For UI 1.8, destroy must be invoked from the
// base widget
$.Widget.prototype.destroy.call( this );
// For UI 1.9, define _destroy instead and don't
// worry about
// calling the base widget
},
methodB: function ( event ) {
//_trigger dispatches callbacks the plugin user
// can subscribe to
// signature: _trigger( "callbackName" , [eventObject],
// [uiObject] )
// e.g. this._trigger( "hover", e /*where e.type ==
// "mouseenter"*/, { hovered: $(e.target)});
this._trigger( "methodA", event, {
key: value
});
},
methodA: function ( event ) {
this._trigger( "dataChanged", event, {
key: value
});
},
// Respond to any changes the user makes to the
// option method
_setOption: function ( key, value ) {
switch ( key ) {
case "someValue":
// this.options.someValue = doSomethingWith( value );
break;
default:
// this.options[ key ] = value;
break;
}
// For UI 1.8, _setOption must be manually invoked
// from the base widget
$.Widget.prototype._setOption.apply( this, arguments );
// For UI 1.9 the _super method can be used instead
// this._super( "_setOption", key, value );
}
});
})( jQuery, window, document );
var collection = $("#elem").widgetName({
foo: false
});
collection.widgetName("methodB");
Комментариев нет:
Отправить комментарий