javascript面向对象包装类Class封装类库剖析


Posted in Javascript onJanuary 24, 2013

javascript是个入门门槛很低的语言,甚至一个从来没有接触过javascript的技术人员,几小时内就可以写出一个简单有用的程序代码。

但是如果因此你就下结论:javascript是门简单的语言。那你就大错特错了。想写出高性能的代码,同样需要具备一个高级程序员的基本素养。

一个java或者c++程序员,不一定能写出高性能的javascript代码,但更容易写出高性能的javascript代码。
javascript的简单是基于它“胸襟广阔”的包容性。它声明时,不需要指定类型,甚至可以任意的转换类型。它面向对象,却没有类(Class)的限制。它是一门崇尚自由又非常严谨的语言,如果你是一个自由主义者,那么,拥抱javascript吧!

面向对象编程(OOP)是一种流行的编程方法。但javascript的OOP,较之JAVA、c++有很大的同,主要体现它的继承方式不同。javascript是基于原型PROTOTYPE继承的。所有对象都是基于原型链,最终追述到Object对象。

这里不想讨论过多的关于javascript的继承方式和其它语言的继承方式的不同之处。主要讨论如何封装javascript的Class,以便更好的管理和维护基础代码,减少重复代码,以及更好的模块化编程。

下面是几个github上找到的比较好的Class封装类库:
一、MY-CLASS
项目地址:https://github.com/jiem/my-class
先看基本用法:
a、新建一个类

(function(){ 
//新建类 
varPerson=my.Class({ 
//添加静态方法 
STATIC:{ 
AGE_OF_MAJORITY:18 
}, 
//构造函数 
constructor:function(name,age){ 
this.name=name; 
this.age=age; 
}, 
//实例方法 
sayHello:function(){ 
console.log('Hellofrom'+this.name+'!'); 
}, 
//实例方法 
drinkAlcohol:function(){ 
this.age<Person.AGE_OF_MAJORITY? 
console.log('Tooyoung!Drinkmilkinstead!'): 
console.log('Whiskeyorbeer?'); 
} 
}); 
//暴露给命名空间 
myLib.Person=Person; 
})(); 
varjohn=newmyLib.Person('John',16); 
john.sayHello();//log"HellofromJohn!" 
john.drinkAlcohol();//log"Tooyoung!Drinkmilkinstead!"

b、继承一个类
(function(){ 
//Dreamer继承Person 
varDreamer=my.Class(Person,{ 
//构造方法 
constructor:function(name,age,dream){ 
Dreamer.Super.call(this,name,age); 
this.dream=dream; 
}, 
//实例方法 
sayHello:function(){ 
superSayHello.call(this); 
console.log('Idreamof'+this.dream+'!'); 
}, 
//实例方法 
wakeUp:function(){ 
console.log('Wakeup!'); 
} 
}); 
//Super访问父类 
varsuperSayHello=Dreamer.Super.prototype.sayHello; 
//暴露给全局命名空间 
myLib.Dreamer=Dreamer; 
})(); 
varsylvester=newmyLib.Dreamer('Sylvester',30,'eatingTweety'); 
sylvester.sayHello();//log"HellofromSylvester!IdreamofeatingTweety!" 
sylvester.wakeUp();//log"Wakeup!"

c、给类添加新方法
//给myLib.Dreamer添加新方法 
my.extendClass(myLib.Dreamer,{ 
//添加静态方法 
STATIC:{ 
s_dongSomeThing:function(){ 
console.log("dosomething!"); 
} 
}, 
//添加实例方法 
touchTheSky:function(){ 
console.log('Touchingthesky'); 
}, 
//添加实例方法 
reachTheStars:function(){ 
console.log('Sheissopretty!'); 
} 
});

