[图解] 你不知道的 JavaScript - “this”

JavaScript 里的 this 到底指得是什么?很多人都会告诉你 this 指的是当前对象。这样理解对么?在大多数情况下确实没错。比如我们经常会在网页上写这样的 JavaScript:

 

<input type="submit" value="提交" onclick="this.value='正在提交数据'" />

 

这里的this显然指的是当前对象,即这个提交按钮。通常,我们使用this的情况都与此类似。但是有什么情况不是这样的呢?

大家看看这个例子:

 

var foo = function() {
    console.log(
this);
}

foo();
new foo();

 

比较一下 foo() 和 new foo() 的运行结果,你会发现,前者 this 指向的并非 foo 本身,而是当前页面的window对象,而后者才真正的指向foo。这是为什么呢?

其实这牵涉到JavaScript的一条重要特性,就是所谓的“闭包”。闭包这个概念说复杂也不复杂,但也不是简单到能用一两句话说清。偶会在以后的文章中深入探讨这个Javascript 最重要的特性。现在,我要告诉大家的是,因为闭包的存在,JavaScript中的作用域变得相当重要。

所谓的作用域,简单的说,就是创建一个函数时在什么环境下创建的。而this变量的值,如果没有指定的话,就是函数当前的作用域。

 

在前面的例子里,foo() 函数是在全局作用域(这里就是window对象),所以this的值是当前的window对象。而通过 new foo() 这样的形式,其实是创建了一个foo()的副本,并在这个副本上进行的操作,所以这里的this就是foo()的这个副本。

 

这样讲可能有点抽象,大家来看个实际的例子:

 

<input type="button" id="aButton" value="demo" onclick="" />
<script type="text/javascript">
function demo() {
    
this.value = Math.random();
}

</script>

 

如果直接调用demo() 函数,程序就会报错,因为demo函数是在window对象中定义的,所以demo的拥有者(作用域)是window,demo的this也是window。而window是没有value属性的,所以就报错了。

图解

如果我们通过创建副本的方式,将这个函数的副本添加到一个HTML元素,那么他的所有者就成了这个元素,this也指代了这个元素:

 

document.getElementById("aButton").onclick = demo;

 

这样就将aButton的onlick属性设置为demo()的一个副本,this也指向了aButton。

图解

你甚至可以为多个不同的HTML元素创建不同的函数副本。每个副本的拥有者都是相对应的HTML元素,各自的this也都指向他们的拥有者,不会造成混乱。

图解

 

但是,如果你这样定义某个元素的onlick事件:

 

<input type="button" id="aButton" value="demo" onclick="demo()" />

 

点击这个button之后,你会发现,程序又会报错了——this又指向了window!

其实,这种方法并没有为程序创建一个函数,而只是引用了这个函数。

具体看一下区别吧。

 

使用创建函数副本的方法:

 

<input type="button" id="aButton" value="demo" />
<script type="text/javascript">
var button = document.getElementById("aButton");
function demo() {
    
this.value = Math.random();
}

button.onclick
= demo;
alert(button.onclick);
</script>

 

得到的输出是:

 

function demo() {
    
this.value = Math.random();
}

 

使用函数引用的方法:

 

<input type="button" id="aButton" value="demo" onclick="demo()" />
<script type="text/javascript">
var button = document.getElementById("aButton");
function demo() {
    
this.value = Math.random();
}

alert(button.onclick);
</script>

 

得到的输出是:

 

function onclick() {
    demo();
}

 

这样就能看出区别了吧。函数引用的方式中,onclick事件只是直接调用demo()函数,而demo()函数的作用域仍旧是window对象,所以this仍然指向window。

图解

 

这样就又引出了一个问题:既然函数副本这么好用,为什么还需要函数引用的方法呢?答案是性能。每新建一个函数的副本,程序就会为这个函数副本分配一定的内存。而实际应用中,大多数函数并不一定会被调用,于是这部分内存就被白白浪费了。而使用函数引用的方式,程序就只会给函数的本体分配内存,而引用只分配指针,这样效率就高很多。程序员么,节约为主,恩

所以我们来看一个更好的解决方案:

 

<script type="text/javascript">
function demo(obj) {
    obj.value 
= Math.random();
}

</script>
<input type="button" value="demo" onclick="demo(this)" />
<input type="button" value="demo" onclick="demo(this)" />
<input type="button" value="demo" onclick="demo(this)" />

 

