JavaScript 对象、函数和继承


Posted in Javascript onJuly 07, 2009

1、 Javascript中的对象

JavaScript可以说是一个基于对象的编程语言,为什么说是基于对象而不是面向对象,因为JavaScript自身只实现了封装,而没有实现继承和多态。既然他是基于对象的,那么我们就来说说js中的对象。有人说js中所有的都是对象,这句话不完全正确。正确的一方是他强调了对象在js中的重要性,对象在js中无处不在,包括可以构造对象的函数本身也是对象。但是另一方面,js中也有一些简单的数据类型,包括数字、字符串和布尔值、null值和undefined值,而这些不是对象。那为什么这些类型的值不是对象呢,毕竟他们也有方法。那让我们来看一下,JavaScript中对于对象的定义,有两种定义。

(1)JavaScript中的对象是可变的键控集合(keyed collections) (此定义来自老道的那本书的第三章)

(2)JavaScript中的对象是无序(unordered)的属性集合,这些属性可以含有简单的数据类型、对象、函数;保存在一个对象属性中的函数也被称为这个对象的方法。 (来自ECMA-262 的4.3.3)(注:这里所说的属性是可以在js脚本中创建和访问的(我们称之为显性属性),不包括系统为对象自动分配的内部属性)

那为什么那个简单的数据类型不是对象呢,主要是因为这些数据类型的值中拥有的方法是不可变的,而一个对象的属性是应当可以被改变的。

2、 对象中的原型链[[proto]]

JavaScript中的每个对象创建的时候系统都会自动为其分配一个原型属性[[proto]],用来连接到他的原型对象。在JavaScript中就是通过每个对象中的[[proto]]来实现对象的继承关系的。但是对象的[[proto]]属性在JavaScript是不能访问和修改的,他是作为一个内部的属性存在的,而且是在对象被创建的同时由系统自动设定的。

当访问一个对象的某一属性,如果这个属性在此对象中不存在,就在他的[[proto]]所指的原型对象的属性中寻找,如果找到则返回,否则继续沿着[[proto]]链一直找下去,直到[[proto]]的连接为null的时候停止。

3、 函数也是对象

JavaScript中的函数本身就是一个对象(所以我们经常称之为函数对象),而且可以说他是js中最重要的对象。之所以称之为最重要的对象,一方面他可以扮演像其他语言中的函数同样的角色,可以被调用,可以被传入参数;另一方面他还被作为对象的构造器(constructor)来使用,可以结合new操作符来创建对象。

既然函数就是对象,所以必然含有对象拥有的全部性质,包括对象在创建时设定的原型链[[proto]]属性。

让我们来看看函数对象和普通对象有什么区别。我们前面说过,对象就是无序的属性集合,那么函数的属性和普通对象的属性有什么不同呢。根据ECMA-262中的13.2节所述,在函数对象创建时,系统会默认为其创建两个属性[[call]]和[[constructor]],当函数对象被当做一个普通函数调用的时候(例如myFunc()),“()”操作符指明函数对象的[[call]]属性就被执行,当他被当做一个构造器被调用的时候(例如new myConst()),他的[[constructor]]属性就被执行,[[cosntructor]]的执行过程我们将在下一节中介绍。除此之外,当一个函数被创建时,系统会默认的再为其创建一个显示属性prototype,并为其赋值为

this.prototype = {constructor:this}

具体内容可以参加老道的那本书的第五章。这个函数对象的prototype属性也是为了js把函数当做构造器来实现继承是准备的,但是这个属性是可以在js脚本中访问和修改的。在这里要强调的一点是,大家一定要区分对象中的[[proto]]属性和函数对象中的prototype属性,我在刚开始学习的时候就是因为没有很好的区分这两个东西,走了很多的弯路。

4、 对象的创建

在js中有两种创建对象的方法,一种是通过字面量来实现,如

var Person = {

“first_name”:'liang',

‘last_name':'yang'

}