d、实现一个类的方法
//声明一个新类 
myLib.ImaginaryTraveler=my.Class({ 
travel:function(){console.log('Travelingonacarpet!');}, 
crossOceans:function(){console.log('SayinghitoMobyDick!');} 
}); 
(function(){ 
//Dreamer继承Person实现ImaginaryTraveler的方法 
varDreamer=my.Class(Person,ImaginaryTraveler,{ 
//构造方法 
constructor:function(name,age,dream){ 
Dreamer.Super.call(this,name,age); 
this.dream=dream; 
} 
//... 
}); 
//暴露给全局命名空间 
myLib.Dreamer=Dreamer; 
})(); 
varaladdin=newDreamer('Aladdin'); 
aladdininstanceofPerson;//true 
aladdininstanceofImaginaryTraveler;//false 
aladdin.travel(); 
aladdin.wakeUp(); 
aladdin.sayHello();

如果怕忘记new操作符
varPerson=my.Class({ 
//youcannowcalltheconstructorwithorwithoutnew 
constructor:function(name,city){ 
if(!(thisinstanceofPerson)) 
returnnewPerson(name,city); 
this.name=name; 
this.city=citye; 
} 
});

下面看一下my.class的源代码解析:
my.Class实现思路基本是这样的,如果只有一个参数,那么声明的是一个基础类,这个参数是用来声明新类的方法和属以及构造函数。它不是继承而来,但它可以被继承。

继承的思路,就是如果有两个参数,第一个参数做为父类被继承,第二参数用来声明新类的方法和属性以及构造函数,它同样可以被继承。

如果有三个以上参数那么,除出第一个参数做为继承的父类,最后一个参数用声明新类的方法和属性以及构造函数。中间的参数是用类来扩展新类的方法。当然也可以通过my.extendClass扩展新方法。
同时,类库为commonJS和浏览环境都提供了支持!

/*globalsdefine:true,window:true,module:true*/ 
(function(){ 
//Namespaceobject 
varmy={}; 
//保证AMD分模块可用 
if(typeofdefine!=='undefined') 
define([],function(){ 
returnmy; 
}); 
elseif(typeofwindow!=='undefined') 
//保证客户端可用 
window.my=my; 
else 
//保证后台可用 
module.exports=my; 
//============================================================================ 
//@methodmy.Class 
//@paramsbody:Object 
//@paramsSuperClass:function,ImplementClasses:function...,body:Object 
//@returnfunction 
my.Class=function(){ 
varlen=arguments.length; 
varbody=arguments[len-1];//最后一个参数是指定本身的方法 
varSuperClass=len>1?arguments[0]:null;//第一个参数是指继承的方法,实例和静态部分均继承 
varhasImplementClasses=len>2;//如果有第三个参数,那么第二个就是implementClass,这里其实只继承实例对象 
varClass,SuperClassEmpty; 
//保证构造方法 
if(body.constructor===Object){ 
Class=function(){}; 
}else{ 
Class=body.constructor; 
//保证后面不覆盖constructor 
deletebody.constructor; 
} 
//处理superClass部分 
if(SuperClass){ 
//中间件实现实例属性的继承 
SuperClassEmpty=function(){}; 
SuperClassEmpty.prototype=SuperClass.prototype; 
Class.prototype=newSuperClassEmpty();//原型继承,解除引用 
Class.prototype.constructor=Class;//保证constructor 
Class.Super=SuperClass;//父对象访问接口 
//静态方法继承,重载superClass方法 
extend(Class,SuperClass,false); 
} 
//处理ImplementClass部分,其实只继承实例属性部分,除SuperClass#arguments[0]#和body#arguments[length-1]# 
if(hasImplementClasses) 
for(vari=1;i<len-1;i++) 
//implement是继承的实例属性部分,重载父对象implementClass方法 
extend(Class.prototype,arguments[i].prototype,false); 
//处理本身声明body部分,静态要STATIC指定,实例部分要删除STATIC部分 
extendClass(Class,body); 
returnClass; 
}; 
//============================================================================ 
//@methodmy.extendClass 
//@paramsClass:function,extension:Object,?override:boolean=true 
varextendClass=my.extendClass=function(Class,extension,override){ 
//静态部分继承静态部分 
if(extension.STATIC){ 
extend(Class,extension.STATIC,override); 
//保证实例部分不继承静态方法 
deleteextension.STATIC; 
} 
//实例属性继继承实例部 
extend(Class.prototype,extension,override); 
}; 
//============================================================================ 
varextend=function(obj,extension,override){ 
varprop; 
//其实这里的flase是表明,覆盖父对象的方法 
if(override===false){ 
for(propinextension) 
if(!(propinobj)) 
obj[prop]=extension[prop]; 
}else{ 
//这里其实不覆盖父对象的方法,包括toString 
for(propinextension) 
obj[prop]=extension[prop]; 
if(extension.toString!==Object.prototype.toString) 
obj.toString=extension.toString; 
} 
}; 
})();