这样,效率和需求就都能兼顾了。

 

最后再多讲一句:在前面的文章里,我特别强调了“如果没有指定this的话”。其实this是可以指定的。Function对象有两个方法:call()和apply()。这两个方法都支持指定一个函数中的this。有兴趣的话您可以去查一下Javascript的手册,看看这两个函数都是干什么用的。而我们经常用的 new foo() 可以用以下这段伪代码来描述:

 

function new (somefunction) {
    
var args = [].slice.call(arguments, 1);
    somefunction.prototype.constructor 
= somefunction;
    somefunction.apply(somefunction.prototype, args);
    
return somefunction.prototype;
}

 

现在明白了,在本文开头的第一个例子里,new foo() 的 this 为什么是 foo 了吧

 

文中部分内容引自 http://www.quirksmode.org/js/this.html

posted @ 2008-04-20 20:06 棕熊 阅读(3159) 评论(47)  编辑 收藏 所属分类: JavaScript

  回复  引用  查看    
#1楼 2008-04-20 20:15 | Justin      

btw:图是用什么画的呀,好漂亮!

  回复  引用  查看    
#2楼 [楼主]2008-04-20 20:20 | 棕熊      
@Justin
photoshop啦,呵呵
  回复  引用  查看    
#3楼 2008-04-21 10:11 | Jeffrey Zhao      
顶,支持。
  回复  引用  查看    
#4楼 2008-04-21 10:20 | airwolf2026      
赞一下楼主的图....
ps:不过黑色背景和白色字体伤眼...
  回复  引用  查看    
#5楼 2008-04-21 10:40 | looping      
写的很清晰~
  回复  引用  查看    
#6楼 2008-04-21 11:02 | 杨正祎(阿一)      
图都那么华丽,顶一下。
  回复  引用  查看    
#7楼 [楼主]2008-04-21 11:07 | 棕熊      
@airwolf2026
呵呵,这个其实是个很常见的误解
最早会有这种误解是因为早期的显示器辐射都很大的关系
一般现在的显示器都不会有这样的问题啦
  回复  引用  查看    
#8楼 2008-04-21 11:11 | 杨正祎(阿一)      
果然还是黑色模板中,使用图片投影效果比较cool。
  回复  引用  查看    
#9楼 2008-04-21 11:25 | 小No      
写都不错~~~对this又有了进一步的了解

虽然平时都是使用以下这种方式,暂时没出过什么问题
<script type="text/javascript">
function demo(obj) {
obj.value = Math.random();
}
</script>
不过看了这篇文章之后,以后对this的使用会比较小心点
  回复  引用  查看    
#10楼 [楼主]2008-04-21 11:26 | 棕熊      
@杨正祎(阿一)
其实白色模板也行,apple的网站就是
关键是图片就要用深色的了,呵呵
  回复  引用  查看    
#11楼 [楼主]2008-04-21 11:29 | 棕熊      
@小No
一般应用的话,这种的确是个不错的方式啦
  回复  引用  查看    
#12楼 2008-04-21 12:28 | 任力      
很漂亮!gg的front-end功底很是深厚呀!

PS:发随笔的时候以摘要的形式发表,一个小建议~~
  回复  引用  查看    
#13楼 [楼主]2008-04-21 13:59 | 棕熊      
@任力
这个……因为发第二篇随笔的时候,系统自动生成了摘要,所以偶以为都是这样的。以后会注意的了^^"
  回复  引用  查看    
#14楼 2008-04-21 14:49 | Klesh Wong      
首先,那个不叫闭包。
其次,直接调用,或者click button时this指向window对象时不会报错,也不会出错。任何时候都可以向window对象添加删除自定义的属性。不会出错。
再者,demo函数根本没有所谓“副本”存在,无论多少个button.onclick=demo,demo都是同一个,没有副本。
而后,<input type="button" value="demo" onclick="demo(this)" />每次定义input的onclick属性本质上相当于创建一个新的函数,理论上来讲这种做法是要比使用dom方式订阅要费内存的,也就是理论其实应该是button.onclick= demo这种方式比较好,不过差别不会明显,不用特意去在意。
最后,this指向哪个象,取决于函数被调用时左边那个对象,foo和new foo()的区别类似于一个是直接在当前作用域下调用,另一个则是先new一个Object,再将这个实例当作foo的this指针对foo进行调用,当然,事实可能比这个要稍微复杂一些,但大抵如此。

