javascript closure

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures

 

Closures are functions that refer to independent (free) variables. 

In other words, the function defined in the closure ‘remembers’ the environment in which it was created in. 

Consider the following:

function init() {
    var name = "Mozilla"; // name is a local variable created by init
    function displayName() { // displayName() is the inner function, a closure
        alert (name); // displayName() uses variable declared in the parent function    
    }
    displayName();    
}
init();

init() creates a local variable name and then a function called displayName(). displayName() is the inner function (a closure) — it is defined insideinit(), and only available within the body of that function . Unlike init(), displayName() has no local variables of its own, and instead reuses the variable name declared in the parent function.

Run the code and see that this works. This is an example of lexical scoping: in JavaScript, the scope of a variable is defined by its location within the source code (it is apparent lexically) and nested functions have access to variables declared in their outer scope.

Now consider the following example:

function makeFunc() {
  var name = "Mozilla";
  function displayName() {
    alert(name);
  }
  return displayName;
}

var myFunc = makeFunc();
myFunc();

If you run this code it will have exactly the same effect as the previous init() example: the string “Mozilla” will be displayed in a JavaScript alert box. What’s different — and interesting — is that the displayName() inner function was returned from the outer function before being executed.

That the code still works may seem unintuitive. Normally, the local variables within a function only exist for the duration of that function’s execution. OncemakeFunc() has finished executing, it is reasonable to expect that the name variable will no longer be accessible. Since the code still works as expected, this is obviously not the case.

The solution to this puzzle is that myFunc has become a closure. A closure is a special kind of object that combines two things: a function, and the environment in which that function was created. The environment consists of any local variables that were in-scope at the time that the closure was created. In this case, myFunc is a closure that incorporates both the displayName function and the “Mozilla” string that existed when the closure was created.

Here’s a slightly more interesting example — a makeAdder function:

function makeAdder(x) {
  return function(y) {
    return x + y;
  };
}

var add5 = makeAdder(5);
var add10 = makeAdder(10);

console.log(add5(2));  // 7
console.log(add10(2)); // 12

In this example, we have defined a function makeAdder(x) which takes a single argument x and returns a new function. The function it returns takes a single argument y, and returns the sum of x and y.

In essence, makeAdder is a function factory — it creates functions which can add a specific value to their argument. In the above example we use our function factory to create two new functions — one that adds 5 to its argument, and one that adds 10.

add5 and add10 are both closures. They share the same function body definition, but store different environments. In add5‘s environment, x is 5. As far asadd10 is concerned, x is 10.

Practical closures

That’s the theory out of the way — but are closures actually useful? Let’s consider their practical implications. A closure lets you associate some data (the environment) with a function that operates on that data. This has obvious parallels to object oriented programming, where objects allow us to associate some data (the object’s properties) with one or more methods.

Consequently, you can use a closure anywhere that you might normally use an object with only a single method.

Situations where you might want to do this are particularly common on the web. Much of the code we write in web JavaScript is event-based — we define some behavior, then attach it to an event that is triggered by the user (such as a click or a keypress). Our code is generally attached as a callback: a single function which is executed in response to the event.

Here’s a practical example: suppose we wish to add some buttons to a page that adjust the text size. One way of doing this is to specify the font-size of the body element in pixels, then set the size of the other elements on the page (such as headers) using the relative em unit:

body {
  font-family: Helvetica, Arial, sans-serif;
  font-size: 12px;
}

h1 {
  font-size: 1.5em;
}
h2 {
  font-size: 1.2em;
}

Our interactive text size buttons can change the font-size property of the body element, and the adjustments will be picked up by other elements on the page thanks to the relative units.

Here’s the JavaScript:

function makeSizer(size) {
  return function() {
    document.body.style.fontSize = size + 'px';
  };
}

var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);

size12, size14, and size16 are now functions which will resize the body text to 12, 14, and 16 pixels, respectively. We can attach them to buttons (in this case links) as follows:

document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;
<a href="#" id="size-12">12</a>
<a href="#" id="size-14">14</a>
<a href="#" id="size-16">16</a>

VIEW ON JSFIDDLE

Emulating private methods with closures

Languages such as Java provide the ability to declare methods private, meaning that they can only be called by other methods in the same class.