二、KLASS
项目地址:https://github.com/ded/klass
先看使用方法:
a、新建一个类
//声明一个类 
varPerson=klass(function(name){ 
this.name=name 
}) 
.statics({//静态方法 
head:':)', 
feet:'_|_' 
}) 
.methods({//实例方法 
walk:function(){} 
})

b、继承一个类
//SuperHuman继承Person 
varSuperHuman=Person.extend(function(name){ 
//自动调用父类的构造方法 
}) 
.methods({ 
walk:function(){ 
//显式声明调用父类的walk方法 
this.supr() 
this.fly() 
}, 
fly:function(){} 
}) 
newSuperHuman('Zelda').walk()

c、字面量方式声明一个类
varFoo=klass({ 
foo:0, 
initialize:function(){ 
this.foo=1 
}, 
getFoo:function(){ 
returnthis.foo 
}, 
setFoo:function(x){ 
this.foo=x 
returnthis.getFoo() 
} 
})

d、实现一个类的方法
因为有时候你可能希望覆写或者混合一个实例方法,可以这样:
//可以传递一个字面量去继承 
varAlien=SuperHuman.extend({ 
beam:function(){ 
this.supr() 
//beamintospace 
} 
}) 
varSpazoid=newAlien('Zoopo') 
if(beamIsDown){ 
//覆写beam方法 
Spazoid.implement({ 
beam:function(){ 
this.supr() 
//fallbacktojets 
this.jets() 
} 
}) 
}

下面看一下klass源代码解析
klass的基本设计思路很明确,极力的模仿其它语言的继承方式。比如:子类构造方法调用父类的构造方法,还可以显式的声明调用父类的方法。

这种判断都是基于正则匹配:fnTest=/xyz/.test(function(){xyz;})?/\bsupr\b/:/.*/;关键字"super"
如果显示的声明了要调用父类的方法,那么声明方法的时候,就包装成一个内部调用父类方法且返回相同值的函数,给当前类的方法。

另一方面,构造方法,也是比较灵活的。如果显示的声明了initialize,那么这就是构造方法。否则如果参数是个function那么它就做为构造方法,否则就用父类的构造方法。

通过statics方式添加静态方法,通过实例的implements和静态方法methods添加实例方法。
通过父类的extend实现继承。
同时,类库为commonJS和浏览环境都提供了支持!