function outter() {
var closure = "i'm clourse of inner function";
function inner() {
return closure;
}
return inner;
}
var f = outter();
上面的closure变量就是inner的闭包。闭包最大的特质就是变量closure只有inner函数能够访问到。
  回复  引用  查看    
#15楼 [楼主]2008-04-21 15:05 | 棕熊      
@Klesh Wong

这里我更想强调的是作用域链的概念,闭包只是作用域链存在的基础,所以带到了一下。本来就是写给一个初学者看的教程,所以没有特别深入,很多只是用类似的东西类比了一下。如果你对这个问题有兴趣,我以后可以写个专题,我们继续再做探讨

-----------------------------------
其次,直接调用,或者click button时this指向window对象时不会报错,也不会出错。任何时候都可以向window对象添加删除自定义的属性。不会出错。
-----------------------------------

或许我说报错,这个的确是没有讲述的很精确。严格的说是“函数并没有得到它预期的效果”。

-----------------------------------
再者,demo函数根本没有所谓“副本”存在,无论多少个button.onclick=demo,demo都是同一个,没有副本。
-----------------------------------

这个的确是为demo函数创建了副本。而这个副本就是button.onlick。
这个概念类似于 var a = b 是为 b 创建了一个叫 a 的副本

-----------------------------------
而后,<input type="button" value="demo" onclick="demo(this)" />每次定义input的onclick属性本质上相当于创建一个新的函数,理论上来讲这种做法是要比使用dom方式订阅要费内存的,也就是理论其实应该是button.onclick= demo这种方式比较好,不过差别不会明显,不用特意去在意。
------------------------------------

在这个例子里确实是这样。我在文章中也指明了,是先为onclick时间创建一个匿名函数,然后再在这个匿名函数中调用demo函数。这个匿名函数本身是有开销的,这个没错。但是当demo函数相当复杂的时候(很多时候往往都是这样的。其实如果只是一个简单操作的话,大多数人应该直接用inline的方式写在元素的onlick属性里了),匿名函数的开销可能远远小于创建函数副本的开销,所以这种函数引用的优越性就能体现出来了。

------------------------------------
foo和new foo()的区别类似于一个是直接在当前作用域下调用,另一个则是先new一个Object,再将这个实例当作foo的this指针对foo进行调用,当然,事实可能比这个要稍微复杂一些,但大抵如此。
------------------------------------

这个请参见我最后用伪代码对new的实现,事实的确是那样没错的。

  回复  引用  查看    
#16楼 2008-04-21 15:25 | Windie Chai(笑煞天)      
第一次见到如此华丽的图示。
  回复  引用  查看    
#17楼 2008-04-21 15:58 | 狼Robot      
学习
  回复  引用  查看    
#18楼 2008-04-21 16:34 | Nicholas Yuen      
学习了~
博主的blog真实赏心悦目啊~
  回复  引用  查看    
#20楼 2008-04-21 17:01 | Renard      
你的文章有相当多的概念性错误。在自己没有搞清楚问题之前,请谨慎发言,以免误导他人。下面按你文章中的顺序一一指出你的错误:

1.[原文]“比较一下 foo() 和 new foo() 的运行结果,你会发现,前者 this 指向的并非 foo 本身,而是当前页面的window对象,而后者才真正的指向foo”

[错误分析] 后者报告说那(this)是一个object。但这个object可不是foo这个函数对象,而是用foo这个构造函数构造的一个新对象。


2.[原文] “而this变量的值,如果没有指定的话,就是函数当前的作用域。”

[错误分析] this不是一个变量,而是一个关键字。它的真正意思,是用以调用函数的那个对象。即,如果你把函数作为某个对象的方法来调用,那么此时函数中的this就是指这个对象。唯一的例外是:直接调用一个函数时(当然不包括使用call和apply等方法来调用一个函数的情况),函数体内的this指向的是全局对象(对于Web客户端JavaScript,它就是window所指的对象)。而函数的作用域是另一个概念。举个例子来说:

function f1() {
var v1 = 'hello from f1!';
function f2() { var v2 = 'hello from f2'; alert(v1); }
f2();
}

f1();

