浅谈javascript 面向对象编程


Posted in Javascript onOctober 28, 2009

感叹是为了缓解严肃的气氛并引出今天要讲的话题,”javascript面向对象编程”,接下来,我们围绕面向对象的几大关键字:封装,继承,多态,展开。
封装:javascript中创建对象的模式中,个人认为通过闭包才算的上是真正意义上的封装,所以首先我们先来简单介绍一下闭包,看下面这个例子:

<script type="text/javascript"> 
function myInfo(){ 
var name ="老鱼",age =27; 
var myInfo = "my name is" + name + "i am" + age +"years old"; 
function showInfo(){ 
alert(myInfo); 
} 
return showInfo; 
} 
var oldFish = myInfo(); 
oldFish(); 
</script>

是不是很眼熟呢?没错了,这其实就是一个简单的闭包应用了。简单解释一下:上面的函数myInfo中定义的变量,在它的内嵌函数showInfo中是可访问的(这个很好理解),但是当我们把这个内嵌函数的返回引用赋值给一个变量oldFish,这个时候函数showInfo是在myInfo函数体外被调用,但是同样可以访问到定义在函数体内的变量。oh yeah!
总结一下闭包的原理吧:函数是运行在定义他们的作用域中而不是调用他们的作用域中。其实返回一个内嵌函数也是创建闭包最常用的一种方法!
如果觉得上面的解释太抽象的话,那么我们一起重塑上面的函数,看看这样是否层次鲜明一些:
<script type="text/javascript"> 
var ioldFish = function(name,age){ 
var name = name,age = age; 
var myInfo = "my name is" + name + "i am" + age +"years old"; 
return{ 
showInfo:function(){ 
alert(myInfo); 
} 
} 
} 
ioldFish("老鱼",27).showInfo(); 
</script>

上例中的编码风格是ext yui中比较常见的,公私分明,一目了然。通过闭包,我们可以很方便的把一些不希望被外部直接访问到的东西隐藏起来,你要访问函数内定义的变量,只能通过特定的方法才可以访问的到,直接从外部访问是访问不到的,写的挺累,饶了一圈终于转回来了,封装嘛,不就是把不希望被别人看到的东西隐藏起来嘛!哈哈……
上例如果转换成JQ的风格的话,应该如下例所写, 这样的封装模式属于门户大开型模式,里面定义的变量是可以被外部访问到的(下面的例子如果你先实例化一个对象,然后在函数外部访问对象的name或者age属性都是可以读取到的)当然这种模式下我们可以设置一些”潜规则”,让团队开发成员明白哪些变量是私用的,通常我们人为的在私有变量和方法前加下划线”_”,标识警戒讯号!从而实现”封装”!
<script type="text/javascript"> 
var ioldFish = function(name,age){ 
return ioldFish.func.init(name,age); 
}; 
ioldFish.func = ioldFish.prototype ={ 
init:function(name,age){ 
this.name = name; 
this.age = age; 
return this; 
}, 
showInfo:function(){ 
var info = "my name is" + this.name +"i am " +this.age+"years old"; 
alert(info); 
} 
}; 
ioldFish.func.init.prototype = ioldFish.func; 
ioldFish(" 老 鱼",27).showInfo(); 
//var oldFish = new ioldFish("老鱼",27); 
//alert(oldFish.name); 
</script>

可能有人会问,哪种模式好呢?这个怎么说呢?两种方式都有优缺点,结合着用呗!总之一个原则,一定一定不能直接被外部对象访问的东西,就用闭包封装吧。”一定一定”四个字很深奥,不断实践中才能体会真谛!
继承:提到这个的时候,要顺便再补充一句:闭包封装中的一个缺点,不利于子类的派生,所以闭包有风险,封装需谨慎!直观起见,下面例子中创建对象的方式,采用”门户大开型”模式。
在javascript中继承一般分为三种方式:”类式继承”,”原型继承”,”掺元类”。下面简单的介绍一下三类继承方式的原理。
A.类式继承:这个是现在主流框架中常用的继承方式,看下例:
<script type="text/javascript"> 
var Name = function(name){ 
this.name = name; 
}; 
Name.prototype.getName = function(){ 
alert(this.name); 
}; 
var Fish = function(name,age){ 
Name.call(this,name); 
this.age = age; 
}; 
Fish.prototype = new Name(); 
Fish.prototype.constructor = Fish; 
Fish.prototype.showInfo = function(){ 
alert(this.age); 
} 
var ioldFish = new Fish("老鱼",27); 
ioldFish.getName(); 
</script>