/** 
*Klass.js-copyright@dedfat 
*version1.0 
*https://github.com/ded/klass 
*Followoursoftwarehttp://twitter.com/dedfat:) 
*MITLicense 
*/ 
!function(context,f){ 
//fnTest用来验证是否可能通过正则找出调用super父类方法的方法 
varfnTest=/xyz/.test(function(){xyz;})?/\bsupr\b/:/.*/, 
noop=function(){}, 
proto='prototype', 
isFn=function(o){ 
returntypeofo===f; 
}; 
//基础类 
functionklass(o){ 
returnextend.call(typeofo==f?o:noop,o,1); 
} 
//包装成一个借用super同名方法的函数 
functionwrap(k,fn,supr){ 
returnfunction(){ 
//缓存原this.super 
vartmp=this.supr; 
//暂把this.super改造成借用super的同名方法above 
//供o里显式的声明(fnTest.text(fn)==true)要借用super的同名方法使用 
this.supr=supr[proto][k]; 
//借用执行并保存返回值 
varret=fn.apply(this,arguments); 
//恢复原this.super 
this.supr=tmp; 
//返回返回值,保证wrap后的返回值跟原来一致 
returnret; 
}; 
} 
//如果o和super有同名方法,且o显式声明借用super的同名方法,就wrap成一个待执行函数供使用 
//如果没有显式的声明借用super的同名方法,或者是o独有的方法,或者不是方法就直接用 
functionprocess(what,o,supr){ 
for(varkino){ 
//如果是非继承方法,按方法注释规则执行,最终都放进what 
if(o.hasOwnProperty(k)){ 
what[k]=typeofo[k]==f 
&&typeofsupr[proto][k]==f 
&&fnTest.test(o[k]) 
?wrap(k,o[k],supr):o[k]; 
} 
} 
} 
//继承方法的实现,fromSub是用来控制是否继承而来,上面的klass里面fromSub是1,表明非继承而来,构造函数不借用super执行 
functionextend(o,fromSub){ 
//noop做为媒介类实现原型继承的解除引用 
noop[proto]=this[proto]; 
varsupr=this, 
prototype=newnoop(),//创建实例对象供原型继承使用,解除引用 
isFunction=typeofo==f, 
_constructor=isFunction?o:this,//如果o是一个构造方法就用,否则由this来决定构造函数 
_methods=isFunction?{}:o,//如果o是一个{...}应该用methods放到fn原型里,如果里面有initialize就是构造函数,如果o是函数就由上面_constructor决定o是构造函数 
fn=function(){//因为kclass借助了kclass,所以最终实际上返回的就是fn,fn其实就新类的构造函数 
//1如果o是{...}就会被methods直接过滤并添加到fn的原型里,如果o里面有initialize,那么fn的原型里就有initialize,那么它就是构造方法 
//2如果o是function,methods什么也添加不到fn的原型里,但是_constructor会接受o当构造函数 
//3如果o是{....},同时里面也没有initialize,那么就是this当构造函数,如果在klass里由call决定,显然构造函数是noop,如果在非基础类里,构造函数就是父类的构造函数 
//由于o不是函数不会自动调用父类的构造函数,只是把父类的构造函数当做当前类的构造函数----这都是由于this的指向决定的 
console.log(this); 
if(this.initialize){ 
this.initialize.apply(this,arguments); 
}else{ 
//调用父类构造方法 
//如上面3,o不是函数,不会调用父类的构造方法 
//基础类无父类,不会调用父类构造方法 
fromSub||isFn(o)&&supr.apply(this,arguments); 
//调用本类构造方法 
//参考上面2,3要么是noop要么是o 
console.log(_constructor==noop); 
_constructor.apply(this,arguments); 
} 
}; 
//构造原型方法的接口 
fn.methods=function(o){ 
process(prototype,o,supr); 
fn[proto]=prototype; 
returnthis; 
}; 
//执行实现新类原型,保证新类的constructor 
fn.methods.call(fn,_methods).prototype.constructor=fn; 
//保证新类可以被继承 
fn.extend=arguments.callee; 
//添加实例方法或者静态方法,statics:静态方法,implement实例方法 
fn[proto].implement=fn.statics=function(o,optFn){ 
//保证o是一个object对象,如果o是一个字符串,那么就是添一个方法的情况,如果o是一个object对象说明是批量添加的 
//因为要从o里面拷贝 
o=typeofo=='string'?(function(){ 
varobj={}; 
obj[o]=optFn; 
returnobj; 
}()):o; 
//添加实例方法或者静态方法,statics:静态方法,implement实例方法 
process(this,o,supr); 
returnthis; 
}; 
returnfn; 
} 
//后台用,nodejs 
if(typeofmodule!=='undefined'&&module.exports){ 
module.exports=klass; 
}else{ 
varold=context.klass; 
//防冲突 
klass.noConflict=function(){ 
context.klass=old; 
returnthis; 
}; 
//前台浏览器用 
//window.kclass=kclass; 
context.klass=klass; 
} 
}(this,'function');