这里函数f1的作用域是全局对象,而其中定义的变量v1的作用域则是f1的调用对象(是call object,而不是the object through which f1 is called!详见《JavaScript权威指南》一书),v1是这个调用对象的一个属性。在f1的函数体内进行的名称(那些没有用“this.name”形式进行访问的名称)解析是在一个作用域链上进行的(这个链包括全局对象和f1的调用对象),先在最底层的那个调用对象中查找,如果找不到再到上一层的对象中查找,直到查过全局对象。同理,函数f2的作用域是f1的调用对象,在f2的函数体中进行的名称解析是在一个包含着三个对象的作用域链上进行的:f2的调用对象 -> f1的调用对象 -> 全局对象。

楼主在本文中所讨论的问题,其实与闭包这个概念没有任何关系。这里就顺便说一下“闭包”的概念。JavaScript中所有函数对象都在内部保存着对自己的作用域的引用(实际上由此就引用到了其整个作用域链)。函数与它在其中执行的那个作用域的结合体就叫闭包。所以所有JavaScript函数都是闭包。这个特点一般情况下不会让人觉得有什么特别之处,但在一个函数把自己的内嵌函数作为返回值返回,使这个内嵌函数能在定义它的那个函数之外执行时,其特别之处就显示出来了。大多数情况下我们所说的闭包,实际上是指这种嵌套函数。

在我举的上面那例子中,如果把alert(v1)改为alert(this.v1),输出结果将是字符串undefined。这是因为此时的this指向的是全局对象,而它没有定义一个名为v1的属性。这可以证明楼主的说法是错误的,因为如果this此时指向函数当前作用域的话,那么this.v1就应该能访问到f1中定义的v1变量。


3.[原文] “而通过 new foo() 这样的形式,其实是创建了一个foo()的副本,并在这个副本上进行的操作”

[错误分析] new foo()创建的不是foo()的副本,也不是foo的副本。它创建了一个新对象,并用这个对象调用foo函数。如果把foo看作一个类定义的话,那么这个语句创建了这个类的一个实例。


4.[原文] “如果直接调用demo() 函数,程序就会报错,因为demo函数是在window对象中定义的,所以demo的拥有者(作用域)是window,demo的this也是window。而window是没有value属性的,所以就报错了。”

[错误分析] 你当真运行过这里所说的程序了吗?肯定只是想象了一下它的结果吧。明明白白地跟你说吧,你举的这个例子:

function demo() {
this.value = Math.random();
}

demo();

可以正常运行,不会报错。其运行结果是为全局对象添加了一个名为value的属性,并将其值设置为一个随机数。这个错误也反映了你根本不知道在JavaScript中为一个对象不存在的属性赋值会先自动创建这个属性再赋值这一现象。

5.[原文] “document.getElementById("aButton").onclick = demo; 这样就将aButton的onlick属性设置为demo()的一个副本,this也指向了aButton。”

[错误分析] 这样做并没有创建demo的副本(“demo()”的副本是什么意思?)。它只是让onclick这个属性指向demo这个函数。


6.[原文] “但是,如果你这样定义某个元素的onlick事件:‘<input type="button" id="aButton" value="demo" onclick="demo()" />’。点击这个button之后,你会发现,程序又会报错了——this又指向了window!其实,这种方法并没有为程序创建一个函数,而只是引用了这个函数。”

[错误分析] 你又在臆想程序运行结果了。这个程序不会出错!原因同前。而且,这种方法的确为程序创建了一个新函数并让按钮对象的onclick属性指向这个函数,它是一个匿名函数,其定义相当于:function() { demo(); }

至于你的这个例子:

var button = document.getElementById("aButton");
function demo() {
this.value = Math.random();
}
alert(button.onclick);

输出结果因浏览器而异,在Firefox中是“function onclick(event) { demo() }”。在IE中是“function anonymous() { demo() }”。这是二者对匿名函数的显示的不同处理方法造成的结果。


[总结] 文章中还有许多错误,其类型没有超出我上面指出的,就不再赘述了。从你的文章可以看出,你对JavaScript的认识还相当肤浅。当然,谁都不是一天就成为行家的。但是在这样撰文向大家宣讲技术之前,你应该认认真真地多看几本书。特别建议你详细读读《JavaScript权威指南》这本书和《ECMAScript语言规范》,而且不止读一、两遍。

最后说一下函数体内的名称解析问题。其实很简单:如果一个名字是通过this来引用的,那么就在用以调用函数的那个对象自身及其原型对象链中查找(如果函数是直接调用的,那么可以把全局对象当作其调用对象);否则就在函数的作用域链(包括其调用对象。再次强调:是调用对象,而不是用以调用它的对象)中查找。
  回复  引用  查看    
