Javascript面向对象研究心得
2014-07-11 14:18
176 查看
这段时间正好公司项目需要,需要修改fullcalendar日历插件,有机会深入插件源码。正好利用这个机会,我也大致学习了以下JS的面向对象编程,感觉收获还是比较多的。
所以写了下面这篇文章希望跟大家探讨探讨JS的面向对象,本人资历尚浅,还望各位大神多多指教。
总述:
现在的发展趋势是,JS越来越面向对象化。而JS本身并未像Java,C#等语言,实现了明显的继承,封装等,我们需要通过JS本身的方式来模拟这些面向对象的方式。
Jquery的一些东西:
1.each方法:
这是在js面向对象编程中十分常用的一种方式,each方法使dom结构循环变得简洁,它可以遍历数组,对象,json等
调用例子:
参数selector可以是dom元素($("input:hidden")),json({one:1,two:2,three:3})等等
当参数是json格式时,传入函数的index就变为了json元素的key
看下jQuery中的each实现(网络摘抄)
上述中,回调函数callback作为参数传入,callback.call(value,i,value)这部分实际上限制了callback传入时只会使用i,value两个参数。
在把callback作为参数的函数中,会定义callback实际的参数列表,外部定义callback时,需遵循此参数列表,最常用的例子就是jqueryAJAX中,调用成功返回的success函数或error函数。
JS高级特性:
1.
===运算符
||,&&运算符
2.函数作为值进行参数传递
jquery的AJAX是函数作为值传递的好例子
js中的函数传递都是按值进行传递的,函数名就是对象的引用,它使得函数可以向任何对象一样作为参数进行传递,因为这一特性JS有了更大的灵活性。
我们可以定义带有函数参数的函数,限定传入函数的参数,而不限定对于参数的实现(callback的原理)
下图为研究fullcalendar插件时的,插件定义了对日历元素的遍历函数,对每个元素的操作则作为callback函数传入,这样我们可以对不同元素操做的遍历进行复用
注意下面这种时尚大气的用法,有点像接口,但更开放
4.JSboolean
在JS编程中,尤其是面向对象编程,经常会向一些判断语句传入一些对象,如(fullcalendar插件中截取):
在JS中,判断语句会将传入值转换成Boolean后判断
转换规则:
JS面向对象:
1.argument对象
在JavaScript的每一个函数里存在一个上下文相关的名为arguments的变量,它的行为类似于一个伪数组,包含了传给函数的所有参数,这个对象存在于每个函数的上下文中。
argument[0]表示函数调用时传入的第一个参数,argument[1]表示第二个参数,由此类推
若修改argument的值,则对应的出入参数也跟着修改。
argument对象在JS模拟函数重载时有很大的用处
2.this
在任何语言中,包括操作系统,上下文这个概念总是不可获取,我们可以把上下文理解成当前程序执行的环境。
在JS中,这个上下文通过this来实现,this总是指向当前工作的上下文对象。在全局环境中,this指向window对象。
在函数调用时,可以通过call和apply方法来改变函数执行的上下文对象,this就会指向新的对象。这在后续的继承中也会提及。
关于this的访问:
在这个例子中,在getNameFunc函数内部取到的this不是object对象的this,而是window全局对象,因为object.getNameFunc()实际返回函数名getNameFunc,这时,调用函数的环境变为了全局环境,this因此变成了window。
JS中,每个函数调用时都会自动取得this和arguments两个特殊变量。
[/code]
3.函数重载
JS与java,c#的面向对象语言不同,函数没有重载的概念,同样的函数名后面的会覆盖前面的(JS函数名就是个引用)。
而JS的重载则可以通过argument对象来实现,如下是从《Javascript面向对象编程》一书中截取。
可以通过argument的length属性判断函数调用时的参数列表,结合typeof类型判断,可以实现函数的重载
4.JS作用域
JS作用域以函数为界限,而没有块约束(for,if,while等),所以在for中定义的var变量实际上在for循环外部依然起作用。
注意:在函数内部未使用var关键字产生的变量,可在全局访问
5.闭包
简单的说,闭包就是内层函数引用外层变量的手段,而外层则无法访问内层的变量,是否闭包是对外层变量的封装,函数是否作为隔离之用?(在函数的生命周期内,外层无法影响变量)
结合《Javascript面向对象编程》和网上找的各种资料,我总结了闭包的以下几种用法。
(1)简单的封装函数,把实现业务的代码和参数分开,避免混乱的代码调用方式。
以下为示例代码(取自:《Javascript面向对象编程》)
(2)匿名自执行函数
在JS编写过程中,经常会在全局存放很多变量,包括很多临时变量,这常常会对其他的函数造成影响,使用闭包可以解决这一问题。
以下例子来自http://blog.csdn.net/sunlylorn/article/details/6534610
[/code]
我们无需去维护函数中的变量,执行结束后内部变量会被释放,而不会影响到全局(避免污染)
(3)设计模式中的单例模式
在single以外是无法访问unique的,并且采用匿名函数,只能初始化一次
[/code]
(4)模拟面向对象类模板
来源:http://blog.csdn.net/sunlylorn/article/details/6534610
[/code]
(5)提供引用变量的创建值,而非其他变化后的值
4.JS中方法访问范围
(1)共有方法
介绍之前先说下prototype的概念。
prototype是所有JS对象共有的构造对象。prototype本身也是对象,可以给其附加方法,属性,prototype添加的方法,属性所有主对象的实例共享。
(2)私有方法
所有函数内部定义的属性,方法,未绑定到this上下文对象的,在函数外部是无法访问到的,例子如下:
[/code]
(3)成员方法(本人类比java,c#等语言,姑且叫他成员方法)
[/code]
(4)通过成员方法访问变量,如上例访问name
(5)静态方法
静态方法类似于java等语言,可以直接通过累访问。
JS中的静态方法,实际可以看做函数对象的属性,而不是通过函数对象创建出的对象的属性。所以无法通过其实例访问函数对象。
5.构造(重要)
在JS中,创建对象有多种方式,相比java等语言比较灵活。
JS中,可以先创建Object的实例,再为其添加属性和方法(不展示例子,比较简单),或通过字面量的方式(大括号包裹属性和方法)。
但是,用上述的基本方式创建对象会产生大量重复代码。以下几种方式是比较推荐的方式。
(1)工厂模式
作为最常用的设计模式之一,在JS中同样可以模拟这种方式。工厂模式用函数封装创建对象的细节,只需要通过接口来创建对象。
[/code]
(2)构造函数
在java,c#等面向对象语言中,这是很常见的。
[/code]
(3)原型Prototype模式
我们创建的每个函数,都有一个Prototype属性,该属性是个指针,指向一个用于包含特定类型所有实例共享属性和方法的对象。
每个构造函数都会有一个原型对象(指向原型对象的指针),而通过构造函数构造出的对象同样有指向相同原型对象的指针,这也是后面能够通过原型链实现继承的基础
(4)静态方法创建
静态方法类似于java等语言,可以直接通过累访问。
JS中的静态方法,实际可以看做函数对象的属性,而不是通过函数对象创建出的对象的属性。所以无法通过其实例访问函数对象。
6.继承(重要)
这里,我只介绍最基本的几种集成方式,一些基于设计模式的扩展,这里就不做赘述了。
(1)原型链法
通过子类的原型对象引用父类的实例对象,该实例对象将引用父类原型对象,从而实现了原型链。
原型链的最上一级一定是Object对象。
原型链搜索机制:读取时,从实例对象开始,顺着原型链一直向上搜索,若找不到属性或方法,则会在最后一步停止。
[/code]
对于原型链法,我有一点疑问,为何子类指向父类原型实例而非原型本身,希望能有高人给与指点
(2)借用构造函数
这是我所见过的最常用的方式,其思想是在子类中调用超类的构造函数,主要通过借助apply和call方法,即用子类的this环境去调用超类的构造函数,实现将超类的属性,方法复制到子类的过程。这在fullcalendar插件中大量使用。
//切换对结果的影响this
[/code]
后续有心的东西还会有所补充
所以写了下面这篇文章希望跟大家探讨探讨JS的面向对象,本人资历尚浅,还望各位大神多多指教。
总述:
现在的发展趋势是,JS越来越面向对象化。而JS本身并未像Java,C#等语言,实现了明显的继承,封装等,我们需要通过JS本身的方式来模拟这些面向对象的方式。
Jquery的一些东西:
1.each方法:
这是在js面向对象编程中十分常用的一种方式,each方法使dom结构循环变得简洁,它可以遍历数组,对象,json等
$(selector).each(function(index,element))或$.each(selector,function(index,element))
调用例子:
vararr1=["aaa","bbb","ccc"]; $.each(arr1,function(i,val){ alert(i);//输出:0,1,2 alert(val);//输出:aaa,bbb,ccc });
参数selector可以是dom元素($("input:hidden")),json({one:1,two:2,three:3})等等
当参数是json格式时,传入函数的index就变为了json元素的key
看下jQuery中的each实现(网络摘抄)
function(object,callback,args){ //该方法有三个参数:进行操作的对象obj,进行操作的函数fn,函数的参数args varname,i=0,length=object.length; if(args){ if(length==undefined){ for(nameinobject){ if(callback.apply(object[name],args)===false){ break; } } }else{ for(;i<length;){ if(callback.apply(object[i++],args)===false){ break; } } } }else{ if(length==undefined){ for(nameinobject){ if(callback.call(object[name],name,object[name])===false){ break; } } }else{ for(varvalue=object[0];i<length&&!==false;value=object[++i]){} /*object[0]取得jQuery对象中的第一个DOM元素,通过for循环, 得到遍历整个jQuery对象中对应的每个DOM元素,通过callback.call(value,i,value); 将callback的this对象指向value对象,并且传递两个参数,i表示索引值,value表示DOM元素; 其中callback是类似于function(index,elem){...}的方法。 所以就得到$("...").each(function(index,elem){...}); */ } } returnobject; } 来源:<atarget=_blankhref="http://www.cnblogs.com/xiaojinhe2/archive/2011/10/12/2208740.html">http://www.cnblogs.com/xiaojinhe2/archive/2011/10/12/2208740.html</a>
上述中,回调函数callback作为参数传入,callback.call(value,i,value)这部分实际上限制了callback传入时只会使用i,value两个参数。
在把callback作为参数的函数中,会定义callback实际的参数列表,外部定义callback时,需遵循此参数列表,最常用的例子就是jqueryAJAX中,调用成功返回的success函数或error函数。
JS高级特性:
1.
===运算符
||,&&运算符
2.函数作为值进行参数传递
jquery的AJAX是函数作为值传递的好例子
js中的函数传递都是按值进行传递的,函数名就是对象的引用,它使得函数可以向任何对象一样作为参数进行传递,因为这一特性JS有了更大的灵活性。
我们可以定义带有函数参数的函数,限定传入函数的参数,而不限定对于参数的实现(callback的原理)
下图为研究fullcalendar插件时的,插件定义了对日历元素的遍历函数,对每个元素的操作则作为callback函数传入,这样我们可以对不同元素操做的遍历进行复用
functionsegmentElementEach(segments,callback){//TODO:useinAgendaView? for(vari=0;i<segments.length;i++){ varsegment=segments[i]; varelement=segment.element; if(element){ callback(segment,element,i); } } }
注意下面这种时尚大气的用法,有点像接口,但更开放
//reporttheelementstotheView,forgeneraldrag/resizeutilities segmentElementEach(segments,function(segment,element){ reportEventElement(segment.event,element); }); //attachmousehandlers //attachHandlers(segments,modifiedEventId); //call`eventAfterRender`callbackforeachevent segmentElementEach(segments,function(segment,element){ trigger('eventAfterRender',segment.event,segment.event,element); });
4.JSboolean
在JS编程中,尤其是面向对象编程,经常会向一些判断语句传入一些对象,如(fullcalendar插件中截取):
//thecontainerwheretheinitialHTMLwillberendered. //If`doAppend`==true,usesatemporarycontainer. varrenderContainer=doAppend?$("<div/>"):finalContainer;
在JS中,判断语句会将传入值转换成Boolean后判断
转换规则:
数据类型 | 转为true的值 | 转为false的值 |
Boolean | true | false |
String | 任何非空字符串 | “”空字符串 |
Number | 任何非零数值 | 0和NaN |
Object | 任何对象 | null |
Undefined | n/a | undefined |
1.argument对象
在JavaScript的每一个函数里存在一个上下文相关的名为arguments的变量,它的行为类似于一个伪数组,包含了传给函数的所有参数,这个对象存在于每个函数的上下文中。
argument[0]表示函数调用时传入的第一个参数,argument[1]表示第二个参数,由此类推
若修改argument的值,则对应的出入参数也跟着修改。
argument对象在JS模拟函数重载时有很大的用处
2.this
在任何语言中,包括操作系统,上下文这个概念总是不可获取,我们可以把上下文理解成当前程序执行的环境。
在JS中,这个上下文通过this来实现,this总是指向当前工作的上下文对象。在全局环境中,this指向window对象。
在函数调用时,可以通过call和apply方法来改变函数执行的上下文对象,this就会指向新的对象。这在后续的继承中也会提及。
关于this的访问:
在这个例子中,在getNameFunc函数内部取到的this不是object对象的this,而是window全局对象,因为object.getNameFunc()实际返回函数名getNameFunc,这时,调用函数的环境变为了全局环境,this因此变成了window。
JS中,每个函数调用时都会自动取得this和arguments两个特殊变量。
[code]<scripttype="text/javascript">
varname="TheWindow";
varobject={
name:"MyObject",
getNameFunc:function(){
returnfunction(){
returnthis.name;
};
}
};
alert(object.getNameFunc()());//"TheWindow"
</script>
[/code]
3.函数重载
JS与java,c#的面向对象语言不同,函数没有重载的概念,同样的函数名后面的会覆盖前面的(JS函数名就是个引用)。
而JS的重载则可以通过argument对象来实现,如下是从《Javascript面向对象编程》一书中截取。
可以通过argument的length属性判断函数调用时的参数列表,结合typeof类型判断,可以实现函数的重载
//一个简单的用来发送消息的函数 functionsendMessage(msg,obj){ //如果同时提供了一个消息和一个对象 if(arguments.length==2) //就将消息发给该对象 obj.handleMsg(msg);
else //否则,刚假定只有消息被提供 //于是显示该消息 alert(msg); }
4.JS作用域
JS作用域以函数为界限,而没有块约束(for,if,while等),所以在for中定义的var变量实际上在for循环外部依然起作用。
注意:在函数内部未使用var关键字产生的变量,可在全局访问
5.闭包
简单的说,闭包就是内层函数引用外层变量的手段,而外层则无法访问内层的变量,是否闭包是对外层变量的封装,函数是否作为隔离之用?(在函数的生命周期内,外层无法影响变量)
结合《Javascript面向对象编程》和网上找的各种资料,我总结了闭包的以下几种用法。
(1)简单的封装函数,把实现业务的代码和参数分开,避免混乱的代码调用方式。
以下为示例代码(取自:《Javascript面向对象编程》)
//用来延迟显示消息的通用函数 functiondelayedAlert(msg,time){ //初始化一个被封套的函数 setTimeout(function(){ //此函数使用了来自封套它的函数的变量msg alert(msg); },time); }其实本人感觉,这好像并没体现出闭包的什么特点,倒反而像是简单的封装
(2)匿名自执行函数
在JS编写过程中,经常会在全局存放很多变量,包括很多临时变量,这常常会对其他的函数造成影响,使用闭包可以解决这一问题。
以下例子来自
[code]vardatamodel={
table:[],
tree:{}
};
(function(dm){
for(vari=0;i<dm.table.rows;i++){
varrow=dm.table.rows[i];
for(varj=0;j<row.cells;i++){
drawCell(i,j);
}
}
//builddm.tree
})(datamodel);
[/code]
我们无需去维护函数中的变量,执行结束后内部变量会被释放,而不会影响到全局(避免污染)
(3)设计模式中的单例模式
在single以外是无法访问unique的,并且采用匿名函数,只能初始化一次
[code]varsingle=(function(){
varunique;
functionConstruct(){
//...生成单例的构造函数的代码
}
unique=newConstuct();
returnunique;
})();
来源:<http://blog.chinaunix.net/uid-26672038-id-3886959.html>
[/code]
(4)模拟面向对象类模板
[code]functionPerson(){
varname="default";
return{
getName:function(){
returnname;
},
setName:function(newName){
name=newName;
}
}
};
varjohn=Person();
print(john.getName());
john.setName("john");
print(john.getName());
varjack=Person();
print(jack.getName());
jack.setName("jack");
print(jack.getName());
运行结果如下:
default
john
default
jack
来源:
[/code]
(5)提供引用变量的创建值,而非其他变化后的值
//id为"main"的一个元素
varobj=document.getElementById("main");
//用来绑定的items数组
varitems=["click","keypress"];
//遍历items中的每一项
for(vari=0;i<items.length;i++){
//用自执行的匿名函数来激发作用域
(function(){
//在些作用域内存储值
varitem=items[i];
//为obj元素绑定函数
obj["on"+item]=function(){
//item引用一个父级的变量,
//该变量在此for循环的上文中已被成功地scoped(?)
alert("Thanksforyour"+item);
};
})();
}
4.JS中方法访问范围
(1)共有方法
介绍之前先说下prototype的概念。
prototype是所有JS对象共有的构造对象。prototype本身也是对象,可以给其附加方法,属性,prototype添加的方法,属性所有主对象的实例共享。
(2)私有方法
所有函数内部定义的属性,方法,未绑定到this上下文对象的,在函数外部是无法访问到的,例子如下:
functionPerson(){
//都是私有的
varname;
functionmove(){
}
}[code]
[/code]
(3)成员方法(本人类比java,c#等语言,姑且叫他成员方法)
[code]functionPerson(name){
varname;
functionmoveUp(){
alert(name+"move");
}
this.moveUp=moveUp;
}
varjack=newPerson("jack");
jack.moveUp();
[/code]
(4)通过成员方法访问变量,如上例访问name
(5)静态方法
静态方法类似于java等语言,可以直接通过累访问。
JS中的静态方法,实际可以看做函数对象的属性,而不是通过函数对象创建出的对象的属性。所以无法通过其实例访问函数对象。
5.构造(重要)
在JS中,创建对象有多种方式,相比java等语言比较灵活。
JS中,可以先创建Object的实例,再为其添加属性和方法(不展示例子,比较简单),或通过字面量的方式(大括号包裹属性和方法)。
但是,用上述的基本方式创建对象会产生大量重复代码。以下几种方式是比较推荐的方式。
(1)工厂模式
作为最常用的设计模式之一,在JS中同样可以模拟这种方式。工厂模式用函数封装创建对象的细节,只需要通过接口来创建对象。
[code]functioncreatePerson(name,age,job){
varo=newObject();
o.name=name;
o.age=age;
o.job=job;
o.sayName=function(){
alert(this.name);
};
returno;
}
varperson1=createPerson("Nicholas",29,"SoftwareEngineer");
varperson2=createPerson("Greg",27,"Doctor");
person1.sayName();//"Nicholas"
person2.sayName();//"Greg"
来源:《Javascript高级程序设计》
[/code]
(2)构造函数
在java,c#等面向对象语言中,这是很常见的。
[code]functionPerson(name){
varname;
functionmoveUp(){
alert(name+"move");
}
this.moveUp=moveUp;
}
varjack=newPerson("jack");
jack.moveUp();
[/code]
(3)原型Prototype模式
我们创建的每个函数,都有一个Prototype属性,该属性是个指针,指向一个用于包含特定类型所有实例共享属性和方法的对象。
每个构造函数都会有一个原型对象(指向原型对象的指针),而通过构造函数构造出的对象同样有指向相同原型对象的指针,这也是后面能够通过原型链实现继承的基础
(4)静态方法创建
静态方法类似于java等语言,可以直接通过累访问。
JS中的静态方法,实际可以看做函数对象的属性,而不是通过函数对象创建出的对象的属性。所以无法通过其实例访问函数对象。
6.继承(重要)
这里,我只介绍最基本的几种集成方式,一些基于设计模式的扩展,这里就不做赘述了。
(1)原型链法
通过子类的原型对象引用父类的实例对象,该实例对象将引用父类原型对象,从而实现了原型链。
原型链的最上一级一定是Object对象。
原型链搜索机制:读取时,从实例对象开始,顺着原型链一直向上搜索,若找不到属性或方法,则会在最后一步停止。
[code]functionSuperType(){
this.property=true;
}
SuperType.prototype.getSuperValue=function(){
returnthis.property;
};
functionSubType(){
this.subproperty=false;
}
//inheritfromSuperType
SubType.prototype=newSuperType();
SubType.prototype.getSubValue=function(){
returnthis.subproperty;
};
varinstance=newSubType();
alert(instance.getSuperValue());//true
[/code]
对于原型链法,我有一点疑问,为何子类指向父类原型实例而非原型本身,希望能有高人给与指点
(2)借用构造函数
这是我所见过的最常用的方式,其思想是在子类中调用超类的构造函数,主要通过借助apply和call方法,即用子类的this环境去调用超类的构造函数,实现将超类的属性,方法复制到子类的过程。这在fullcalendar插件中大量使用。
[code] //以下是fullcalendar插件中穿件日历基础对象时继承事件处理类型EventManager的过程,t是用来存储this变量的,防止
//切换对结果的影响this
//Imports
//-----------------------------------------------------------------------------------
EventManager.call(t,options);
varisFetchNeeded=t.isFetchNeeded;
varfetchEvents=t.fetchEvents;
[/code]
后续有心的东西还会有所补充
相关文章推荐
- [Javascript 高级程序设计]学习心得记录9 js面向对象
- JavaScript里的面向对象心得
- Javascript研究心得总结
- 【JavaScript】$.extend使用心得及源码研究
- Jakarta-Common-BeanUtils研究心得(1)
- 面向对象的JavaScript编程
- WEBTIMER控件研究的心得:丢开书本做一个WebTimer
- Jakarta-Common-BeanUtils研究心得(1)
- Jakarta-Common-BeanUtils研究心得(2)[转载]
- 研究心得------->CPU信息的获得,比较全面的。
- 关于最近研究XmlHttp的一些心得
- 面向对象的JavaScript编程(liuruhong(原作))
- Jakarta-Common-BeanUtils研究心得(2)[转载]
- Jakarta-Common-BeanUtils研究心得
- javascript与PHP结合的一些学习心得
- Microsfot.Web.UI.WebControls.TreeView JavaScript控制方法研究
- WEBTIMER控件研究的心得:WebTimer的启示
- Jakarta-Common-BeanUtils研究心得(1)[转载]
- 面向对象的Javascript语言
- 在Javascript中使用面向对象的编程(翻译)