三、还有一种简单实现
实现思路很简单,就是利用ECMAScript5原型式继承Object.create方法,封装成一个方法,如果不支持ECMAScript5的环境,就平移退化到
functionF(){}; 
F.prototype=superCtor.prototype; 
ctor.prototype=newF(); 
ctor.prototype.constructor=ctor;

同样的,除最后一个参数是当前类的方法声明,其它参数均做为继承父类,需要循环继承,但当这里处理的相对比较简单,没涉及到覆盖。你可以自己动手添加。
varClass=(function(){ 
/** 
*Inheritsfunction.(node.js) 
* 
*@paramctorsubclass'sconstructor. 
*@paramsuperctorsuperclass'sconstructor. 
*/ 
varinherits=function(ctor,superCtor){ 
//显式的指定父类 
ctor.super_=superCtor; 
//ECMAScript5原型式继承并解除引用 
if(Object.create){ 
ctor.prototype=Object.create(superCtor.prototype,{ 
constructor:{ 
value:ctor, 
enumerable:false, 
writable:true, 
configurable:true 
} 
}); 
}else{ 
//无Object.create方法的平稳退化 
functionF(){}; 
F.prototype=superCtor.prototype; 
ctor.prototype=newF(); 
ctor.prototype.constructor=ctor; 
} 
}; 
/** 
*Classfunction. 
*/ 
returnfunction(){ 
//最后一个参数是新类方法、属性和构造函数声明 
varsubClazz=arguments[arguments.length-1]||function(){}; 
//initialize是构造函数,否构造函数就是一个空函数 
varfn=subClazz.initialize==null?function(){}:subClazz.initialize; 
//继承除最一个参数以的类,多继承,也可以用作扩展方法 
for(varindex=0;index<arguments.length-1;index++){ 
inherits(fn,arguments[index]); 
} 
//实现新类的方法 
for(varpropinsubClazz){ 
if(prop=="initialize"){ 
continue; 
} 
fn.prototype[prop]=subClazz[prop]; 
} 
returnfn; 
} 
})();

看下面实例:
/** 
*ThedefinitionofCatClass. 
*/ 
varCat=Class({ 
/** 
*Constructor. 
* 
*@paramnameCat'sname 
*/ 
initialize:function(name){ 
this.name=name; 
}, 
/** 
*Eatfunction. 
*/ 
eat:function(){ 
alert(this.name+"iseatingfish."); 
} 
}); 
/** 
*ThedefinitionofBlackCatClass. 
*/ 
varBlackCat=Class(Cat,{ 
/** 
*Constructor. 
* 
*@paramnameCat'sname. 
*@paramageCat'sage. 
*/ 
initialize:function(name,age){ 
//calltheconstructorofsuperclass. 
BlackCat.super_.call(this,name); 
this.age=age; 
}, 
/** 
*Eatfunction. 
*/ 
eat:function(){ 
alert(this.name+"("+this.age+")iseatingdog."); 
} 
}); 
/** 
*ThedefinitionofBlackFatCatClass. 
*/ 
varBlackFatCat=Class(BlackCat,{ 
/** 
*Constructor. 
* 
*@paramnameCat'sname. 
*@paramageCat'sage. 
*@paramweightCat'sweight. 
*/ 
initialize:function(name,age,weight){ 
//calltheconstructorofsuperclass. 
BlackFatCat.super_.call(this,name,age); 
this.weight=weight; 
}, 
/** 
*Eatfunction. 
*/ 
eat:function(){ 
alert(this.name+"("+this.age+")iseatingdog.Myweight:"+this.weight); 
} 
}); 
/** 
*ThedefinitionofDogClass. 
*/ 
varDog=Class({}); 
varcat=newBlackFatCat("John",24,"100kg"); 
cat.eat(); 
//true 
alert(catinstanceofCat); 
//true 
alert(catinstanceofBlackCat); 
//true 
alert(catinstanceofBlackFatCat); 
//true 
alert(cat.constructor===BlackFatCat); 
//false 
alert(catinstanceofDog);

