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中最常看到切最容易迷惑的语法(转)
Oct 29 Javascript
JavaScript新窗口与子窗口传值详解
Feb 11 Javascript
JavaScript中的原型链prototype介绍
Dec 30 Javascript
全面解析Bootstrap手风琴效果
Apr 17 Javascript
gulp-uglify 与gulp.watch()配合使用时报错(重复压缩问题)
Aug 24 Javascript
node.js调用Chrome浏览器打开链接地址的方法
May 17 Javascript
浅谈通过JS拦截 pushState和replaceState事件
Jul 21 Javascript
vue写一个组件
Apr 09 Javascript
利用JS实现一个同Excel表现的智能填充算法
Aug 13 Javascript
在vue中利用全局路由钩子给url统一添加公共参数的例子
Nov 01 Javascript
vue实现购物车的监听
Apr 20 Javascript
javascript实现点击按钮切换轮播图功能
Sep 23 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
基于Snoopy的PHP近似完美获取网站编码的代码
2011/10/23 PHP
PHP易混淆函数的区别及用法汇总
2014/11/22 PHP
phpstorm 正则匹配删除空行、注释行(替换注释行为空行)
2018/01/21 PHP
js focus不起作用的解决方法(主要是因为dom元素是否加载完成)
2010/11/05 Javascript
JQuery+JS实现仿百度搜索结果中关键字变色效果
2011/08/02 Javascript
js改变img标签的src属性在IE下没反应的解决方法
2013/07/23 Javascript
简单谈谈JavaScript的同步与异步
2015/12/31 Javascript
javascript匀速运动实现方法分析
2016/01/08 Javascript
AngularJS ng-repeat数组有重复值的解决方法
2016/10/23 Javascript
Bootstrap学习笔记 轮播(Carousel)插件
2017/03/21 Javascript
ES6新特性之类(Class)和继承(Extends)相关概念与用法分析
2017/05/24 Javascript
利用JS测试目标网站的打开响应速度
2017/12/01 Javascript
vue.js分页中单击页码更换页面内容的方法(配合spring springmvc)
2018/02/10 Javascript
超详细的5个Shell脚本实例分享(值得收藏)
2019/08/15 Javascript
vue3修改link标签默认icon无效问题详解
2019/10/09 Javascript
JavaScript Date对象功能与用法学习记录
2020/04/28 Javascript
vue elementui tree 任意级别拖拽功能代码
2020/08/31 Javascript
编写Python脚本批量下载DesktopNexus壁纸的教程
2015/05/06 Python
python中Matplotlib实现绘制3D图的示例代码
2017/09/04 Python
使用Python读取二进制文件的实例讲解
2018/07/09 Python
python爬虫框架scrapy实现模拟登录操作示例
2018/08/02 Python
浅谈Python爬虫基本套路
2019/03/25 Python
VSCode配合pipenv搞定虚拟环境的实现方法
2020/05/17 Python
pycharm激活方法到2099年(激活流程)
2020/09/22 Python
Python加载数据的5种不同方式(收藏)
2020/11/13 Python
一款恶搞头像特效的制作过程 利用css3和jquery
2014/11/21 HTML / CSS
精美的手工家居和生活用品:Nkuku
2019/11/01 全球购物
Ruby中的保护方法和私有方法与一般面向对象程序设计语言的一样吗
2013/05/01 面试题
幼儿园门卫岗位职责
2014/02/14 职场文书
有创意的广告词
2014/03/18 职场文书
护理见习报告范文
2014/11/03 职场文书
2014年财务科工作总结
2014/11/11 职场文书
国庆放假通知怎么写
2015/07/30 职场文书
生日宴会祝酒词
2015/08/10 职场文书
2016教师国培研修感言
2015/12/08 职场文书
小学四年级班务总结该怎么写?
2019/08/16 职场文书