上述子类Fish中并没定义getName方法,但是子类Fish的实例对象ioldFish依然调用到了该方法,这是因为子类Fish继承了超类Name中定义的getName方法。解释一下,这里子类Fish的prototype指到了超类的一个实例,在子类Fish中虽然没有申明getName方法,但是根据原型链原理,会向prototype指向的上一级对象中去查找是否有该方法,如果没找到该方法,会一直搜索到最初的原型对象。这其实也就是继承的原理了。这里特别说明一下,Fish.prototype.constructor = Fish;这句,由于默认子类的prototype应该是指向本身的,但是之前把prototype指向到了超类的实例对象,所以在这里要把它设置回来。当然这里可以把相关代码通过一个函数来组织起来,起到伪装extend的作用,看如下代码:
function extend(subClass,superClass){ 
var F = function(){}; 
F.prototype = superClass.prototype; 
subClass.prototype = new F(); 
subClass.prototype.constructor = subClass; 
}

B.原型继承,从内存性能上看优于类式继承。
<script type="text/javascript"> 
function clone(object){ 
var F = function(){}; 
F.prototype = object; 
return new F(); 
}; 
var Name = { 
name:"who's name", 
showInfo:function(){ 
alert(this.name); 
} 
}; 
var Fish = clone(Name); 
//Fish.name = "老鱼"; 
Fish.showInfo(); 
</script>

很明显,原型继承核心就是这个clone函数,同样是原型链的原理,不同的是它直接克隆超类,这样的话子类就继承了超类的所有属性和方法.特别说一下,这类继承并不需要创建构造函数,只需要创建一个对象字变量,定义相应的属性和方法,然后在子类中只需要通过圆点”.”符号来引用属性和方法就可以了.
C.掺元类:把一些常用通用性比较大的方法统一封装在一个函数中,然后通过下面这个函数分派给要用到这些方法的类.还可以针对不同的类,选择性的传递需要的方法。
<script type="text/javascript"> 
function agument(receveClass,giveClass){ 
if(arguments[2]){ 
var len = arguments.length; 
for(i=2;i<len;i++){ 
receveClass.prototype[arguments[i]] = giveClass.prototype[arguments[i]]; 
} 
} 
else{ 
for(method in giveClass.prototype){ 
if(!receveClass.prototype[method]){ 
receveClass.prototype[method] = giveClass.prototype[method]; 
} 
} 
} 
}; 
var Name = function(){}; 
Name.prototype ={ 
sayLike:function(){ 
alert("i like oldfish"); 
}, 
sayLove:function(){ 
alert("i love oldfish"); 
} 
} 
var Fish = function(){}; 
var ioldFish = new Fish(); 
agument(Fish,Name,"sayLove"); 
ioldFish.sayLove(); 
ioldFish.sayLike(); 
</script>

多态:个人觉得这个比较抽象,很难言传,所以下面就从重载和覆盖两个方面来简单阐述一下。
重载:上面这个例子中agument函数初始带了两个参数,但是在后面的调用中,agument(Fish,Name,”sayLove”)同样可以带入任意多个参数,javascript的重载,是在函数中由用户自己通过操作arguments这个属性来实现的。
覆盖:这个很简单,就是子类中定义的方法如果与从超类中继承过来的的方法同名,就覆盖这个方法(这里并不是覆盖超类中的方法,注意一下),这里就不累赘了!
最后重点着墨说一下this和执行上下文,在前面举的封装例子中,this都是表示this所在的类的实例化对象本身,但是并不是千篇一律的,打个比方,通过HTML属性定义的事件处理代码,见如下代码:
<script type="text/javascript"> 
var Name = function(name) { 
this.name = name; 
this.getName = function () { 
alert(this.name); 
} 
}; 
var ioldFish = new Name("老鱼"), 
btn = document.getElementById('btn'); 
btn.onclick = ioldFish.getName; 
//btn.onclick = function(){ioldFish.getName.call(ioldFish)}; 
</script>

上例中点了按钮以后弹出框里并没有显示出实例对象的属性,这是因为this的执行上下文已经改变了,他现在所在的上下文应该是input这个HTML标签,但是该标签又不存在getName这个属性,所以自然无法输出这个属性的属性值了!从这个例子我们不难看出:执行上下文是在执行时才确定的,它随时可以变。
当然你可以去掉上面我注释掉的那段代码,通过call改变this的执行上下文,从而获取getName方法。apply方法同样可以实现改变执行上下文的功能,不过在prototype框架中发现了一个更为优美的实现方法bind。看一下这个方法的实现吧,不得不感叹先人的伟大……
Function.prototype.bind = function(obj) { 
var method = this, 
temp = function() { 
return method.apply(obj, arguments); 
}; 
}

