Javascript 中的类和闭包


Posted in Javascript onJanuary 08, 2010

有人说javascript也是面向对象的,只是它是prototype based,当然这只是概念上的区别,我不想讨论js是不是面向对象的,关键是想说明虽然javascript的类表现得很像其他语言中的类,但是内部的实现机理确不太一致,如果一味的把javascript中的类类比作其他语言中的类,有时候脑子会犯混。

先看一段简单的代码,一般教材上介绍如何新建一个类的时候都是这样的(当然还有更复杂的方法,不过本质上是一样的):

function MyClass(x) { 
this.x = x; 
} 
var obj = new MyClass('Hello class'); 
alert(obj.x);

毫无疑问,此时obj具有一个x属性,现在的值是 Hello class. 但是,obj到底是什么?MyClass仅仅是一个函数而已,我们称之为构造函数。在其他OO的语言中,构造函数是要放在class关键字内部的,也就是先要声明一个类。另外,函数体内的this又是什么?其他OO语言中,this的概念是很明确的,就是当前对象,因为它在构造函数执行之前已经声明了类,类的内部的一些字段都是已经定义好的。

先解释下,在javascript的函数中,this关键字表示的是调用该函数的作用域(scope),作用域的概念也不是太好理解,下面再解释。不过可以简单的认为它是调用函数的对象。再看MyClass函数,它内部的this是什么呢?

如果我们把代码改成:

var obj = MyClass('Hello class');

这是完全合乎语法的。如果这段代码是在浏览器中运行的,调试一下可以发现,this是window对象。而和obj没有任何关系,obj还是undefined,alert也不会有结果。原来的代码之所以可以工作,都是new关键字的功劳。new关键字把一个普通的函数变成了构造函数。也就是说,MyClass还是一个普通的函数,它之所以能构造出一个obj,基本上是new的功劳。当函数之前有new关键字的时候,javascript会创造一个匿名对象,并且把当前函数的作用域设置为这个匿名对象。然后在那个函数内部引用this的话就是引用的这个匿名对象,最后,即使这个函数没有return,它也会把这个匿名对象返回出去。那么obj自然就具有了x属性。

现在这个MyClass已经有点像一个类了。但是,这并不是new的工作的全部。Javascript同样可以方便的实现继承——依靠是prototype.prototype也是一个对象,毕竟除了原始类型,所有的东西都是对象,包括函数。更为重要的是,前面提到javascript是prototype based,它的含义就是在javascript中没有类的概念,类是不存在的,一个函数,它之所以表现的像类,就是靠的prototype. prototype可以有各种属性,也包括函数。上一段说的new在构造对象的过程中,在最终返回那个匿名对象之前,还会把那个函数的prototype中的属性一一复制给这个对象。这里的复制是复制的引用,而不是新建的一个对象,把内容复制过来,在其内部,相当于保留了一个构造它的函数的prototype的引用。有些教材含糊的说所有的“所有对象都有一个prototype属性”,这种说法是不确切的,虽然它内部确实有一个prototype属性,但是对外是不可见的。只有函数对象是有prototype属性的,函数对象的prototype默认有一个constructor属性。

看如下的代码:

function MyClass(x) { 
this.x = x; 
} 
var proObj = new MyClass('x'); 
InheritClass.prototype = proObj; 
MyClass.prototype.protox = 'xxx'; 
function InheritClass(y) { 
this.y = y; 
} 
var obj = new InheritClass('Hello class'); 
MyClass.prototype.protox = 'changed'; 
proObj.x = 'Changed Too'; 
alert(obj.protox); 
alert(obj.x);

输出的结果是changed和Changed Too。此代码说明了对象内部保留的是构造函数的prototype的引用,要注意的是,proObj中也是保留的它的构造函数的prototype的引用。如果把代码改成:
var obj = new InheritClass('Hello class'); 
proObj.protox = 'I am winner'; 
MyClass.prototype.protox = 'changed'; 
proObj.x = 'Changed Too'; 
alert(obj.protox); 
alert(obj.x);