四、mootools类库的Class
源码解析可以看这里:http://www.cnblogs.com/hmking/archive/2011/09/30/2196504.html
看具体用法:
a、新建一个类
varCat=newClass({ 
initialize:function(name){ 
this.name=name; 
} 
}); 
varmyCat=newCat('Micia'); 
alert(myCat.name);//alerts'Micia' 
varCow=newClass({ 
initialize:function(){ 
alert('moooo'); 
} 
});

b、继承的实现
varAnimal=newClass({ 
initialize:function(age){ 
this.age=age; 
} 
}); 
varCat=newClass({ 
Extends:Animal, 
initialize:function(name,age){ 
this.parent(age);//callsinitalizemethodofAnimalclass 
this.name=name; 
} 
}); 
varmyCat=newCat('Micia',20); 
alert(myCat.name);//alerts'Micia'. 
alert(myCat.age);//alerts20.

c、扩充类的实现
varAnimal=newClass({ 
initialize:function(age){ 
this.age=age; 
} 
}); 
varCat=newClass({ 
Implements:Animal, 
setName:function(name){ 
this.name=name 
} 
}); 
varmyAnimal=newCat(20); 
myAnimal.setName('Micia'); 
alert(myAnimal.name);//alerts'Micia'.

五、悟透javascript:语法甘露
先看用法实例
a、创建类
//创建类Person 
varPerson=Class(object,{ 
Create:function(name,age){ 
this.name=name; 
this.age=age; 
}, 
SayHello:function(){ 
alert("Hello,I'm"+this.name+","+this.age+"yearsold."); 
} 
}); 
varBillGates=New(Person,["BillGates",53]); 
BillGates.SayHello();

b、继承类
//Employee继承Person 
varEmployee=Class(Person,{ 
Create:function(name,age,salary){ 
Person.Create.call(this,name,age); 
//调用基类的构造函数 
this.salary=salary; 
}, 
ShowMeTheMoney:function(){ 
alert(this.name+"$"+this.salary); 
} 
}); 
varSteveJobs=New(Employee,["SteveJobs",53,1234]); 
SteveJobs.SayHello(); 
SteveJobs.ShowMeTheMoney();

下面是源码分析:显然,多了一个New方法,创建类和新建类的实例都被巧妙的封装了。形成了一个有意义的整体!还有一点不同的地方,所有的类都基于字面量,而不是基于函数。代码很简短,但其中原理却很丰富也很巧妙,可以细细品味一番!
//创建类的函数,用于声明类及继承关系 
functionClass(aBaseClass,aClassDefine){ 
//创建类的临时函数壳 
functionclass_(){ 
this.Type=aBaseClass; 
//我们给每一个类约定一个Type属性,引用其继承的类 
for(varmemberinaClassDefine) 
this[member]=aClassDefine[member]; 
//复制类的全部定义到当前创建的类 
}; 
class_.prototype=aBaseClass; 
returnnewclass_(); 
}; 
//创建对象的函数,用于任意类的对象创建 
functionNew(aClass,aParams){ 
//创建对象的临时函数壳 
functionnew_(){ 
this.Type=aClass; 
//我们也给每一个对象约定一个Type属性,据此可以访问到对象所属的类 
if(aClass.Create) 
aClass.Create.apply(this,aParams); 
//我们约定所有类的构造函数都叫Create,这和DELPHI比较相似 
}; 
new_.prototype=aClass; 
returnnewnew_(); 
};

由于写的比较笼统,可能有很多地方没有解析到,也可能有不准确的地方,还望指正。
看完上面几种解析,相信息自己也可以写出一个自己的封装类库出来,至于,怎么实现看个人喜好了。但基本的思都是一样的基于原型的继承方式和循环拷贝新方法。

原文来自:穆乙 http://www.cnblogs.com/pigtail/