相信如果能看明白的话,您已经可以靠这些知识点,去写一个简单的脚本框架了,多多实践,相信不久的将来就能高手进级了!如果没看明白,也不用着急,面向对象本来就有些抽象,多练习练习,应该OK的了,加油……
这篇先写到这吧,下篇文章可以和大家一起探讨一下,javascript的设计模式,敬请期待。
Javascript 相关文章推荐
jQuery MD5加密实现代码
Mar 15 Javascript
jquery dialog键盘事件代码
Aug 01 Javascript
jquery获取焦点和失去焦点事件代码
Apr 21 Javascript
JS 实现点击a标签的时候让其背景更换
Oct 15 Javascript
原生js实现的贪吃蛇网页版游戏完整实例
May 18 Javascript
AngularJS中的表单简单入门
Jul 28 Javascript
Jquery表单验证失败后不提交的解决方法
Oct 18 Javascript
JavaScript Ajax实现异步通信
Dec 14 Javascript
angularJs中datatable实现代码
Jun 03 Javascript
67 个节约开发时间的前端开发者的工具、库和资源
Sep 12 Javascript
详解单页面路由工程使用微信分享及二次分享解决方案
Feb 22 Javascript
利用JavaScript的Map提升性能的方法详解
Aug 14 Javascript
小议javascript 设计模式 推荐
Oct 28 #Javascript
JavaScript 组件之旅(四):测试 JavaScript 组件
Oct 28 #Javascript
JavaScript 组件之旅(三):用 Ant 构建组件
Oct 28 #Javascript
JavaScript 组件之旅(二)编码实现和算法
Oct 28 #Javascript
JavaScript 组件之旅(一)分析和设计
Oct 28 #Javascript
js 数组实现一个类似ruby的迭代器
Oct 27 #Javascript
jquery 操作单选框,复选框,下拉列表实现代码
Oct 27 #Javascript
You might like
咖啡的植物学知识
2021/03/03 咖啡文化
php设计模式 Singleton(单例模式)
2011/06/26 PHP
PHP中递归的实现实例详解
2017/11/14 PHP
利用JS来控制键盘的上下左右键(示例代码)
2013/12/14 Javascript
JS 在指定数组中随机取出N个不重复的数据
2014/06/10 Javascript
jQuery插件boxScroll实现图片轮播特效
2015/07/14 Javascript
js实现超酷的照片墙展示效果图附源码下载
2015/10/08 Javascript
基于jQuery的Web上传插件Uploadify使用示例
2016/05/19 Javascript
关于Node.js的events.EventEmitter用法介绍
2017/04/01 Javascript
AngularJS常见过滤器用法实例总结
2017/07/06 Javascript
在vue项目中引入highcharts图表的方法(详解)
2018/03/05 Javascript
关于layui导航栏不展示下拉列表的解决方法
2019/09/25 Javascript
javascript-hashchange事件和历史状态管理实例分析
2020/04/18 Javascript
vue+element-ui JYAdmin后台管理系统模板解析
2020/07/28 Javascript
使用JS实现鼠标放上图片进行放大离开实现缩小功能
2021/01/27 Javascript
JavaScript实现手风琴效果
2021/02/18 Javascript
[07:57]DOTA2热力大趴狂欢夜 广州站活动回顾
2013/11/27 DOTA
[01:35]2018完美盛典章节片——共竞
2018/12/17 DOTA
Python连接mssql数据库编码问题解决方法
2015/01/01 Python
python回调函数用法实例分析
2015/05/09 Python
Python时间获取及转换知识汇总
2017/01/11 Python
用python写扫雷游戏实例代码分享
2018/05/27 Python
对python判断是否回文数的实例详解
2019/02/08 Python
Python交互环境下打印和输入函数的实例内容
2020/02/16 Python
Python Http请求json解析库用法解析
2020/11/28 Python
瑞典时尚服装购物网站:Miinto.se
2017/10/30 全球购物
Carter’s官方旗舰店:美国受欢迎的婴童服装品牌
2018/01/21 全球购物
Pretty You London官网:英国拖鞋和睡衣品牌
2019/05/08 全球购物
应届毕业生个人自我评价
2013/09/20 职场文书
酒店门卫岗位职责
2013/12/29 职场文书
清洁工表扬信
2014/01/08 职场文书
写给爸爸的道歉信
2014/01/15 职场文书
医学生求职自荐书
2014/06/12 职场文书
安全保证书
2015/01/16 职场文书
利用Python网络爬虫爬取各大音乐评论的代码
2021/04/13 Python
JavaScript设计模式之原型模式详情
2022/06/21 Javascript