另一种方法是通过构造器来创建

var my = new Person(‘liang','yang');

其实第一种方式的创建过程相当于调用Object构造器来实现,如下。

var Person = new Object();

Person.first_name = ‘liang';

Person.last_name = ‘yang'

所以我们可以把js中所有对象的创建都合并到使用构造器来实现,下面我么来详细说明构造器创建对象的过程:

第一步,先创建一个空的对象(既没有任何属性),并将这个对象的[[proto]]指向这个构造器函数的prototype属性对象

第二步,将这个空的对象作为this指针传给构造器函数并执行

第三步,如果上面的函数返回一个对象,则返回这个对象,否则返回第一步创建的对象

第四步,把函数当做一个类来使用

由上面的步骤我们可以看出,一般来说函数对象的prototype指向的是一个普通对象,而不是一个函数对象,这个普通对象中的属在由此函数构造器创建的对象中也可以访问。由此可以如此设计我们的代码,假设一个函数就可以代表一个类,这个构造器函数生成的对象就是这个类的实例对象,那么实例对象中应有的属性和方法应该放在这个构造器函数的prototype中,这个类的静态方法就可以直接放到这个函数作为对象的属性中,最后这个函数体就是我们平时在面向对象语言中所说的构造函数(在这里我们要区分连个词“构造函数”和“构造器函数”,所谓构造函数是指普通的面向对象语言中的类的构造函数,而构造器函数是指javascript中的一个函数被当做构造器使用)。

在第3节我们说过每个函数的prototype对象中总是含有一个constructor属性,这个属性就是连接到我们的这个函数本身。再加之,有这个函数生成的每个对象的[[proto]]属性都是指向构造器函数的prototype对象,所以通过[[proto]]链,每个由构造器函数生成的对象,都有一个constructor属性,指向生成他的构造器函数,因此我们可以通过这个属性来判断这个对象是有哪个构造器函数生成的。

5、 函数继承(类继承)

说了这么多,终于到了我们可以在javascript中讨论继承的时候了,我们先来考虑一下要实现类的继承我们都要做些什么,假设我们要从superClass继承到子类subClass

为了使得由subClass生成的对象中能够访问superClass生成的对象中的属性,那么可以使subClass.prototype为一个superClass构造函数生成的对象。

subclass.prototye = new superClass();

但是问题来了,根据我们在第4节说的new superClass()不仅复制了superClass.prototype中的所有方法,而且还运行了superClass()这个函数,这个函数起到的作用是类中的构造函数。我们知道应该在子类的构造函数中调用父类的构造函数来实现初始化。为此我们可以创建一个构造函数为空的,但是原型和superClass原型一致的函数,并使subClass.prototype指向这个函数生成的对象。

var F = function() {};

F.prototype = superClass.prototype;

subClass.protptype = new F();

这样我们就可以再不调用构造函数的同时完成属性复制的工作。但是还有一个问题,那就是我们修改了subClass的prototype属性,从而也就删除了其中的constructor属性,这样我们就无法知道他是哪个构造器函数生成的对象了。我们可以再次给他赋值

subClass.prototype.constructor = subClass;

这样复制属性的问题就迎刃而解了。但是新的问题又出现了,在subClass中我们无法知道他的父类是哪个构造器函数,所以就无法在构造函数中调用父类的构造函数,为此我们可以为subClass添加一个属性,指明他的父类

subClass.superClass = superClass.prototype;

这样我么就可以在子类的构造函数中使用subClass.superClass.constructor来访问父类的构造函数了。最后我们把以上的思路写成一个函数

myPro.extend = function (subClass,superClass) {

var F = function() {};

F.prototype = superClass.prototype;

subClass.protptype = new F();

subClass.prototype.constructor = subClass;

subClass.superClass = superClass.prototype;

superClass.prototype.constructor = superClass;

}

Javascript 相关文章推荐
使用jQuery轻松实现Ajax的实例代码
Aug 16 Javascript
ExtJS4 组件化编程,动态加载,面向对象,Direct
May 12 Javascript
关于Javascript与iframe的那些事儿
Jul 04 Javascript
详解Angular的内置过滤器和自定义过滤器【推荐】
Dec 26 Javascript
VUE axios上传图片到七牛的实例代码
Jul 28 Javascript
vue左右侧联动滚动的实现代码
Jun 06 Javascript
Vue.js实现表格渲染的方法
Sep 07 Javascript
JavaScript数据结构与算法之检索算法示例【二分查找法、计算重复次数】
Feb 22 Javascript
Vue开发之封装上传文件组件与用法示例
Apr 25 Javascript
教你如何用Node实现API的转发(某音乐)
Sep 20 Javascript
解决Vue在Tomcat8下部署页面不加载的问题
Nov 12 Javascript
js中!和!!的区别与用法
May 09 Javascript
js 日期转换成中文格式的函数
Jul 07 #Javascript
javascript 面向对象思想 附源码
Jul 07 #Javascript
jquery BS,dialog控件自适应大小
Jul 06 #Javascript
javascript 浏览器判断 绑定事件 arguments 转换数组 数组遍历
Jul 06 #Javascript
javascript 写类方式之十
Jul 05 #Javascript
javascript 写类方式之九
Jul 05 #Javascript
javascript 写类方式之八
Jul 05 #Javascript
You might like
便携利器 — TECSUN PL-365简评
2021/03/02 无线电
在PHP中操作Excel实例代码
2010/04/29 PHP
php学习笔记 php中面向对象三大特性之一[封装性]的应用
2011/06/13 PHP
php生成N个不重复的随机数实例
2013/11/12 PHP
thinkphp循环结构用法实例
2014/11/24 PHP
用PHP代码在网页上生成图片
2015/07/01 PHP
关于document.cookie的使用javascript
2008/04/11 Javascript
Js获取事件对象代码
2010/08/05 Javascript
jQuery中[attribute!=value]选择器用法实例
2014/12/31 Javascript
JavaScript通过元素索引号删除数组中对应元素的方法
2015/03/18 Javascript
JS解决iframe之间通信和自适应高度的问题
2016/08/24 Javascript
JavaScript中${pageContext.request.contextPath}取值问题及解决方案
2016/12/08 Javascript
jquery ajax异步提交表单数据的方法
2017/10/27 jQuery
nodejs实现的简单web服务器功能示例
2018/03/15 NodeJs
脚手架vue-cli工程webpack的作用和特点
2018/09/29 Javascript
浅谈Vuex注入Vue生命周期的过程
2019/05/20 Javascript
python轻松查到删除自己的微信好友
2016/01/10 Python
Python图像灰度变换及图像数组操作
2016/01/27 Python
轻松实现TensorFlow微信跳一跳的AI
2018/01/05 Python
python实现扫描日志关键字的示例
2018/04/28 Python
编码实现字符串转整型的函数
2012/06/02 面试题
NET程序员上机面试题
2015/05/23 面试题
初任培训自我鉴定
2013/10/07 职场文书
大学生党课思想汇报
2013/12/29 职场文书
元旦红领巾广播稿
2014/02/19 职场文书
乡镇交通安全实施方案
2014/03/29 职场文书
团队精神的演讲稿
2014/05/14 职场文书
中学生关于梦想的演讲稿
2014/08/22 职场文书
群众路线领导班子整改方案
2014/10/25 职场文书
庆六一开幕词
2015/01/29 职场文书
2016年清明节红领巾广播稿
2015/12/17 职场文书
2016年学习贯彻十八届五中全会精神心得体会
2016/01/05 职场文书
导游词之蓬莱长岛
2019/12/17 职场文书
JS继承最简单的理解方式
2021/03/31 Javascript
mysql timestamp比较查询遇到的坑及解决
2021/11/27 MySQL
Python使用BeautifulSoup4修改网页内容
2022/05/20 Python