JavaScript 中所有函数的参数都是按值传递的

本文地址:kamal

JavaScript 中所有函数的参数都是按值传递的

当参数是值类型的时候

function changeStuff(num){
    num = num * 10;
}
var num = 10;
changeStuff(num);

alert(num);//10

可以看到当函数参数是值类型的时候在函数内部改变变量的值,不会在函数作用域外表现出来,说明函数参数是传值的方式传入函数内部。

当函数参数是Object类型的时候

function changeStuff1(obj1){
    obj1.item = "changed";
}

var obj1 = new Object();
obj1.item = "unchanged";

changeStuff1(obj1);
alert(obj1.item); //changed

这时obj1内的item的值在函数之外表现出来了,如果是传值的话,函数内部改变变量的值应该不会影响到外部的obj1啊。难道对Object类型的参数传的是传引用? 再看第三个例子

function changeStuff2(obj2){
    obj2 = {item:"changed"};
}

var obj2 = new Object();
obj2.item = "unchanged";

changeStuff2(obj2);
alert(obj2.item);//unchanged

《JavaScript高级程序设计》一书中就解释到这里就说已经证明了“ECMAScript 中所有函数的参数都是按值传递的”。可是我还是不能理解为什么第二个和第三个例子会有不一样的结果。又找了一些函数传参数的解释[1]

传值   
 是把实参的值赋值给行参   
 那么对行参的修改,不会影响实参的值 

传地址   
 是传值的一种特殊方式,只是他传递的是地址,不是普通的如int   
 那么传地址以后,实参和行参都指向同一个对象 

传引用   
 真正的以地址的方式传递参数   
 传递以后,行参和实参都是同一个对象,只是他们名字不同而已   
 对行参的修改将影响实参的值

在第二个例子中,obj1传入函数 changeStuff1 时,changeStuff1函数内部变量 obj1是一个新的内存变量,只是期指向旧的对象值,所以改变对象中item的值会在函数外部表现出来。

在第三个例子中obj2传入函数 changeStuff12时,changeStuff2函数内部变量 obj2是一个新的内存变量,依旧期指向旧的对象值,但在函数内部又给内部的obj2变量重新赋值,使其指向一个新的对象,所以改变对象中item的值不会影响函数外部的obj2对象。

最后得出结论,JavaScript 中所有函数的参数都是按值传递的 ,只是对于Object类型来说,这个值本身就是一个指向Object对象的地址。最后给出完整的测试函数[2]

function changeStuff(num, obj1, obj2)
{
    num = num * 10;
    obj1.item = "changed";
    obj2 = {item: "changed"};
}

var num = 10;

var obj1 = new Object();
obj1.item = "unchanged";

var obj2 = new Object();
obj2.item = "unchanged";

changeStuff(num, obj1, obj2);
alert(num); //10
alert(obj1.item); //changed
alert(obj2.item); //unchanged

[1] 传值和传引用、传地址的区别是什么?

[2] Is JavaScript is a pass-by-reference or pass-by-value language?

//20141205 append
几个例子用相同的变量名,有误导之嫌,起码函数参数可以用别的名称

jQuery中.bind(),.live()和.delegate()的区别

本文翻译自 Steve Schwartz The Difference Between jQuery’s .bind(), .live(), and .delegate()
本文地址 kamal http://yukun.im/javascript/232

jquery tags bind live delegate

.bind().live(), 和 .delegate() 之间的区别并不明显。但是理解它们的不同之处有助于写出更简洁的代码,并防止我们的交互程序中出现没有预料到的bug。

基础

DOM树

首先,图形化的HTML文档能帮助我们更好的理解。一个简单的HTML页面看起来应该像这样

DOM tree sample

事件冒泡(也称作事件传递)(Event bubbling aka event propagation)

点击一个链接,触发绑定在链接元素上的 click 事件,进而触发绑定到这个元素的click事件的函数。

$('a').bind('click', function() { alert("That tickles!") });

所以一次点击会触发一个alert。
trigger the alert
然后,这个 click 事件会从DOM树向上传递,传播到父元素,然后传递给每一个祖先元素。
event propagates up the tree

