Showing posts with label javascript. Show all posts
Showing posts with label javascript. Show all posts

Friday, October 23, 2009

Best practice to develop jQuery plugin

  1. Create a private scope for $
  2. Attach plugin to $.fn alias
  3. Add implicit iteration
  4. Enable chaining
  5. Add default options
  6. Add custom options
  7. global custom options
<!DOCTYPE html><html lang="en"><body> <div id="counter1"></div><div id="counter2"></div> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script><script> (function($) { $.fn.count = function(customOptions){ var options = $.extend({},$.fn.count.defaultOptions, customOptions); return this.each(function(){ var $this = $(this); $this.text(options.startCount); var myInterval = window.setInterval(function(){ var currentCount = parseFloat($this.text()); var newCount = currentCount+1; $this.text(newCount+''); }, 1000); }); }; $.fn.count.defaultOptions = { startCount:'100' }; })(jQuery); jQuery.fn.count.defaultOptions.startCount = '300'; jQuery('#counter1').count(); jQuery('#counter2').count({startCount:'500'}); </script></body></html>

Wednesday, October 21, 2009

type check in jQuery

String: typeof object === "string" Number: typeof object === "number" Boolean: typeof object === "boolean" Object: typeof object === "object" Function: jQuery.isFunction(object) Array: jQuery.isArray(object) Element: object.nodeType null: object === null undefined: typeof variable === "undefined" or object.prop === undefined null or undefined: object == null

Saturday, October 17, 2009

it must be "new"ed.

The following is constructor which prevent not using "new" keyword

function User(first, last){ if ( !(this instanceof arguments.callee) ) return new User(first, last); this.name = first + " " + last; }

Friday, October 16, 2009

javascript scope

In JavaScript, {blocks} do not have scope. Only functions have scope. Vars defined in a function are not visible outside of the function.

function overload

function.length can tell you the number of parameters defined, using this we can create overload functions