输出的就是 I am winner 和 Changed Too了。事实上,这些prototype逐层引用,构成了一个prototype链。当读取一个对象的属性的时候,首先寻找自己定义的属性,如果没有,就逐层向内部隐含的prototype属性寻找。但是在写属性的时候,就会把它的引用覆盖掉,是不会影响prototype的值的。
再介绍闭包,首先说明下,这里的闭包(closure)和离散数学中关系的传递闭包中的不是一个概念,我曾以为他们之间有关联,后来仔细想想,似乎并无什么关联,恰好名字一样而已。先看定义:
Closure
A "closure" is an expression (typically a function) that can have free variables together with an environment that binds those variables (that "closes" the expression).
要完全理解闭包需要对Javascript函数的机理有比较透彻的理解,而这个机理有点复杂,并不是三言两语能讲清的,有兴趣的朋友可以看这里 Javascript clousures. 即使是这篇文章,也只是大概讲了下原理。大意就是任何一个函数调用都在一个运行上下文(Execution Context)中执行的,这个上下文中有一个作用域对象,其中包括了这个函数的局部变量、参数等。另外,如果一个函数是一个内部函数,它的作用域中含有它外部函数的作用域。在内部函数遇到一个变量名的时候,它是从内部的作用域找起,不断往外层的作用域找。因此,如果内部函数作为一个对象返回出外部函数的时候,即使外部函数已经执行完毕,但是由于其内部函数仍有引用指向它,内部函数不会被释放,因为内部函数有外部函数的作用域,因此外部函数的局部变量也不会被释放。这就构成了闭包。
Javascript 相关文章推荐
js和jquery对dom节点的操作(创建/追加)
Apr 21 Javascript
js关于精确计算和数值格式化以及直接引js文件
Jan 28 Javascript
使用GruntJS构建Web程序之合并压缩篇
Jun 06 Javascript
js鼠标悬浮出现遮罩层的方法
Jan 28 Javascript
javascript的replace方法结合正则使用实例总结
Jun 16 Javascript
微信小程序 加载 app-service.js 错误解决方法
Oct 12 Javascript
JS检测数组类型的方法小结
Mar 14 Javascript
bootstrap+jQuery实现的动态进度条功能示例
May 25 jQuery
JavaScript字符串检索字符的方法
Jun 23 Javascript
AngularJS 中的数据源的循环输出
Oct 12 Javascript
响应式框架Bootstrap栅格系统的实例
Dec 19 Javascript
Vue项目中使用flow做类型检测的方法
Mar 18 Javascript
Extjs学习笔记之六 面版
Jan 08 #Javascript
jQuery开发者都需要知道的5个小技巧
Jan 08 #Javascript
javascript new一个对象的实质
Jan 07 #Javascript
IE iframe的onload方法分析小结
Jan 07 #Javascript
判断iframe是否加载完成的完美方法
Jan 07 #Javascript
Extjs学习笔记之五 一个小细节renderTo和applyTo的区别
Jan 07 #Javascript
EXT中xtype的含义分析
Jan 07 #Javascript
You might like
PHP读MYSQL中文乱码的解决方法
2006/12/17 PHP
分享十款最出色的PHP安全开发库中文详细介绍
2015/03/22 PHP
php检查页面是否被百度收录
2015/10/28 PHP
详解php中反射的应用
2016/03/15 PHP
PHP计算数组中值的和与乘积的方法(array_sum与array_product函数)
2016/04/01 PHP
Thinkphp结合AJAX长轮询实现PC与APP推送详解
2017/07/31 PHP
JavaScript之appendChild、insertBefore和insertAfter使用说明
2010/12/30 Javascript
可在线编辑网页文字效果代码(单击)
2013/03/02 Javascript
js 判断上传文件大小及格式代码
2013/11/13 Javascript
jquery操作cookie插件分享
2014/01/14 Javascript
一个JavaScript函数把URL参数解析成Json对象
2014/09/24 Javascript
node.js中的buffer.Buffer.byteLength方法使用说明
2014/12/10 Javascript
js实现文本框宽度自适应文本宽度的方法
2015/08/13 Javascript
JS实现超精简的链接列表在固定区域内滚动效果代码
2015/11/04 Javascript
JS实现日期时间动态显示的方法
2015/12/07 Javascript
JS实现iframe自适应高度的方法(兼容IE与FireFox)
2016/06/24 Javascript
AngularJS 单元测试(一)详解
2016/09/21 Javascript
Bootstrap 3.x打印预览背景色与文字显示异常的解决
2016/11/06 Javascript
原生javascript移动端滑动banner效果
2017/03/10 Javascript
几种响应式文字详解
2017/05/19 Javascript
vue使用jsonp抓取qq音乐数据的方法
2018/06/21 Javascript
ionic grid(栅格)九宫格制作详解
2018/06/30 Javascript
解决js相同的正则多次调用test()返回的值却不同的问题
2018/10/10 Javascript
JS计算两个数组的交集、差集、并集、补集(多种实现方式)
2019/05/21 Javascript
vue实现移动端input上传视频、音频
2020/08/18 Javascript
[03:14]DOTA2斧王 英雄基础教程
2013/11/26 DOTA
[15:15]教你分分钟做大人:狙击手
2014/10/30 DOTA
python matplotlib库直方图绘制详解
2019/08/10 Python
Python3使用腾讯云文字识别(腾讯OCR)提取图片中的文字内容实例详解
2020/02/18 Python
python3.8.1+selenium实现登录滑块验证功能
2020/05/22 Python
Python 如何实现访问者模式
2020/07/28 Python
Python 实现图片转字符画的示例(静态图片,gif皆可)
2020/11/05 Python
HTML5操作WebSQL数据库的实例代码
2017/08/26 HTML / CSS
就业推荐自我鉴定
2013/10/06 职场文书
2015年企业新年寄语
2014/12/08 职场文书
2014年教研员工作总结
2014/12/23 职场文书