JavaScript does not provide a native way of doing this, but it is possible to emulate private methods using closures. Private methods aren’t just useful for restricting access to code: they also provide a powerful way of managing your global namespace, keeping non-essential methods from cluttering up the public interface to your code.

Here’s how to define some public functions that can access private functions and variables, using closures which is also known as the module pattern:

var Counter = (function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  };   
})();

alert(Counter.value()); /* Alerts 0 */
Counter.increment();
Counter.increment();
alert(Counter.value()); /* Alerts 2 */
Counter.decrement();
alert(Counter.value()); /* Alerts 1 */

There’s a lot going on here. In previous examples each closure has had its own environment; here we create a single environment which is shared by three functions: Counter.increment, Counter.decrement, and Counter.value.

The shared environment is created in the body of an anonymous function, which is executed as soon as it has been defined. The environment contains two private items: a variable called privateCounter and a function called changeBy. Neither of these private items can be accessed directly from outside the anonymous function. Instead, they must be accessed by the three public functions that are returned from the anonymous wrapper.

Those three public functions are closures that share the same environment. Thanks to JavaScript’s lexical scoping, they each have access to theprivateCounter variable and changeBy function.

You’ll notice we’re defining an anonymous function that creates a counter, and then we call it immediately and assign the result to the Counter variable. We could store this function in a separate variable and use it to create several counters.

var makeCounter = function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  }  
};

var Counter1 = makeCounter();
var Counter2 = makeCounter();
alert(Counter1.value()); /* Alerts 0 */
Counter1.increment();
Counter1.increment();
alert(Counter1.value()); /* Alerts 2 */
Counter1.decrement();
alert(Counter1.value()); /* Alerts 1 */
alert(Counter2.value()); /* Alerts 0 */

Notice how each of the two counters maintains its independence from the other. Its environment during the call of the makeCounter() function is different each time. The closure variable privateCounter contains a different instance each time.

Using closures in this way provides a number of benefits that are normally associated with object oriented programming, in particular data hiding and encapsulation.

Creating closures in loops: A common mistake

Prior to the introduction of the let keyword in JavaScript 1.7, a common problem with closures occurred when they were created inside a loop. Consider the following example:

<p id="help">Helpful notes will appear here</p>
<p>E-mail: <input type="text" id="email" name="email"></p>
<p>Name: <input type="text" id="name" name="name"></p>
<p>Age: <input type="text" id="age" name="age"></p>
function showHelp(help) {
  document.getElementById('help').innerHTML = help;
}

function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': 'Your e-mail address'},
      {'id': 'name', 'help': 'Your full name'},
      {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];

  for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = function() {
      showHelp(item.help);
    }
  }
}

setupHelp();

VIEW ON JSFIDDLE

The helpText array defines three helpful hints, each associated with the ID of an input field in the document. The loop cycles through these definitions, hooking up an onfocus event to each one that shows the associated help method.

If you try this code out, you’ll see that it doesn’t work as expected. No matter what field you focus on, the message about your age will be displayed.

The reason for this is that the functions assigned to onfocus are closures; they consist of the function definition and the captured environment from thesetupHelp function’s scope. Three closures have been created, but each one shares the same single environment. By the time the onfocus callbacks are executed, the loop has run its course and the item variable (shared by all three closures) has been left pointing to the last entry in the helpText list.

One solution in this case is to use more closures: in particular, to use a function factory as described earlier on:

function showHelp(help) {
  document.getElementById('help').innerHTML = help;
}

function makeHelpCallback(help) {
  return function() {
    showHelp(help);
  };
}

function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': 'Your e-mail address'},
      {'id': 'name', 'help': 'Your full name'},
      {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];

  for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = makeHelpCallback(item.help);
  }
}

setupHelp();

VIEW ON JSFIDDLE

This works as expected. Rather than the callbacks all sharing a single environment, the makeHelpCallback function creates a new environment for each one in which help refers to the corresponding string from the helpText array.

Performance considerations

It is unwise to unnecessarily create functions within other functions if closures are not needed for a particular task, as it will negatively affect script performance both in terms of processing speed and memory consumption.

For instance, when creating a new object/class, methods should normally be associated to the object’s prototype rather than defined into the object constructor. The reason is that whenever the constructor is called, the methods would get reassigned (that is, for every object creation).

