##1. Type Detection
typeof sometimes returns unreasonable values, for example, RegExp objects return object, test code:
var regex = /^what$/i;
regex = new RegExp('^what$');
alert(typeof regex);
instanceof doesn't work when there are multiple frames on a page, objects from different frames return false with instanceof
You can use Object.prototype.toString.call(value) === '[object Array/Function...]' for type checking, it can also be used to distinguish native objects from custom objects, for example:
[object JSON]//native JSON object
[object Object]//custom JSON object
Note: In IE, function objects implemented as COM objects won't return [object Function] with toString
##2. Scope-Safe Constructors
Calling a constructor with the new operator adds properties to the newly created object, while directly calling a constructor adds properties to the global object window
To avoid polluting the global scope, you can use the following constructor:
/* May pollute global scope
function Student(name){
this.name = name;
}
*/
//Scope-safe constructor
function Student(name){
if(this instanceof Student){
this.name = name;
}
else{
return new Student(name);
}
}
The above constructor can avoid accidentally adding properties to the global object when directly calling the constructor, but using this approach for inheritance may cause problems, type checking may cause inheritance to fail
##3. Lazy Loading (Avoid Repeated Branch Detection)
-
On the first execution of branch detection, overwrite the original function, for example:
function detect(){ if(...){ detect = function(){ // } } else if(...){ detect = function(){ // } } else... } -
You can use an immediately invoked anonymous function that returns an anonymous function to implement lazy loading, for example:
var detect = (function(){ if(...){ return function(){ // } } else if(...){ return function(){ // } } else... })();
The first approach loses performance on the first call, but subsequent calls won't lose performance; the second approach won't lose performance even on the first call, because the time cost is put into the first code loading
##4. Function Binding (Specify Execution Context)
You can use the following function to specify the execution context for a function and create a new function:
function bind(fun, context){
return function(){
return fun.apply(context, arguments);
}
}
It's convenient to generate new functions based on existing functions, [IE9+] has a native bind method, for example var newFun = fun.bind(obj);
Note: Function binding has the disadvantages of high memory consumption and slow execution
##5. Function Currying (Also Called Function Application, Allows Specifying Some Parameters)
General way to create curried functions:
function curry(fun){
var args = Array.prototype.slice.call(arguments, 1);//Remove the first parameter fun, get the given parameter values
return function(){
var innerArgs = Array.prototype.slice.call(arguments);//Convert internal arguments object to array (to use concat method)
var finalArgs = args.concat(innerArgs);//Concatenate parameter list
return fun.apply(null, finalArgs);//Pass the concatenated parameter list to fun
}
}
Or enhance the bind method to implement currying:
function bind(fun, context){
var args = Array.prototype.slice.call(arguments, 2);//Remove the first 2 parameters
return function(){
var innerArgs = Array.prototype.slice.call(arguments);//Same as curry
var finalArgs = args.concat(innerArgs);//Same as curry
return fun.apply(context, finalArgs);//Specify execution context and parameters
}
}
Note: Both currying and bind have additional overhead, don't abuse them
##6. Tamper-Proof Objects
-
Non-extensible objects (cannot add new properties)
var obj = {a : 1, b : 2}; alert(Object.isExtensible(obj));//true Object.preventExtensions(obj);//Set obj to non-extensible alert(Object.isExtensible(obj));//false obj.c = 3; alert(obj.c);//undefined
Note: Setting non-extensible operation cannot be undone (can't change back), after setting, cannot add new properties, but can modify/delete existing properties
-
Sealed objects (can only modify existing properties, cannot delete or add)
var obj = {a : 1, b : 2}; Object.seal(obj);//Set sealed object alert(Object.isSealed(obj));//true obj.c = 3; alert(obj.c);//undefined delete obj.a;//Error in strict mode alert(obj.a);//1 -
Frozen objects (read-only, accessor properties can be written)
var obj = {a : 1, b : 2}; Object.freeze(obj);//Set sealed object alert(Object.isFrozen(obj));//true obj.a = 3; alert(obj.a);//1
The above implementations are all new features added in ES5, browser support is unknown, local test shows [IE8-] does not support, Chrome and FF support
##7. Function Throttling
Split large time-consuming tasks into smaller pieces, control execution with setTimeout
Advantages: Improves page response speed
Disadvantages: Logical coherence is lost, implementation difficulty increases, and it's not easy to implement transaction control because the complete transaction is split apart
##8. Observer Pattern
You can implement the observer pattern with custom events:
function EventTarget(){
this.handlers = {};
}
EventTarget.prototype = {
constructor: EventTarget,
addHandler: function(type, handler){
if (typeof this.handlers[type] == "undefined"){
this.handlers[type] = [];
}
this.handlers[type].push(handler);
},
fire: function(event){
if (!event.target){
event.target = this;
}
if (this.handlers[event.type] instanceof Array){
var handlers = this.handlers[event.type];
for (var i=0, len=handlers.length; i < len; i++){
handlers[i](event);
}
}
},
removeHandler: function(type, handler){
if (this.handlers[type] instanceof Array){
var handlers = this.handlers[type];
for (var i=0, len=handlers.length; i < len; i++){
if (handlers[i] === handler){
break;
}
}
handlers.splice(i, 1);
}
}
};
Usage is as follows:
function handleMessage(event){
alert("Message received: " + event.message);
}
//Create new object
var target = new EventTarget();
//Add event handler
target.addHandler("message", handleMessage);
//Trigger event
target.fire({ type: "message", message: "Hello world!"});
//Remove event handler
target.removeHandler("message", handleMessage);
//Trigger event again, should have no event handlers
target.fire({ type: "message", message: "Hello world!"});
No comments yet. Be the first to share your thoughts.