JavaScript的灵活性
JavaScript是目前最流行、应用最广泛的语言之一,它是一种极富表现力的语言,它具有C家族语言所罕见的特性。这种语言允许我们使用各种方式来完成同一个任务或者功能,还允许我们在面向对象编程的过程使用函数式编程中的概念来丰富其实现方式。这种语言允许我们采用多种不同的编程风格进行编程,如简单一些的函数式编程,复杂一些的面向对象编程。所以我们可以在长期的编码过程中,培养专门属于自己的编程风格,下面的例子会体现出JavaScript的灵活性。
下面我们将要实现一个模拟开始播放音乐和停止播放音乐的小功能,代码如下:
1 /************************************************/ 2 //方法一:传统的函数式编码方式 3 function start() { 4 console.log("music start"); 5 } 6 7 function stop() { 8 console.log("music stop"); 9 } 10 11 //页面调用方法一 12 start(); 13 stop(); 14 /************************************************/ 15 //方法二:利用prototype 16 var Player=function() {} //声明function对象 17 18 Player.prototype.start=function(){ //为对象的prototype添加方法 19 console.log("music start by prototype"); 20 } 21 Player.prototype.stop=function(){ 22 console.log("music stop by prototype"); 23 } 24 25 //页面调用方法二 26 Player =new Player(); 27 Player.start(); 28 Player.stop(); 29 /************************************************/ 30 //方法三:prototype进一步封装 31 var ControlPlayer=function(){} 32 33 ControlPlayer.prototype={ 34 start:function () { 35 console.log("music start prototype-start"); 36 }, 37 stop:function(){ 38 console.log("music stop prototype-stop"); 39 } 40 } 41 42 //页面调用方法三 43 var c=new ControlPlayer(); 44 c.start(); 45 c.stop(); 46 /************************************************/ 47 //方法四:通过prototype添加方法,链式调用 48 Function.prototype.method=function(name,fn){ //注意function的大小写,小写会不识别句点 49 this.prototype[name]=fn; 50 return this;//链式调用 51 } 52 53 ControlPlayer.method('start',function(){ 54 console.log('music starting by chain... ...'); 55 }).method('stop',function(){ 56 console.log('music stopping by chain... ...'); 57 }); 58 59 60 //页面调用方法四 61 ControlPlayer.prototype.start(); 62 ControlPlayer.prototype.stop(); 63 64 /************************************************/ 65 //方法五:通过prototype添加方法,非链式调用 66 Function.prototype.method=function(name,fn){ //注意function的大小写,小写会不识别句点 67 this.prototype[name]=fn; 68 //return this;//链式调用 69 } 70 71 ControlPlayer.method('start',function(){ 72 console.log('music starting... ...'); 73 }); 74 75 ControlPlayer.method('stop',function(){ 76 console.log('music stopping... ...'); 77 }); 78 79 //页面调用方法五,同四 80 ControlPlayer.prototype.start(); 81 ControlPlayer.prototype.stop();
方法一很简单,但是无法创建可以保存状态并且具有一些仅仅对其内部状态进行操作的方法的对象;方法二中将两个方法(start、stop)赋给该类的prototype属性;如果希望把类的定义封装在一起,可以采用方法三的做法;方法四中使用Function.prototype.method为该类添加新的方法,它有两个参数,第一个是字符串型的方法名称,第二个是具体的好函数;如果对方法四进行扩展,可以让它返回this,这样就可以进行链式调用,也就是方法五的内容。通过以上的几个例子可以发现,不使用prototype属性定义的对象方法,是静态的,只能用类名进行调用,另外此静态方法中无法使用this关键字来调用对象的其他属性;而使用prototype属性定义的对象方法,是非静态的,只有在实例化之后才能调用,其方法内部可以使用this关键字来调用对象的其他属性;只有function才能被实例化,而Object则不能被实例化,前者可以通过new操作符,而后者可以通过赋值把对象保存在变量中并通过该变量对其内部成员进行访问;如果是对象中的function,需要new一下这个function再使用。
弱类型语言
在Javascript中,声明变量时可以不指定类型,但是这不意味着变量没有类型,变量的类型取决于变量的值。在Javascript中有5中原始类型,分别是Number、String、Boolean、Undefined和Null,此外还有对象类型Object和包含可执行代码的函数类型,前者是一种复合数据类型(数组也是Object类型,它包含着一批有序的值集合)。原始类型按值传递,而其他类型包括复合对象是按引用传递。Javascript中,可以通过赋值来改变数据类型,原始数据类型之间也可以进行转换,toString方法可以把Number和Boolean类型转成字符串,parseFloat和parseInt可以把字符变为数值型,双重非操作可以把Number和String转成Boolean类型。弱类型的变量带来了极大的灵活性,Javascript会根据值进行自动转换。
函数是一等对象
在Javascript中,函数可以存储在变量中,可以作为参数传给其他函数,可以作为返回值从其他函数传出,还可以在运行时进行构造,这些特性带来了极大的灵活性和极强的表达能力,而这些正是构建传统面向对象的框架基础。下面的代码创建了一个匿名函数并赋值给一个变量,代码如下:
1 (function(){ 2 console.log('anonymous function!'); 3 })(); //此处括号表示立即调用,另外如果没有分号,多个匿名函数时会报错 4 5 (function(){ 6 var a=20; 7 var b=10; 8 console.log(a*b); 9 })(); 10 11 (function(a,b){ 12 console.log(a*b); 13 })(12,13);//括号中传入的参数和function中的形参一致,参数由外部传入 14 15 var result =(function(a,b){ 16 return a*b; 17 })(3,3); 18 console.log(result);//result的值是匿名函数的返回值
匿名函数最有趣的用途是用于创建闭包(closure),闭包是一个受到保护的变量空间,由内嵌函数生成。Javascript具有函数级的作用域,这意味着在函数内部定义的变量在函数的外部是不可以访问的。Javascript的作用域是词法性质,这意味着函数是运行在定义它的作用域中,而不是调用它的作用域中。把这两个因素结合起来,就可以通过把变量包裹在匿名函数中而对其加以保护,代码如下:
1 var item; 2 (function(){ 3 var a=12; 4 var b=23; 5 item=function(){ 6 return a*b; 7 }; 8 })(); 9 10 item();
变量a和b定义在匿名函数中,因为函数item定义在这个闭包中,所以它能访问这两个变量,即使是在该闭包结束后。
对象的易变性
在JavaScript中,一切都是对象,除了那几种基本类型,即便是基本类型,在必要的时候也会被自动包装为对象,所有对象都是易变的(mutable),这意味着可以在JavaScript中使用其他语言不允许的技术。例如,为函数添加属性:
1 function displayError(msg) { 2 displayError.numTimesExcuted++;//新增属性 3 alert(msg); 4 } 5 6 displayError.numTimesExcuted=0;
这意味着可以对先前的类或实例化的对象进行修改,代码如下:
1 //定义Person类 2 function Person(name,age){ 3 this.name=name; 4 this.age=age; 5 } 6 //为Person类添加GetName和GetAge方法 7 Person.prototype={ 8 GetName:function(){ 9 return this.name; 10 }, 11 GetAge:function(){ 12 return this.age; 13 } 14 } 15 //新建一个Person类的实例tom 16 var tom=new Person('tom',21); 17 var name=tom.GetName(); 18 console.log(name); 19 20 //为Person类添加Greeting方法 21 Person.prototype.Greeting=function(){ 22 return 'hi,'+this.GetName(); 23 } 24 //新建一个Person类的实例lucy 25 var lucy=new Person('lucy',23); 26 console.log(lucy.Greeting()); 27 //为tom实例添加displayGreeting方法 28 tom.displayGreeting=function(){ 29 alert(this.Greeting()); 30 } 31 tom.displayGreeting();
在这个例子中,Greeting方法是在创建Person类的两个实例(tom、lucy)之后添加的,但是这两个实例依然能够访问到,这是因为prototype的工作机制。对象tom还得到了displayGreeting方法,这是其他实例所没有的。与对象的易变性相关的还有一个内省(introspection)的概念,在运行时检查对象所具有的属性和方法,还可以使用这种方法动态地创建类和执行其方法,这种技术称之为反射(reflection),大多数模仿传统面向对象的特性的技术都是基于对象的易变性和反射。在JavaScript中,任何东西都可以在运行时动态地改变,当然也有不利的一面,因为我们定义一个具有一套方法的类,到最后却不能把保证它依旧完好如初。
JavaScript的继承
JavaScript中的继承并非传统的面向对象的真正的继承,而是基于prototype原型链的继承方式,它可以用来模拟传统的基于类的继承方式,但是性能略有差异,具体的使用还要看需求。
JavaScript中的设计模式
JavaScript的强大的表现力赋予了我们在运用设计模式编写代码时极大的创造性,在JavaScript中使用设计模式主要有以下几个原因:
<1>可维护性:有助于降低模块间的耦合性,使代码重构和换用不同的模块变得容易,使得代码维护更加容易;
<2>沟通方便:设计模式为处理不同类型的对象提供了一套通用的术语,这样编码人员在沟通时只要讲明采用的设计模式即可清晰地表达,而不必涉足更细节层次的东西;
<3>提升性能:某些设计模式会大幅提升应用的性能,减少网络传输,比如享元模式和代理模式,当然也有好多设计模式会降低性能,这个就需要使用者自行把握。
总结
JavaScript的丰富表现力是其力量之源,即使这种弱类型语言没有自己的内置对象,但是我们可以随时根据自己的需求进行扩展,由于其实弱类型语言,所以定义变量时并不需要指定类型。函数是一等对象,并且可以动态创建,因此可以创建闭包;所有的对象都是易变的,可以在运行时修改;可以使用的继承方式有两种,一种是原型链继承、另一种是类式继承,它们各有优缺点。JavaScript的设计模式并非完全有益,需要使用者根据具体的需求进行决策,使用不当甚至会产生负面的效果,过度复杂的架构会把应用程序拖入泥沼,所以要根据我们自己的编码风格选择适合的模式来完成具体的工作。
作者:悠扬的牧笛
博客地址:http://www.cnblogs.com/xhb-bky-blog/p/5866675.html
声明:本博客原创文字只代表本人工作中在某一时间内总结的观点或结论,与本人所在单位没有直接利益关系。非商业,未授权贴子请以现状保留,转载时必须保留此段声明,且在文章页面明显位置给出原文连接。