Javascript 相关文章推荐
Javascript模块化编程(一)AMD规范(规范使用模块)
Jan 17 Javascript
提高jQuery性能优化的技巧
Aug 03 Javascript
vuex实现简易计数器
Oct 27 Javascript
js实现功能比较全面的全选和多选
Mar 02 Javascript
详解Angular 中 ngOnInit 和 constructor 使用场景
Jun 22 Javascript
JS和JQuery实现雪花飘落效果
Nov 30 jQuery
vue 指定组件缓存实例详解
Apr 01 Javascript
浅谈关于JS下大批量异步任务按顺序执行解决方案一点思考
Jan 08 Javascript
Vue-cli3项目引入Typescript的实现方法
Oct 18 Javascript
js实现蒙版效果
Jan 11 Javascript
解决vue 给window添加和移除resize事件遇到的坑
Jul 21 Javascript
如何手写一个简易的 Vuex
Oct 10 Javascript
js iframe跨域访问(同主域/非同主域)分别深入介绍
Jan 24 #Javascript
js中页面的重新加载(当前页面/上级页面)及frame或iframe元素引用介绍
Jan 24 #Javascript
ajax处理php返回json数据的实例代码
Jan 24 #Javascript
一个级联菜单代码学习及removeClass与addClass的应用
Jan 24 #Javascript
js屏蔽鼠标键盘(右键/Ctrl+N/Shift+F10/F11/F5刷新/退格键)
Jan 24 #Javascript
js鼠标点击事件在各个浏览器中的写法及Event对象属性介绍
Jan 24 #Javascript
Js 回车换行处理的办法及replace方法应用
Jan 24 #Javascript
You might like
php判断字符以及字符串的包含方法属性
2008/08/30 PHP
PHP安装memcached扩展笔记
2015/05/28 PHP
Yii视图CGridView列表用法实例分析
2016/07/12 PHP
laravel多条件查询方法(and,or嵌套查询)
2019/10/09 PHP
如何在centos8自定义目录安装php7.3
2019/11/28 PHP
理解Javascript_15_作用域分配与变量访问规则,再送个闭包
2010/10/20 Javascript
javascripit实现密码强度检测代码分享
2013/12/12 Javascript
JS实现很酷的EMAIL地址添加功能实例
2015/02/28 Javascript
Bootstrap布局组件教程之Bootstrap下拉菜单
2016/06/12 Javascript
简单谈谈Vue 模板各类数据绑定
2016/09/25 Javascript
微信小程序 Storage API实例详解
2016/10/02 Javascript
微信小程序 教程之WXSS
2016/10/18 Javascript
jQuery UI仿淘宝搜索下拉列表功能
2017/01/10 Javascript
浅谈jQuery框架Ajax常用选项
2017/07/08 jQuery
用JS实现简单的登录验证功能
2017/07/28 Javascript
Vue使用枚举类型实现HTML下拉框步骤详解
2018/02/05 Javascript
mpvue小程序仿qq左滑置顶删除组件
2018/08/03 Javascript
js中call()和apply()改变指针问题的讲解
2019/01/17 Javascript
vue-router的两种模式的区别
2019/05/30 Javascript
微信小程序嵌入腾讯视频源过程详解
2019/08/08 Javascript
vue中axios的二次封装实例讲解
2019/10/14 Javascript
Vue数字输入框组件示例代码详解
2020/01/15 Javascript
Python对象体系深入分析
2014/10/28 Python
值得收藏,Python 开发中的高级技巧
2018/11/23 Python
对python For 循环的三种遍历方式解析
2019/02/01 Python
python 函数中的参数类型
2020/02/11 Python
Python3 获取文件属性的方式(时间、大小等)
2020/03/12 Python
Omio意大利:全欧洲低价大巴、火车和航班搜索和比价
2017/12/02 全球购物
公司周年庆典邀请函
2014/01/12 职场文书
好邻里事迹材料
2014/01/16 职场文书
父亲八十大寿答谢词
2014/01/23 职场文书
党校毕业心得体会
2014/09/13 职场文书
商铺门面租房协议书
2014/10/21 职场文书
龙猫观后感
2015/06/09 职场文书
2015年数学教研工作总结
2015/07/22 职场文书
如何Tomcat中使用ipv6地址
2022/05/06 Servers