在DOM树中, document 是根节点。
现在我们能容易的解释.bind().live(), 和 .delegate()之间的差别了

.bind()

$('a').bind('click', function() { alert("That tickles!") });

这是最直接的绑定方法。jQuery 扫描文档找到所有 $(‘a’) 元素,然后给每一个找到的元素的 click 事件绑定处理函数。

.live()

$('a').live('click', function() { alert("That tickles!") });

jQuery绑定处理函数到 $(document) 元素,并把 ‘click’ 和 ‘a’ 作为函数的参数。有事件冒泡到document节点的时候,检查这个事件是不是 click 事件,target element能不能匹配 ‘a’ css选择器,如果两个条件都是true,处理函数执行。

live方法也可以绑定到指定的元素(或者说“上下文(context)”)而不用绑定到document,比如:

$('a', $('#container')[0]).live(...);

.delegate()

$('#container').delegate('a', 'click', function() { alert("That tickles!") });

jQuery扫描文档找到 $(‘#container’),绑定处理函数到他的 click 事件,’a’ css选择器作为函数的参数。当有事件冒泡到 $(‘#container’),检查事件是不是 click,并检查target element是不是匹配css选择器,如果两者都符合,执行函数。

注意这次和 .live() 方法很相似,除了把事件绑定到特定元素与跟元素的区别。精明的JS’er 或许会总结成 $(‘a’).live() == $(document).delegate(‘a’),真的是这样吗? 不,不全是。

为什么 .delegate() 比 .live() 好

jQuery 的 delegate方法比 live 方法更应该成为首选有一个原因。考虑以下的场景:

$('a').live('click', function() { blah() });
// or
$(document).delegate('a', 'click', function() { blah() });

速度

上面第二个执行比第一个快,因为第一个会遍历整个文档查找 $(‘a’) 元素,并保存为jQuery对象,但是live方法只需要传一个字符串参数’a’而已,$() 方法并不知道我们会用链式表达式在后面用上.live()。

delegate 方法就只需要找到并存贮 $(document)元素就够了。

有一种hack是在 $(document).ready()之外调用live方法,这样就会立即执行。这时候DOM还没有填充,也就不会查找元素或创建jQuery对象。

灵活性和链式语法

这方面live方法依然令人费解。想一下,它链在$(‘a’)对象,但实际上是在$(document)对象起作用。因为这个原因,在链式表达式中使用live让人很不安,我觉得live方法变成一个全局的jQuery方法 $.live(‘a’,…) 会更有意义。

只支持css选择器

最后,live方法有一个最大的缺点,只能用css选择器,用起来很不方便。

有关css选择器的缺点,参看 Exploring jQuery .live() and .die()

原作者更新

为什么使用 .live() 或 .delegate() 而不用 .bind()

最后,bind 方法看起来更清晰,更直接,是吗?但是这里有两个原因我们推荐 delegate 或 live:

  • 绑定事件处理函数到还不存在DOM中的元素。 bind 方法直接绑定函数到每个单独的元素,不能绑定到还没有添加到页面里的元素,如果你写了$(‘a’).bind(…),然后用ajax给页面增加了新的链接,新添加的链接不会绑定事件。live 或 delegate 或者其它绑定到祖先元素的事件,让现在有的元素,或者以后增的元素都可以使用。
  • 绑定处理函数到一个元素或者少数几个元素,监听后代元素,而不是绑定100个相同的处理函数到单独的元素。这样更有性能优势。

阻止冒泡

最后注意一下事件冒泡。通常我们能用这样的方法阻止其他处理函数:

$('a').bind('click', function(){
    e.preventDefault();
    //or
    e.stopPropagation();
})

但是在这里,用 live 或 delegate 方法绑定的事件会一直传递到事件真正绑定的地方才会执行。这时其他的函数已经执行过了。

//更新2013-05-16 10:42:21

在jQuery 1.7中,.on()方法 提供绑定事件处理的所有功能。 http://www.css88.com/jqapi-1.9/on/