#21楼 2008-04-21 17:30 | 怪怪      
@Klesh Wong
@Renard
支持一下二位, 虽然前两天刚和Klesh Wong在一些无聊问题上致过气 :P 倒不是说LZ自身的理解一定有问题, LZ显然还是挺强的, 不过写文章还是严谨一点好, 尤其是不能辜负老赵兄的一番推荐啊~
  回复  引用  查看    
#22楼 2008-04-21 17:45 | 玉开      
不错,支持一下
  回复  引用  查看    
#23楼 2008-04-21 17:49 | Klesh Wong      
@棕熊
啧啧,有意思,你可以查一下ecmascript的规范,闭包是javascript的特性,而“作用域“的概念是第一种编程语言都有的。事实上根据规范,闭包是由于“作用域”加上Javascript的函数式编程而产生出来的一种特性。所谓的“作用域链”则是实现“闭包”的技术。

楼主对术语有待提高,所谓副本,即是某一个副本的变动不会影响到其它的实例,想象一下影印,在影印本上涂改是不会影响到原件的。再参考一下《魔兽世界》中的副本,彼此也是不会影响的。

而楼主所举的例子中,demo如果变动则是一定会影响到其它引用的。
var a = b;的例子要看b是否“引用类型”,如果b是“原始类型”的话,确实可以理解为“副本”,但function,javascript的函数事实上是一种“引用类型”
var b = function() {alert('c')};
var a = b;
的时候,a和b是一样的指向function() {alert('c')},当针对function() {alert('c')}这个函数体有变动时是会影响到a和b的行为的。

<input type="button" value="demo" onclick="demo(this)" />
这种才是会产生所谓的“匿名函数”。。
button.onclick = demo;
这种直接就把demo函数赋给button.onclick,并不存在“匿名函数”。然后所谓demo函数本身有多复杂,这个没有关系,两种方式最终都要调用它。
而button.onclick = demo;会比较自然,因为这时候demo的this指针是指向相应的dom对象。注意这里没有副本,没有开销。


"现在明白了,在本文开头的第一个例子里,new foo() 的 this 为什么是 foo 了吧"
这句话根本就说明你没有理解对头,首先,foo是什么?foo是一个函数,this是永远不会指向到foo函数本身的,而是指向foo左边那个实例或者call/apply指定的某个实例,但它绝对不会是指向foo。要注意javascript的“函数”同时也是一种变量,一种“引用类型”。
  回复  引用  查看    
#24楼 2008-04-21 18:11 | Klesh Wong      
@怪怪
呵呵,多谢,大家就求同存异吧。
  回复  引用  查看    
#25楼 2008-04-21 18:48 | Flyingis      
似曾相识:
http://www.quirksmode.org/js/this.html
blog设计很漂亮,佩服楼主的设计功底!
  回复  引用  查看    
#26楼 [楼主]2008-04-21 18:50 | 棕熊      
@References
额,忽视了,现在就加

@Klesh Wong
@Renard
原本只是想用浅显的语言讲一些javascript的基本问题,没想到只是因为想尽量的让人明白,却在文章中写下一些令人更加confuse的东西。受教了,以后一定注意。希望两位gg以后也多指教,呵呵
  回复  引用  查看    
#27楼 [楼主]2008-04-21 18:52 | 棕熊      
@Flyingis
恩,确实有部分内容借鉴自quirksmode,现在加上了refference
话说quirksmode也算是我的入门网站了,很好的一个地方,顺便推荐大家去看了
  回复  引用  查看    
#28楼 2008-04-21 18:57 | RicCC      
继续努力
  回复  引用  查看    
#29楼 2008-04-21 19:31 | 秋千      
受教了,以前没研究这么原理的东西。
  回复  引用  查看    
#30楼 2008-04-21 20:00 | 镜涛      
@Renard
看样子我也该多看看《权威指南》了
  回复  引用  查看    
#31楼 2008-04-21 22:08 | Axel      
@Renard
真正厉害!

我看一下LZ,感觉有表达不妥,但重要的是我自己也无法表达清楚。
但你至少两个观点表达得非常清楚。
这方面你很严谨,向你学习。

欣赏LZ的精神,很努力。

团队中两者谁更好合作?所谓后台程序员一定喜欢LZ的。:)
  回复  引用  查看    