function addMethod(object, name, fn){ // Save a reference to the old method var old = object[ name ]; // Overwrite the method with our new one object[ name ] = function(){ // Check the number of incoming arguments, // compared to our overloaded function if ( fn.length == arguments.length ) // If there was a match, run the function return fn.apply( this, arguments ); // Otherwise, fallback to the old method else if ( typeof old === "function" ) return old.apply( this, arguments ); }; } function Ninjas(){ var ninjas = [ "Dean Edwards", "Sam Stephenson", "Alex Russell" ]; addMethod(this, "find", function(){ return ninjas; }); addMethod(this, "find", function(name){ var ret = []; for ( var i = 0; i < ninjas.length; i++ ) if ( ninjas[i].indexOf(name) == 0 ) ret.push( ninjas[i] ); return ret; }); addMethod(this, "find", function(first, last){ var ret = []; for ( var i = 0; i < ninjas.length; i++ ) if ( ninjas[i] == (first + " " + last) ) ret.push( ninjas[i] ); return ret; }); } var ninjas = new Ninjas(); assert( ninjas.find().length == 3, "Finds all ninjas" ); assert( ninjas.find("Sam").length == 1, "Finds ninjas by first name" ); assert( ninjas.find("Dean", "Edwards").length == 1, "Finds ninjas by first and last name" ); assert( ninjas.find("Alex", "X", "Russell") == null, "Does nothing" );

later method

Object.prototype.later = function (msec, method) {     var that = this, args = Array.prototype.slice. apply(arguments, [2]); if (typeof method === 'string') { method = that[method]; } setTimeout(function () { method.apply(that, args); }, msec); return that; };

fixed a closure bug

Closure refer the ability that a function can access and manipulate external variable from with a function. Sometimes, this is not good because the if a function depends on the external state, and the external state changes, when the function may produce unexpected result.

//this is because the function inside setTimeout refers to the i only when it is //executed, by then i==4, the problem is that i is external variable for (var i = 0; i < 4; i++) { setTimeout(function() { alert(i); //it is always 4 }, i * 1000); } //the solution is make the i as local variable, so parameter is a solution, and //self-execution var count = 0; for (var i = 0; i < 4; i++) { (function(j) { setTimeout(function() { alert(j); //it will show 0, 1, 2, 3 }, j * 1000); }) (i); }

So sometimes it is good to remove the external dependencies. The follow code is anther example.

var ninja = { yell: function(n) { return n > 0 ? arguments.callee(n - 1) + "a" : "hiy"; } } /* this is also ok var ninja = { yell: function x(n) { return n > 0 ? x(n - 1) + "a" : "hiy"; } }; */ /*this is has bug, because ninja.yell is inside of function which depends external state var ninja = { yell: function(n) { return n > 0 ? ninja.yell(n - 1) + "a" : "hiy"; } }; */ var sumurai = { yell: ninja.yell }; ninja = null; ok(sumurai.yell(4) == "hiyaaaa", "argumnets.collee is the function itself");

efficency string operation

Because string is immutable in JavaScript, concatenation with array.join('') is much efficient. The following code use all the chinese characters. Click here to show

var sb = []; sb[sb.length] = "<p>"; for (var i = 0x4e00; i <= 0x9fcf; i++) { sb[sb.length] = String.fromCharCode(i); } sb[sb.length] = "</p>"; $("#chinese").html(sb.join("")); return false;

the bind function

John Resig has page Learning Advanced JavaScript to explain how the following script works.

// The .bind method from Prototype.js Function.prototype.bind = function(){ var fn = this, args = Array.prototype.slice.call(arguments), object = args.shift(); return function(){ return fn.apply(object, args.concat(Array.prototype.slice.call(arguments))); }; };

His explanation is wonderful. And this piece of code is simple, powerful. But it maybe still hard for anyone to understand without any explanation. So I refactored it as follow, and add one use case.

Function.prototype.bind = function() { var function_to_be_bound = this; var args = Array.prototype.slice.call(arguments); var context_object = args.shift(); var binding_parameters = args; return function() { var invoking_parameters = Array.prototype.slice.call(arguments); var combined_parameters = binding_parameters.concat(invoking_parameters); var result_from_function_run_in_new_context = function_to_be_bound.apply(context_object, combined_parameters); return result_from_function_run_in_new_context; }; } function reply_greeting(your_name) { //"this" is the context object alert("my name is " + this.name + ", Nice to meet you, " + your_name); } var fred = { name: "fred" }; var reply_greeting_of_fred_to_you = reply_greeting.bind(fred); var reply_greeting_of_fred_to_john = reply_greeting.bind(fred, "john"); reply_greeting_of_fred_to_you("jeff"); //expect: "my name is fred, Nice to meet you, jeff" reply_greeting_of_fred_to_john(); //expect: "my name is fred, Nice to meet you, john"

Another article may help you to understand is Functional Javascript

Thursday, October 15, 2009

memoried function

Function.prototype.memorized = function(key) { this._values = this._values || {}; if (this._values[key] !== undefined) { return this._values[key] } else { //"this" is parent function object this._values[key] = this.apply(this, arguments); /* the "this" passed as context object is optional? */ return this._values[key]; } }; function isPrime(num) { alert(this); var prime = num != 1; for (var i = 2; i < num; i++) { if (num % i == 0) { prime = false; break; } } return prime; } var a = isPrime.memorized(5); alert(a); var b = isPrime.memorized(5); alert(b);

curry function

Function.method('curry', function() { //arguments can not be passed in to closure function //var l = arguments.length; //var args = []; //for (var i = 0; i < l; i++) { // args[i] = arguments[i]; //} var args = Array.prototype.slice.apply(arguments); var original_function = this; return function() { //arguments is not the external arguments //for (var i = 0; i < arguments.length; i++) { // args[args.length] = arguments[i]; //} args = args.concat(Array.prototype.slice.call(arguments)); return original_function.apply(null, args); }; }); function add() { var sum = 0; for (i = 0; i < arguments.length; i++) { sum += arguments[i]; } return sum; } var add1 = add.curry(1); var s = add1(2); alert(s);

Tuesday, October 13, 2009

an issue caused by prototype inheritance in javascript

This issue is not obvious, in some case, it does not even cause any error at all. But fixing this issue brings some benefit. Here the use case.

var user_constructor = function() { alert(this.constructor); //function() { alert(this .. }; this.constructor.count++; this.Id = this.constructor.count; }; user_constructor.count = 0; var c = new user_constructor(); alert(c.Id); //1 alert(c.constructor == user_constructor); //true alert(user_constructor.count); //1

We know that constructor is also an object, (an object is not necessarily a constructor), in our case our constructor has property "count" to count the instance created by the constructor. And the Id is this last count. Very straight forward. Now if we want add in inheritance, we can implement as follow.

var user_constructor = function() { alert(this.constructor); //function Object() { [native code] } this.constructor.count++; this.Id = this.constructor.count; }; user_constructor.count = 0; var user_prototype = { Name: "Unknown" }; user_constructor.prototype = user_prototype; var c = new user_constructor(); alert(c.Id); //NaN alert(c.constructor == user_constructor); //false alert(user_constructor.count); //0

Suddenly, the code broke down. Is it because the fault of the prototype object? No. We need to have deeper understand how constructor works. When an object is created by using "new user_constructor()", the javascript runtime need to determine what constructor is used used to create an object "this". But how. According to ECMAScript Language Specification Edition 3

ECMAScript supports prototype-based inheritance. Every constructor has an associated prototype,and every object created by that constructor has an implicit reference to the prototype(calledthe object’s prototype)associated with its constructor.

To implement this feature, the runtime need to create "this" that has be behavior of user_constructor.prototype, so runtime will use user_constructor.prototype.constructor function to create "this". In the first case, there is no prototype based inheritance. So user_constructor.prototype is [object Object], naturally, user_constructor.prototype.constructor should [object Object] as well, but it is actually function() { alert(this .. }. Why. I don't know exactly, but this may be because user_constructor has no user assigned prototype, so the object should be created by user_constructor itself. In the second case, user_constructor.prototype is user_prototype, which is create by "function Object() { [native code] }". So runtime will use that function as constructor. After we rationalize this, we can easily fix this behavior as follow by adding one line "user_prototype.constructor = user_constructor;"

var user_constructor = function() { alert(this.constructor); //function() { alert(this .. }; this.constructor.count++; this.Id = this.constructor.count; }; user_constructor.count = 0; var user_prototype = { Name: "Unknown" }; user_constructor.prototype = user_prototype; user_prototype.constructor = user_constructor; var c = new user_constructor(); alert(c.Id); //NaN alert(c.constructor == user_constructor); //false alert(user_constructor.count); //0

Wednesday, April 15, 2009

Object vs Function

Object.prototype.sayhi = function() { alert("hi"); } var o = {}; alert(o.constructor); //function Object(){[native code]}, created by function Object() alert(Object.prototype); //[object Object], ultimate prototype o.sayhi(); //show hi Function.prototype.saybye = function() { alert("bye"); } alert(f.constructor); //function Function(){[native code]}, created by function Fuction() alert(Function.prototype); //function prototype{[native code]}, //I think Function.prototype still link to an object constant {}, //so that it can be route to [object Object], the ultimate prototype alert(Function.prototype.constructor); //function Function(){[native code]}, created by function Fuction() alert(f.prototype); //[object Object], ultimate prototype function f() { } f.sayhi(); //show hi f.saybye(); //show bye alert(document.constructor); //[objectHTMLDocument alert(document.constructor.prototype); //[Interface prototype object] alert(document.constructor.prototype.constructor); //null in IE, function Object() in firefox

Tuesday, April 14, 2009

delete

The delete operator can be used to remove a property from an object. It will remove a property from the object if it has one. It will not touch any of the objects in the prototype linkage. Removing a property from an object may allow a property from the prototype linkage to shine through: var stooge = {}; stooge.name = "fred"; if (typeof Object.beget !== 'function') { Object.beget = function(o) { var F = function() { }; F.prototype = o; return new F(); }; } var another_stooge = Object.beget(stooge); another_stooge.name = "jeff"; alert(another_stooge.name); //jeff delete another_stooge.name; alert(another_stooge.name); //fred

do not use hasOwnProperty

The hasOwnProperty method does not look at the prototype chain: flight.hasOwnProperty('number') // true flight.hasOwnProperty('constructor') // false //use if (typeof flight["number"]) == "undefined") { }

|| and &&

&& is guard operator aka "logical and", and || is default operator "aka logical or", we normally see the code like the following

if ( condtion1 && condition2) { } if (condition1 || condition2) { }

&& means, If first operand is truthy, the result is second operand, else result is is first operand. It can be used to avoid null reference.

if (a){ return a.memeber; } else { return a; } //this is the same return a && a.member

|| means, if first operand is truethy, then result is first operand, else result is second operand. It can be used to fill in default value like the following

var last = input || {}; //{} is default value

Sunday, April 12, 2009

context of setTimeout and eval

setTimeout is a method of window object. "this" context refer to the window. But you can change its context in the following way. setTimeout does not support call method. setTimeout(function() { alert(this); }, 0); //window //setTimeout.call({}, function() { alert(this); }, 0); //not supported setTimeout((function() { alert(this); }).call({}), 0); //object eval is also a method of Global object, in the case of browse, this is window object. But is context defined by its Containing context. Eval also does not support call method function Foo() { this.TestContext = function() { eval("alert(this==window);"); //show false setTimeout(function() { alert(this == window); }, 0); //show true } } var f = new Foo(); f.TestContext(); eval("alert(this);"); eval.call({}, "alert(this);"); //firefox does not support this, but IE support, but it does not change context

Anonymous function's contexgt

var name = "window"; var f = (function() { alert(this.name); }); f(); //window f.name = "local"; f(); //still window, the function still run in the context of window f.call(f); //local var p = { name: "local" }; p.f = f; p.f(); //local f.call(p); //local (function() { alert(this); })(); //the defining context is window, it will output window (function() { alert(this); }).call({}); //the defining context is window, but call switch the context

== vs ===

Here is the msdn documentation

Equality (==, !=)

  • If the types of the two expressions are different, attempt to convert them to string, number, or Boolean.
  • NaN is not equal to anything including itself.
  • Negative zero equals positive zero.
  • null equals both null and undefined.
  • Values are considered equal if they are identical strings, numerically equivalent numbers, the same object, identical Boolean values, or (if different types) they can be coerced into one of these situations.
  • Every other comparison is considered unequal.

Identity (===, !==)

These operators behave identically to the equality operators except no type conversion is done, and the types must be the same to be considered equal.

Here is some test case writing in Qunit.

test("Equality test", function() { ok(1 == 1 && 'a' == 'a' && 1 == '1' && 0 == false && '' == false); ok(null == null, "null equals to null"); ok(null == undefined, "null equals undefined"); ok(undefined == undefined, "undefined equals undefined"); ok({} != {}, "different objects are unequal"); }); test("Identity test", function() { ok(1 !== "1" && null !== undefined, "must be the same type, not conversion"); });

Thursday, November 27, 2008

Factory vs Constructor

In A better javascript constructor, factory, I said factory is a better way to create object. We have seen jQuery can build a jQuery object using factory. But internally jQuery using constructor/prototype. Let me be clear about what do I mean by factory and constructor in javascript. Both factory and constructor can return build object. Factory has a return statement, but constructor has no return statement. Because of this, factory has be called without and without new, the effects are identical. Constructor has to be call with "new". It would return null reference without "new", and moreover it will run in the context of caller, sometimes the caller is global object. Good factory has no such problem, because it's designer's intention be called this way. But internally jQuery use constructor, see my post. Because constructor has use good feature : prototype. but factory can not. the demo code is as below.

function AnimalConstructor() {}; AnimalConstructor.prototype = { name: "fred" } var an = new AnimalConstructor(); alert(an.name); AnimalConstructor.prototype.age = 18; alert(an.age); //show 18 function AnimalFactory() { return { name: "fred" }; }; var b = AnimalFactory(); alert(b.name); AnimalFactory.prototype.age = 18; alert(b.age); //show undefined

Why, the object created by factory has no secret link to factory's prototype. The object's constructor is not the factory, in our case the object's constructor is [object Object]. So changing factory's prototype has no effect.

In article From closures to prototypes, part 1 and From closures to prototypes, part 2, there is a comparison between closure and prototype. Now it is recommended to implement a framework using constructor, prototype internally, but as facade it is more user friendly using factory, just like jQuery.