深入理解javascript原型和闭包(1)——一切都是对象“一切都是对象”这句话的重点在于如何去理解“对象”这个概念。——当然,也不是所有的都是对象,值类型就不是对象。首先咱们还是先看看javascript中一个常用的函数——typeof()。typeof应该算是咱们的老朋友,还有谁没用过它?typeof函数输出的一共有几种类型,在此列出:functionshow(x){console.log(typeof(x));//undefinedconsole.log(typeof(10));//numberconsole.log(typeof('abc'));//stringconsole.log(typeof(true));//booleanconsole.log(typeof(function(){}));//functionconsole.log(typeof([1,'a',true]));//objectconsole.log(typeof({a:10,b:20}));//objectconsole.log(typeof(null));//objectconsole.log(typeof(newNumber(10)));//object}show();以上代码列出了typeof输出的集中类型标识,其中上面的四种(undefined,number,string,boolean)属于简单的值类型,不是对象。剩下的几种情况——函数、数组、对象、null、newNumber(10)都是对象。他们都是引用类型。判断一个变量是不是对象非常简单。值类型的类型判断用typeof,引用类型的类型判断用instanceof。varfn=function(){};console.log(fninstanceofObject);//true好了,上面说了半天对象,各位可能也经常在工作中应对对象,在生活中还得应对活生生的对象。有些个心理不正常或者爱开玩笑的单身人士,还对于系统提示的“找不到对象”耿耿于怀。那么在javascript中的对象,到底该如何定义呢?对象——若干属性的集合。java或者C#中的对象都是new一个class出来的,而且里面有字段、属性、方法,规定的非常严格。但是javascript就比较随意了——数组是对象,函数是对象,对象还是对象。对象里面的一切都是属性,只有属性,没有方法。那么这样方法如何表示呢?——方法也是一种属性。因为它的属性表示为键值对的形式。而且,更加好玩的事,javascript中的对象可以任意的扩展属性,没有class的约束。这个大家应该都知道,就不再强调了。先说个最常见的例子:以上代码中,obj是一个自定义的对象,其中a、b、c就是它的属性,而且在c的属性值还是一个对象,它又有name、year两个属性。这个可能比较好理解,那么函数和数组也可以这样定义属性吗?——当然不行,但是它可以用另一种形式,总之函数/数组之流,只要是对象,它就是属性的集合。以函数为例子:varfn=function(){alert(100);};fn.a=10;fn.b=function(){alert(123);};fn.c={name:王福朋,year:1988};上段代码中,函数就作为对象被赋值了a、b、c三个属性——很明显,这就是属性的集合吗。你问:这个有用吗?回答:可以看看jQuery源码!在jQuery源码中,“jQuery”或者“$”,这个变量其实是一个函数,不信你可以叫咱们的老朋友typeof验证一下。console.log(typeof($));//functionconsole.log($.trim(ABC));验明正身!的确是个函数。那么咱们常用的$.trim()也是个函数,经常用,就不用验了吧!很明显,这就是在$或者jQuery函数上加了一个trim属性,属性值是函数,作用是截取前后空格。javascript与java/C#相比,首先最需要解释的就是弱类型,因为弱类型是最基本的用法,而且最常用,就不打算做一节来讲。其次要解释的就是本文的内容——一切(引用类型)都是对象,对象是属性的集合。最需要了解的就是对象的概念,和java/C#完全不一样。所以,切记切记!最后,有个疑问。在typeof的输出类型中,function和object都是对象,为何却要输出两种答案呢?都叫做object不行吗?——当然不行。具体原因,且听下回分解!深入理解javascript原型和闭包(2)——函数和对象的关系上文(理解javascript原型和作用域系列(1)——一切都是对象)已经提到,函数就是对象的一种,因为通过instanceof函数可以判断。varfn=function(){};console.log(fninstanceofObject);//true对!函数是一种对象,但是函数却不像数组一样——你可以说数组是对象的一种,因为数组就像是对象的一个子集一样。但是函数与对象之间,却不仅仅是一种包含和被包含的关系,函数和对象之间的关系比较复杂,甚至有一点鸡生蛋蛋生鸡的逻辑,咱们这一节就缕一缕。还是先看一个小例子吧。functionFn(){this.name='王福朋';this.year=1988;}varfn1=newFn();上面的这个例子很简单,它能说明:对象可以通过函数来创建。对!也只能说明这一点。但是我要说——对象都是通过函数创建的——有些人可能反驳:不对!因为:varobj={a:10,b:20};vararr=[5,'x',true];但是不好意思,这个——真的——是一种——“快捷方式”,在编程语言中,一般叫做“语法糖”。做“语法糖”做的最好的可谓是微软大哥,它把他们家C#那小子弄的不男不女从的,本想图个人见人爱,谁承想还得到处跟人解释——其实它是个男孩!话归正传——其实以上代码的本质是://varobj={a:10,b:20};//vararr=[5,'x',true];varobj=newObject();obj.a=10;obj.b=20;vararr=newArray();arr[0]=5;arr[1]='x';arr[2]=true;而其中的Object和Array都是函数:console.log(typeof(Object));//functionconsole.log(typeof(Array));//function所以,可以很负责任的说——对象都是通过函数来创建的。现在是不是糊涂了——对象是函数创建的,而函数却又是一种对象——天哪!函数和对象到底是什么关系啊?别着急!揭开这个谜底,还得先去了解一下另一位老朋友——prototype原型。本系列文章不打算动辄几千字的长篇大论,咱们小步快跑,不至于看的太乏味。深入理解javascript原型和闭包(3)——prototype原型既typeof之后的另一位老朋友!prototype也是我们的老朋友,即使不了解的人,也应该都听过它的大名。如果它还是您的新朋友,我估计您也是javascript的新朋友。在咱们的第一节(深入理解javascript原型和闭包(1)——一切都是对象)中说道,函数也是一种对象。他也是属性的集合,你也可以对函数进行自定义属性。不用等咱们去试验,javascript自己就先做了表率,人家就默认的给函数一个属性——prototype。对,每个函数都有一个属性叫做prototype。这个prototype的属性值是一个对象(属性的集合,再次强调!),默认的只有一个叫做constructor的属性,指向这个函数本身。如上图,SuperType是是一个函数,右侧的方框就是它的原型。原型既然作为对象,属性的集合,不可能就只弄个constructor来玩玩,肯定可以自定义的增加许多属性。例如这位Object大哥,人家的prototype里面,就有好几个其他属性。咦,有些方法怎么似曾相似?对!别着急,之后会让你知道他们为何似曾相识。接着往下说,你也可以在自己自定义的方法的prototype中新增自己的属性functionFn(){}Fn.prototype.name='王福朋';Fn.prototype.getYear=function(){return1988;};看到没有,这样就变成了没问题!但是,这样做有何用呢?——解决这个问题,咱们还是先说说jQuery吧。var$div=$('div');$div.attr('myName','王福朋');以上代码中,$('div')返回的是一个对象,对象——被函数创建的。假设创建这一对象的函数是myjQuery。它其实是这样实现的。myjQuery.prototype.attr=function(){//……};$('div')=newmyjQuery();不知道大家有没有看明白。如果用咱们自己的代码来演示,就是这样functionFn(){}Fn.prototype.name='王福朋';Fn.prototype.getYear=function(){return1988;};varfn=newFn();console.log(fn.name);console.log(fn.getYear());即,Fn是一个函数,fn对象是从Fn函数new出来的,这样fn对象就可以调用Fn.prototype中的属性。因为每个对象都有一个隐藏的属性——“__proto__”,这个属性引用了创建这个对象的函数的prototype。即:fn.__proto__===Fn.prototype这里的__proto__成为“隐式原型”,下回继续分解。深入理解javascript原型和闭包(4)——隐式原型注意:本文不是javascript基础教程,如果你没有接触过原型的基本知识,应该先去了解一下,推荐看《javascript高级程序设计(第三版)》第6章:面向对象的程序设计。上节已经提到,每个函数function都有一个prototype,即原型。这里再加一句话——每个对象都有一个__proto__,可成为隐式原型。这个__proto__是一个隐藏的属性,javascript不希望开发者用到这个属性值,有的低版本浏览器甚至不支持这个属性值。所以你在VisualStudio2012这样很高级很智能的编辑器中,都不会有__proto__的智能提示,但是你不用管它,直接写出来就是了。上面截图看来,obj.__proto__和Object.prototype的属性一样!这么巧!答案就是一样。obj这个对象本质上是被Object函数创建的,因此obj.__proto__===Object.prototype。我们可以用一个图来表示。即,每个对象都有一个__proto__属性,指向创建该对象的函数的prototype。那么上图中的“Objectprototype”也是一个对象,它的__proto__指向哪里?好问题!在说明“Objectprototype”之前,先说一下自定义函数的prototype。自定义函数的prototype本质上就是和varobj={}是一样的,都是被Object创建,所以它的__proto__指向的就是Object.prototype。但是Object.prototype确实一个特例——它的__proto__指向的是null,切记切记!还有——函数也是一种对象,函数也有__proto__吗?又一个好问题!——当然有。函数也不是从石头缝里蹦出来的,函数也是被创建出来的。谁创建了函数呢?——Function——注意这个大写的“F”。且看如下代码。以上代码中,第一种方式是比较传统的函数创建方式,第二种是用newFunctoin创建。首先根本不推荐用第二种方式。这里只是向大家演示,函数是被Function创建的。好了,根据上面说的一句话——对象的__proto__指向的是创建它的函数的prototype,就会出现:Object.__proto__===Function.prototype。用一个图来表