Consider the following impractical but demonstrative case:

function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
  this.getName = function() {
    return this.name;
  };

  this.getMessage = function() {
    return this.message;
  };
}

The previous code does not take advantage of the benefits of closures and thus could instead be formulated:

function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}
MyObject.prototype = {
  getName: function() {
    return this.name;
  },
  getMessage: function() {
    return this.message;
  }
};

However, redefining the prototype is not recommended, so the following example is even better because it appends to the existing prototype:

function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}
MyObject.prototype.getName = function() {
  return this.name;
};
MyObject.prototype.getMessage = function() {
  return this.message;
};

In the two previous examples, the inherited prototype can be shared by all objects and the method definitions need not occur at every object creation. SeeDetails of the Object Model for more details.

解密 JavaScript 中的 this

我想在本文解释JavaScript中的this,希望有助你理解this的工作机制。作为JavaScript程序员,学习this对于你的发展有很大帮助,可以说利大于弊。这篇文章的灵感来自于我最近的工作——我即将完成的书的最后章节——《JavaScript 应用程序设计 | JavaScript Application Design》(注意:现在你可以购买早期版本),我写的是关于scope工作原理的方面。

似是而非,这可能就是你对this的感觉:

很疯狂,不是吗?在这篇短文,我想揭开它的神秘面纱。

this的工作原理

如果一个函数被作为一个对象的方法调用,那么this将被指派为这个对象。

var parent = {

method: function () {

console.log(this);

}

};

parent.method();

// <- parent

注意这种行为非常“脆弱”,如果你获取一个方法的引用并且调用,那么this的值不会是parent了,而是window全局对象。这让大多数开发者迷惑。

 

var parentless = parent.method;

parentless();

// <- Window

底线是你应该查看调用链,以理解被调用函数是一个对象的属性还是它自己。如果它被作为属性调用,那么this的值将变成该属性的对象,否则this的值将被指派为全局对象或window。如果在严格模式下,this的值将是undefined。

在被当作构造函数的情况下,当使用new关键字时,this将被指派为被创建的实例对象。

 

function ThisClownCar () {

console.log(this);

}

new ThisClownCar();

// <- ThisClownCar {}

注意,在这种情况下没有办法识别出一个函数是否应该被用作构造函数,因此省略new关键字导致this的结果将是全局对象,就像我们上面看到的没有用parent调用的例子。

 

ThisClownCar();

// <- Window

改动this

.call.apply .bind 方法用来操作调用函数的方式,帮我们定义this的值和传递给函数的参数值。

Function.prototype.call 可以有任意数量的参数,第一个参数被分配给this,剩下的被传递给调用函数。

 

Array.prototype.slice.call([1, 2, 3], 1, 2)

// <- [2]

Function.prototype.apply 的行为和.call类似,但它传递给函数的参数是一个数组,而不是任意参数。

 

String.prototype.split.apply('13.12.02', ['.'])

// <- ['13', '12', '02']

Function.prototype.bind 创建一个特殊的函数,该函数将永远使用传递给.bind的参数作为this的值,以及能够分配部分参数,创建原函数的珂里化(curride)版本。

var arr = [1, 2];

var add = Array.prototype.push.bind(arr, 3);

// effectively the same as arr.push(3)

add();

// effectively the same as arr.push(3, 4)

add(4);

console.log(arr);

// <- [1, 2, 3, 3, 4]

作用域链中的this

在下面的例子,this将无法在作用域链中保持不变。这是规则的缺陷,并且常常会给业余开发者带来困惑。

function scoping () {

console.log(this);

return function () {

console.log(this);

};

}

scoping()();

// <- Window

// <- Window

有一个常见的方法,创建一个局部变量保持对this的引用,并且在子作用域中不能有同命变量。子作用域中的同名变量将覆盖父作用域中对this的引用。

function retaining () {

var self = this;

return function () {

console.log(self);

};

}

retaining()();

// <- Window

除非你真的想同时使用父作用域的this,以及当前this值,由于某些莫名其妙的原因,我更喜欢是使用的方法.bind函数。这可以用来将父作用域的this指定给子作用域。

function bound () {

return function () {

console.log(this);

}.bind(this);

}

