JavaScript从诞生起就没有被定式化,在成长中不断兼容并取,所以它支持多种不同的编程风格。你可以采用函数式编程风格,也可以采用面向对象的编程风格。或者你完全抛弃函数式编程或面向对象编程,同样能够写出复杂的程序。
最简单的JavaScript程序可能就是一个函数,一个简单的函数可能就是一个表达式,但是这个表达式却能够高效完成复杂的任务。如果说,把JavaScript视为一个可爱的机器猫,随意的玩几个特效,这也无伤大雅,但是,即使是这个机器猫,也足以杀死一头怪兽。这正是JavaScript语言的可爱之处,也是它的儒雅风格。
JavaScript不欺生,也不耍大牌。对于编程的门外汉,简单的学习即可快速上手,即使你不是编程高手,随意耍弄JavaScript,也能够乘风破浪、虎虎生风。为了帮助你更直观地体验JavaScript语言的灵活性,下面我们结合一个简单的示例进行说明,这个示例讲述了如何使用不同的方法设计一个简单的IO接口对象。
1.过程化设计
如果以过程式程序设计的方法来设计一个简单的IO接口,实现的代码如下:
function set(x){ // 传入
this.x = x;
}
function get(){ // 传出
return this.x;
}
这种做法很简单,但你无法有效保存操作的数据和状态。
2.定义原型方法
不过,我们可以为上面两个函数定义一个类,然后把两个函数绑定到类的原型对象上。这样如果需要使用它们,只需要把这个类创建为实例对象,最后再调用对象的方法即可。这样就把函数有机地整合到一个类结构中,避免过程化设计的松散。
// 定义类
var Box = function(){}
// 定义类的原型方法
Box.prototype.set = function(x){ // 传入
this.x = x;
}
Box.prototype.get = function(){ // 传出
return this.x
}
// 创建类的实例对象
var box = new Box(); // 类的对象
box.set(10); // 传入值
var a = box.get(); // 读取值
alert(a); // 返回10
上面示例演示了如何定义名为Box的类,然后又如何为类定义原型方法get()、set()。什么是原型和原型方法,简单地说就是对象的公共方法。
3.封装原型方法
如果你感觉上述程序写法还是很松散,那么我们也可以这样来封装原型方法:
// 定义类
var Box = function(){}
Box.prototype = { // 原型对象
set : function(x){ // 传入
this.x = x;
},
get : function(){ // 传出
return this.x
}
}
这样就与传统的面向对象编程中类结构有点接近了,即把方法都封装在类的结构中。
4.造车不如设计标准
如果你很好奇或者喜欢刺激,那么我们还可以借助JavaScript来设计更惊险的动作。下面示例演示了如何为Function对象定义一个原型方法,自然这个方法将被所有函数继承,包括构造函数类。这个原型方法能够根据传入的名称和函数,把函数封装为指定名称的方法,并把该方法绑定到指定的构造函数(即类)上。
// 定义类
var Box = function(){}
// 为类的祖宗定义一个制造方法的引擎
Box.prototype.constructor.constructor.prototype.make=function(n, f){
this.prototype[n] = f;
}
Box.make("set", function(x){ // 生成传入方法
this.x = x;
})
Box.make("get", function(){ // 生成传出方法
return this.x
})
这个作用域链(Box.prototype.constructor.constructor.prototype)很长,长得几乎能够让人背过气,它是什么玩意?如果我们这样来写,也许你有点感觉,当然你需要一点JavaScript编程背景,即使什么都不会,当做在这里秀一下JavaScript特技,等你学完本书后,再回头看看它,就会豁然开朗。上面示例可以简化为下面的设计效果:
// 为Function对象定义原型方法
// 定义一个制造方法的引擎
Function.prototype.make = function(n, f){
this.prototype[n] = f;
}
// 定义类
var Box = function(){}
// 为类制造方法
Box.make("set", function(x){ // 生成传入方法
this.x = x;
})
Box.make("get", function(){ // 生成传出方法
return this.x
})
这样就明白了,原来Box.prototype.constructor.constructor指向的就是Function对象。上面示例演示了如何为Function对象定义原型方法,该方法有两个参数,第一个参数是一个字符串,它表示为指定对象定义的方法名,第二个参数是一个函数体,它是定义方法的具体内容。
5.方法接龙
继续我们的飞车之旅,如果在原型方法make()中仅添加一行代码return this;,那么就会让整个程序变得惊心动魄,不信你可以继续往下看。
Function.prototype.make = function(n, f){
this.prototype[n] = f;
return this; // 返回对象自身
}
var Box = function(){}
// 连环生成对象的方法set()、get()和add()
Box.make("set", function(x){ // 生成传入方法
this.x = x;
}).make("get", function(){ // 生成传出方法
return this.x
}).make("add", function(){ // 生成参数加倍方法
return this.x + this.x;
})
上面方法能够连环调用的核心就在于在Function对象的原型方法make()中添加了一个返回值this,它代表对象自身,这样就不用再指定调用方法的对象了,所以可以连环调用。
6.实例体验
最后,我们就可以具体应用Box类了。例如,在下面示例中,首先使用new运算符调用构造函数Box(),获得一个实例对象,然后直接调用实例对象的方法即可。
var box = new Box(); // 创建实例对象
box.set(10); // 调用方法set(),传入值
alert(box.get()); // 调用方法get(),获取传入值
alert(box.add()); // 调用方法add(),获取参数值的倍数