#32楼 2008-04-22 08:21 | 李战      
才看到这么强的文章,深深地被楼主的艺术境界打动。支持!
  回复  引用    
#33楼 2008-04-22 09:24 | xiongharry [未注册用户]
本来我也想指出一点问题的,看到这么多人说就算了,总归还是要谢谢楼主,写出这么细致的东西。

其实JS老手也不用太过于较真,这个不是写给你们看的。这篇文章对于JS新手来说是非常有益的。 It's enough!

PS:可以提一下js的对象树的概念,也许更容易让人理解,也更能理解js的本质。
  回复  引用    
#34楼 2008-04-22 14:05 | 夕颜子 [未注册用户]
太牛牛啦`` 楼主超牛 小鸟 学习啦
  回复  引用  查看    
#35楼 2008-04-27 02:31 | Joyaspx      
高手众多啊,向各位学习了
  回复  引用  查看    
#36楼 2008-04-28 13:12 | Henllyee.Cui      
写得很不错。我还是一个菜鸟,经常看老赵的东西,跟着后面学会了asp.net ajax的框架。看了这篇文章确实感觉大胡仔的水平是可以的。我有个建议就是:大胡仔能不能留一个Q&A板块,供一些人提问。
在这我有一个问题:在javascript中,怎样让this不去动态分配?因为我在写些封装的类的时候经常遇到这样的问题。当我一个类中定义了一个私有成员(如:this._height)在类中的某个共有方法中的一个dom元素的事件绑定时使用到了这个私有成员(如:obj.onclick=function(){...}中调用刚才的_height),this就会指向这个dom元素了,所以不能调用。请问我如何不让this去动态分配,而是一直指向特定类的对象?我还是个菜鸟,请不要见笑啊,呵呵
  回复  引用  查看    
#37楼 [楼主]2008-04-28 16:54 | 棕熊      
@Henllyee.Cui
你可以试试看apply(bind, args[])方法哦
可以通过第一个参数bind来指定任意的this
  回复  引用  查看    
#38楼 2008-04-29 13:49 | Henllyee.Cui      
@棕熊
好的。我来试试看看,谢谢大胡仔!
  回复  引用    
#39楼 2008-05-03 20:41 | 今日事 [未注册用户]
好东西啊,在你们的讨论中学到了好多
  回复  引用  查看    
#40楼 2008-05-05 09:11 | Q.Lee.lulu      
向楼主致敬!!辛苦了

还有向
@Klesh Wong
@Renard
两位大大学习,两位的严谨、认真的态度很值得学习!!

  回复  引用    
#41楼 2008-05-25 20:06 | hax [未注册用户]
楼主的错误已经有许多人留言指出了。我只想说一点,这样的文章对于新手绝对是毒药(比如后面留言中的菜鸟们),建议楼主躬身自省一下,在标题或正文前面发一个声明,承认一下这篇文章只是自己学习过程中的一点认知,新手勿读。
  回复  引用  查看    
#42楼 2008-06-14 20:57 | 汉城      
[图解] 你不知道的 JavaScript - “this”
@Klesh Wong的Javascript(2) (RSS) 只发布了2篇
@Renard 可能刚注册或工作太忙吧。。没有发布
博主写的错也好 对也好 毕竟花时间整理出来 我们要感谢gg~!
有些东西单词是从国外引进过来的。。。所以翻译后的原来的概念就变了。。
外来词是不好理解的 特别是对初学者 博主比喻的可能不恰当 但没办法发的办法。。。 给小学生讲一下“类”看看 您讲完了就知道 为什么这么写了
  回复  引用  查看    
#43楼 2008-07-17 13:24 | looping      
恩,不错。
  回复  引用  查看    
#44楼 2008-07-19 17:44 | Cat Chen      
呃……看到“demo函数副本”这个说法,我已经彻底无语了。对于JavaScript这一门语言而言,去看看《JavaScript权威指南》吧。

对于“函数副本”的问题,去了解一下各种语言是怎么编译或者解释执行的吧,我想不通一门语言干什么要设计为允许存在函数副本,因为函数是好像C#的string那样具有不变性的,一个函数一旦在内存中编译或解释出来,你就不可能再去修改它,如果需要你必须动态再创建一个新函数,所以函数一定是引用类型。当然,可能这世界上真的存在一门语言,有一个特定的理由去创造“函数副本”这种我认为纯粹浪费内存的东西,如果有人知道的话可以告诉我。