bound()();

// <- Window

其他问题?

你是否有任何关于this的问题?关于this怎样?如果你认为我错过了任何其他边界情况,或优雅的解决方案,请让我知道

如果你喜欢这篇文章,一定要看看我即将到来的书《JavaScript 应用程序设计 | JavaScript Application Design》,您可以访问购买早期版本的链接。

可以直接拿来用的15个jQuery代码片段

jQuery里提供了许多创建交互式网站的方法,在开发Web项目时,开发人员应该好好利用jQuery代码,它们不仅能给网站带来各种动画、特效,还会提高网站的用户体验。

本文收集了15段非常实用的jQuery代码片段,你可以直接复制黏贴到代码里,但请开发者注意了,要理解代码再使用哦。下面就让我们一起来享受jQuery代码的魅力之处吧。

1.预加载图片

(function($) {
var cache = [];

// Arguments are image paths relative to the current page.
$.preLoadImages = function() {
var args_len = arguments.length;
for (var i = args_len; i--;) {
var cacheImage = document.createElement('img');
cacheImage.src = arguments[i];
cache.push(cacheImage);
}
}
jQuery.preLoadImages("image1.gif", "/path/to/image2.png");

源码

2. 让页面中的每个元素都适合在移动设备上展示

var scr = document.createElement('script');
scr.setAttribute('src', 'https://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js');
document.body.appendChild(scr);
scr.onload = function(){
$('div').attr('class', '').attr('id', '').css({
'margin' : 0,
'padding' : 0,
'width': '100%',
'clear':'both'
});
};

源码

3.图像等比例缩放

$(window).bind("load", function() {

// IMAGE RESIZE
$('#product_cat_list img').each(function() {
var maxWidth = 120;
var maxHeight = 120;
var ratio = 0;
var width = $(this).width();
var height = $(this).height();
if(width > maxWidth){
ratio = maxWidth / width;
$(this).css("width", maxWidth);
$(this).css("height", height * ratio);
height = height * ratio;
}
var width = $(this).width();
var height = $(this).height();
if(height > maxHeight){
ratio = maxHeight / height;
$(this).css("height", maxHeight);
$(this).css("width", width * ratio);
width = width * ratio;
}
});

//$("#contentpage img").show();

// IMAGE RESIZE
});

源码

4.返回页面顶部

// Back To Top
$(document).ready(function(){
$('.top').click(function() {
$(document).scrollTo(0,500);
});
});
//Create a link defined with the class .top
<a href="#" class="top">Back To Top</a>

源码

5.使用jQuery打造手风琴式的折叠效果

var accordion = {
init: function(){
var $container = $('#accordion');
$container.find('li:not(:first) .details').hide();
$container.find('li:first').addClass('active');
$container.on('click','li a',function(e){
e.preventDefault();
var $this = $(this).parents('li');
if($this.hasClass('active')){
if($('.details').is(':visible')) {
$this.find('.details').slideUp();
} else {
$this.find('.details').slideDown();
}
} else {
$container.find('li.active .details').slideUp();
$container.find('li').removeClass('active');
$this.addClass('active');
$this.find('.details').slideDown();
}
});
}
};

 

6.通过预加载图片廊中的上一幅下一幅图片来模仿Facebook的图片展示方式

var nextimage = "/images/some-image.jpg";
$(document).ready(function(){
window.setTimeout(function(){
var img = $("").attr("src", nextimage).load(function(){
//all done
});
}, 100);
});

源码

7.使用jQuery和Ajax自动填充选择框

$(function(){
$("select#ctlJob").change(function(){
$.getJSON("/select.php",{id: $(this).val(), ajax: 'true'}, function(j){
var options = '';
for (var i = 0; i < j.length; i++) {
options += '
'
+ j[i].optionDisplay + '
'
;
}
$("select#ctlPerson").html(options);
})
})
})

源码

8.自动替换丢失的图片

// Safe Snippet
$("img").error(function () {
$(this).unbind("error").attr("src", "missing_image.gif");
});
// Persistent Snipper
$("img").error(function () {
$(this).attr("src", "missing_image.gif");
});

源码

9.在鼠标悬停时显示淡入/淡出特效

$(document).ready(function(){
$(".thumbs img").fadeTo("slow", 0.6);
// This sets the opacity of the thumbs to fade down to 60% when the page loads
$(".thumbs img").hover(function(){
$(this).fadeTo("slow", 1.0);
// This should set the opacity to 100% on hover
},function(){
$(this).fadeTo("slow", 0.6);
// This should set the opacity back to 60% on mouseout
});
});

源码

10.清空表单数据

function clearForm(form) {

// iterate over all of the inputs for the form

// element that was passed in
$(':input', form).each(function() {
var type = this.type;
var tag = this.tagName.toLowerCase();
// normalize case

// it's ok to reset the value attr of text inputs,

// password inputs, and textareas
if (type == 'text' || type == 'password' || tag == 'textarea')
this.value = "";
// checkboxes and radios need to have their checked state cleared
// but should *not* have their 'value' changed
else if (type == 'checkbox' || type == 'radio')
this.checked = false;
// select elements need to have their 'selectedIndex' property set to -1
// (this works for both single and multiple select elements)
else if (tag == 'select')
this.selectedIndex = -1;
});
};

源码

11.预防对表单进行多次提交

$(document).ready(function() {
$('form').submit(function() {
if(typeof jQuery.data(this, "disabledOnSubmit") == 'undefined') {
jQuery.data(this, "disabledOnSubmit", { submited: true });
$('input[type=submit], input[type=button]', this).each(function() {
$(this).attr("disabled", "disabled");
});
return true;
}
else
{
return false;
}
});
});

源码

12.动态添加表单元素

//change event on password1 field to prompt new input
$('#password1').change(function() {

//dynamically create new input and insert after password1
$("#password1").append("");
});

源码

13.让整个Div可点击

blah blah blah. link
The following lines of jQuery will make the entire div clickable: $(".myBox").click(function(){ window.location=$(this).find("a").attr("href"); return false; });

源码

14.平衡高度或Div元素

var maxHeight = 0;
$("div").each(function(){
if ($(this).height() > maxHeight) { maxHeight = $(this).height(); }
});
$("div").height(maxHeight);

源码

15. 在窗口滚动时自动加载内容

var loading = false;
$(window).scroll(function(){
if((($(window).scrollTop()+$(window).height())+250)>=$(document).height()){
if(loading == false){
loading = true;
$('#loadingbar').css("display","block");
$.get("load.php?start="+$('#loaded_max').val(), function(loaded){
$('body').append(loaded);
$('#loaded_max').val(parseInt($('#loaded_max').val())+50);
$('#loadingbar').css("display","none");
loading = false;
});
}
}
});
$(document).ready(function() {
$('#loaded_max').val(50);
});

mootools date picker

http://www.monkeyphysics.com/mootools/script/2/

用intialize方法重新加载picker对象,类似reload refresh 方法,Mu

//datepicker
            calendar2 = null;
            var loadCalendar2 = function(){
               
                if($(‘date_depart’).value)
                {
                    currentmindate = new Date($(‘date_depart’).value);
                }
                if(!calendar2)
                {
                    calendar2 = new DatePicker(‘.date_retour’, {
                        days : trans_days,
                        months : trans_months,
                        minDate: currentmindate,
                        maxDate: currentmaxdate,
                        inputOutputFormat: ‘Y/m/d H:i:s’,
                        onSelect: function(date) {
                            //calendar1.options.maxDate = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
                            /* if ( $(‘research_button_etape_1’) == undefined )
                                this.callAjaxReservation(); */
                            if ( date.getDay() == 6 && !this.showWarningSaturday )
                            {
                                //this.showWarningSaturday = true;
                                this.showPopUp(‘/index.php?template=reservation/popup_warning_saturday’, ‘ajax’, 460, 360);
                            }
                        }.bind(this),
                        pickerClass: ‘datepicker_vista’,
                        allowEmpty: true,
                        toggleElements: ‘.showCalendar2’
                    });
                }
                else{
                    // change mindate of date_retour
                    $$(‘.date_retour’).set(‘value’, ”);
                    calendar2.initialize(‘.date_retour’, {minDate: currentmindate});
                }
            }
           
            calendar1 = new DatePicker(‘.date_depart’, {
                days : trans_days,
                months : trans_months,
                minDate: currentmindate,
                maxDate: currentmaxdate,
                inputOutputFormat: ‘Y/m/d H:i:s’,
                onSelect: function(date) {
                    //calendar2.options.minDate = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
                    /* if ( $(‘research_button_etape_1’) == undefined )
                        this.callAjaxReservation(); */
                    if ( date.getDay() == 6 && !this.showWarningSaturday )
                    {
                        //this.showWarningSaturday = true;
                        this.showPopUp(‘/index.php?template=reservation/popup_warning_saturday’, ‘ajax’, 460, 360);
                    }
                   
                    // load Calendar2
                    loadCalendar2();
                }.bind(this),
                pickerClass: ‘datepicker_vista’,
                allowEmpty: true,
                toggleElements: ‘.showCalendar’
            });
            // first load calendar2
            loadCalendar2();

Dojo与jQuery综合比较分析

最近Dojo和jQuery双双发布了最新的1.8版本,有着相同版本号的两个Javascript库也有许多核心的相同之处:相同的资源加载机制AMD、相同的选择器 引擎Sizzle等。作为业界知名的Javascript库,Dojo和jQuery在各自领域有着为数众多的拥护者。不过正所谓一把钥匙开一把锁,对一个项目来说肯定有个最适合它的工具库,用对了工具才能事半功倍。所以对项目经理或是技术总监来说,工程开始前的技术选型是关键一步,本文将对Dojo和 jQuery最新版本进行一个综合比较,重点在于区分两者的适用场景,而不涉及讨论孰优孰劣。

Licence

Dojo和jQuery都属于活跃的开源项目,并且都使用自由度很高的开源协议,可以免费使用,没有费用和许可问题。Dojo 许可协议为BSD&AFL,jQuery许可协议为MIT&GPL。

框架组成

Dojo属于重量级开发框架,由框架核心(dojo)、基本控件库(dijit)、扩展包(dojox)组成的,三部分都是由dojo官方提供。

jQuery属于轻量级框架,本身仅包含框架核心,另外有一个与jQuery名下的独立开源项目jQuery UI,它提供了最常用的8个控件。

另外jQuery的第三方开发者基于jQuery的框架核心开发了许多扩展控件和功能。不过这些第三方插件质量参差不齐,许可方式不同,需要慎重选择和使用。当选择了多家提供的插件时,还需要注意这些插件共存的兼容性问题。

总体来说,jQuery以及jQueryUI源于官方开发,比第三方插件更值得信赖,jQueryUI秉承了jQuery小块灵的特点,适合Web快速开发。不过鉴于jQueryUI提供控件数量上的限制,进行对UI交互依赖较重的应用时略显力不从心。

核心包大小

下图是Dojo与jQuery框架核心的大小比较,压缩后的dojo核心是135K,jQuery是93K。

编程风格

Dojo使用面向对象编程方式,为大型应用开发提供了保证;jQuery使用函数式编程方式,开发小型应用时更加灵活快捷。

Javascript 自身使用原型链模拟继承,但仅仅依靠原型链模拟的类继承不能提供全面的面向对象能力。Dojo在Javascript的基础进行了面向对象能力的加强和规 范化,提供了原生Javascript不具备的面向对象编程能力,比如父类方法重载(注意,不是重写)、多继承、构造函数调用链等等,并提供一系列面向对象编程规范的函数和属性declare、inherit、declaredClass、extend等作为dojo自身的编程基础。在dojo中,所有的 UI控件都被定义为类,许多Dojo的核心功能库也被定义为类,这都是出于更好的代码重用性的考虑。

Javascript从本质上来说属于函数式编程语言,jQuery没有改变Javascript的编程方式,使其学习成本大大降低。

常用功能支持情况

下图中数据来源自wiki,包含了一些流行的Javascript框架对于Web项目开发中经常会出现的功能需求的支持情况。本文仅涉及dojo与jQuery,其他框架的支持情况请看原文链接(http://en.wikipedia.org/wiki/Comparison_of_JavaScript_frameworks)

Dojo

jQuery

版本

1.7.2

1.7.2

代码组成

JavaScript + HTML

JavaScript

浏览器功能检测

Yes

Yes

DOM API包装

Yes

Yes

XMLHttpRequest

Yes

Yes

JSONP技术

Yes

Yes

接收服务器推送数据

Yes

Yes

其他数据格式支持

Yes: XML, HTML, CSV, ATOM

Yes: XML, HTML

拖拽功能

Yes

Yes

页面视觉特效

Yes

Yes

事件绑定

Yes

Yes

动作撤销/行为历史管理

Yes

With plugins

输入验证

Yes

With plugins

表格(Grid)控件

Yes

With plugins

树控件

Yes

With plugins

编辑器控件

Yes

With plugins

自动补全输入

Yes

With plugins

HTML生成器

Yes

Yes

自定义控件主题

Yes

Yes

Modal Dialog & Resizable Panel

Yes

With plugins

布局控件

Yes

With plugin

Canvas

Yes

手持设备支持

Yes

With plugin

Accessibility

Yes

Yes

ARIA支持

Yes

Yes

可视化编程工具

Yes

Yes

离线存储

No

No

跨浏览器的矢量绘图

Yes

图表控件

Yes

With plugin

Internet Explorer

6+

6+

Mozilla Firefox

3+

2+

Safari

4

3+

Opera

10.50 only

9+

Chrome

3

1+

由上图可见,dojo作为重量级的Javascript框架,提供了对绝大多数Web开发功能需求的支持。而jQuery除了对Web绘图功能支持不够以外,其余功能基本都可以通过引入第三方插件来提供支持。不过还是会涉及到前文提到的代码协议问题和插件间的兼容性问题。

代码风格

从代码风格上来看,除去jQuery标志性的“$”符号外,其实dojo与jQuery在一些常用API上的命名和参数列表基本相似。

核心代码性能

Web应用中的性能非常关键,dojo与jQuery在核心功能上的性能指标在Javascript框架中都属优秀,那么他们之间的比较结果如何呢?本文将比较两者在DOM操作、选择器以及事件绑定这三个最常用功能上的性能指标。场景如下:

1.  添加500个DOM节点;

2.  改变添加节点的style属性;

3.  在500个节点中选择一部分节点改变其innerHTML;

4.  为每个节点绑定鼠标事件;

这四步对应的dojo代码如下:

[javascript] view plaincopy

  1. function dojoStep1() {    
  2. var html = “”;    
  3. for (var i = 0; i < 500; i++) {    
  4.         html += “<div><span class=test data=0>” + i + “</span></div>”;    
  5.     }    
  6.     dojo.byId(“container”).innerHTML = html;    
  7. }    
  8. function dojoStep2 () {    
  9.     dojo.query(“.test”, “container”).style(“color”,”red” );    
  10. }    
  11. function dojoStep3() {    
  12.     dojo.query(“#container div:nth-child(odd)”).addContent(“<span>odd row:</span>”);    
  13.     dojo.query(“#container div:nth-child(even)”).addContent(“<span>even row:</span>”);    
  14. }    
  15. function dojoStep4() {    
  16.     dojo.query(“#container span”).on(“mouseenter,mouseleave”, function(e){    
  17. if(e.type == “mouseenter”){    
  18.             dojo.style(e.target, “color”, “blue”);        
  19.     }  
  20.     });  

四个步骤对应的jQuery代码如下:

[javascript] view plaincopy

  1. function jQueryStep1() {    
  2. var html = “”;    
  3. for (var i = 0; i < 500; i++) {    
  4.         html += “<div><span class=test data=0>” + i + “</span></div>”;    
  5.     }    
  6.     $(“#jContainer”)[0].innerHTML = html;    
  7. }    
  8. function jQueryStep2() {    
  9.     $(“#jContainer .test”).css({ color: “blue” });    
  10. }    
  11. function jQueryStep3() {    
  12.     $(“#jContainer div:even”).append(“<span>even row:</span>”);    
  13.     $(“#jContainer div:odd”).append(“<span>odd row:</span>”);    
  14. }    
  15. function jQueryStep4() {    
  16.     $(“#jContainer span”).hover(function () {    
  17.         $(this).css(“color”, “red”);    
  18.     }, function () {    
  19.         $(this).css(“color”, “black”);    
  20.     });    

Firefox/Chrome/IE8的测试结果如下所示:

根据Dojo1.7.2与jQuery1.7.2几个核心函数的比较结果可见,dojo与jQuery的选择器性能相差无几,dojo略胜一筹。由于 dojo和jQuery中的选择器就是dojo基金会下的项目Sizzle.js,所以这个结果也比较合理;从DOM操作来看,dojo的性能也优于jQuery;在事件绑定方面,dojo的性能明显高于jQuery。

总结

Dojo在众多前台框架中,属于重量级开发框架,在面向对象支持,代码架构,多极模块加载机制,控件完整性等方面有着较为突出的特点,适用于企业级或是复杂的大型Web应用开发;jQuery属于轻量级开发框架,架构和机制相对简单,易于开发,应用广泛,适用于相对简单的Web 2.0开发。 Dojo和jQuery分别为复杂应用开发和简单应用开发设计,由此也带来二者一些技术特点的不同。从工具本身角度看,二者特点鲜明,在实际项目中,需要根据具体需求来衡量,进行工具的选择。

mootools class 定义

Mootols 中定义class的时候, options中的最后一个选项后面要避免出现分隔号”,”, 同样 function后面没有内容了,也不需要再加逗号了, 否则IE7下js会报错

var request_finance = new Class({
    Implements: [Options, Events],
   
    options: {
        url: ‘/action/main/requestfinance/’,
        method: null
    },
   
    initialize: function(options){
       
        this.setOptions(options);
        this.OpenSession();
        this.ScaleInfoAB();
    },
   
    // initialize web service method
    initializeParams: function(params){
       
        if(typeof(params) == ‘object’)
        {
            return JSON.encode(params);
        }
        else
        {
            return ”;
        }
    },
   
    sendRequest: function(valMethod, valPrefix, fctSuccess, params){
        if(params == undefined || params == null)
        {
            params = ”;
        }
       
        new Request.JSON({
            url: this.options.url,
            method: ‘post’,
            data: {params: this.initializeParams(params), method: valMethod, paramPrefix: valPrefix},
            onSuccess: fctSuccess
        }).send();
       
    },

Namespacing your JavaScript

Perhaps a very uncommon approach to developing web applications that require JavaScript (but should be more common) is namespacing your scripts. This can be done very simple-like in a manner that is painless and nice-looking. And I know “looking-nice” should generally not be a determining factor on how your code should be written, but this indeed has some benefits as well.

COMING UP WITH A NAME

First things first, we need a name to space. If I were me (sometimes I’m not), but if I were, I would go with my own name Dustin Diaz. But of course, I’d want to abbreviate that to just DD. However since I don’t want this to be confused with Drag n’ Drop, I’ll add my middle initial which will make it DED. Now from this point on, all JavaScript for DustinDiaz.com or developed by me for others, it will live under the DED Object.

EXTENDING THE NAMESPACE

Perhaps my most favorite way of setting up an application object is to do the following steps:

  • Create the DED Object as a function literal
  • self-invoke the function
  • return my methods

This is demonstrated below:

NAMESPACE OBJECT EXAMPLE
var DED = function() {

	return {

		method_1 : function() {

			// do stuff here

		},

		method_2 : function() {

			// do stuff here

		}

	};

}();

The benefit of this structure is that due to the fact that we have a function literal, we can add private properties and methods into the function before returning an object. This can be done as follows:

ADDING PRIVATE PROPERTIES AND METHODS
var DED = function() {

	var private_var;

	function private_method() {

		// do stuff here

	}

	return {

		method_1 : function() {

			// do stuff here

		},

		method_2 : function() {

			// do stuff here

		}

	};

}();

In this case if we were to set our private_method to return… oh I dunno, “Hello World” – then we can call that within our public methods, alert it, and indeed receive a nice little message. However one thing to note, I would argue whether or not our public methods are… public. Of course, in this simple example, they are nothing but public. But there are also “privledged methods” which have access from the outside (public) and also have access to private methods. They are indeed possible to delete and replace, but you can never get at the information to which they were trying to keep secret.

Douglas Crockford also explains a bit on this in relation to Objects and its members, and specifically using thethis keyword in his article labeled private memebers in JavaScript. Hopefully the examples on this page will get you thinking of how better to organize your code, avoid conflicts with external application code, playing nice with the “other JavaScript kids,” and indeed the benefits of closures (inner functions). Have fun!