刚刚学习完JavaScript中面向对象关于对象的一些基本知识,现在通过一些简单的案例,来阐述一下我对其的理解。
众所周知,面向对象的特征主要可以概括为:封装性、继承性和多态性。
一 封装性案例(表单生成器)
封装指的是隐藏内部的实现细节,只对外开放操作接口。
封装的优势在于,无论一个对象内部的代码经过了多少次修改,只要不改变接口,就不会影响到使用这个对象时编写的代码。
下面是表单生成器的实现代码:
1 定义表单存储格式
<form method="post">
姓名:<input type="text" name="user">
性别:<input type="radio" name="gender" value="m">男
<input type="radio" name="gender" value="w">女
爱好:<input type="checkbox" name="hobby[]" value="swimming">游泳
<input type="checkbox" name="hobby[]" value="reading">读书
<input type="checkbox" name="hobby[]" value="running">跑步
住址:<select name="area">
<option>--请选择--</option>
<option value="bj">北京</option>
<option value="sh">上海</option>
<option value="sz">深圳</option>
</select>
自我介绍:<textarea name="introduce"></textarea>
<input type="submit" value="提交">
</form>
2 将表单转换成对象
(1)通过对象保存单行文本框
{
tag:'input', //标签名
text:'姓名:', //提示文本
attr:{type:'text',name:'user'}, //标签属性
option:null //选项(无)
(2)通过对象保存单选框
{
tag:'input',
text:'性别:',
attr:{type:'radio',name:'gender'},
option:{m:'男',w:'女'}
//m、v为单选框的value属性值,男、女为提示文本
}
(3)通过对象保存复选框
{
tag:'input',
text:'爱好:',
attr:{type:'checkbox',name:'hobby[]'},
option:{swimming:'游泳',reading:'读书',running:'跑步'}
}
(4)通过对象保存下拉选框
{
tag:'select',
text:'住址:',
attr:{name:'area'},
option:{'':'--请选择--',bj:'北京',sh:'上海',sz:'深圳'}
}
(5)通过对象保存文本域
{
tag:'textarea',
text:'自我介绍:',
attr:{name:'introduce',rows:'5',cols:'50'},
option:null
}
(6)通过对象保存提交按钮
{
tag:'input',
text:'',
attr:{type:'submit',value:'提交'},
option:null
}
3 封装表单生成器
将表单生成器封装成一个构造函数。接下来创建一个FormBuilder.js文件。
(function(window){
var FormBuilder=function(data){
this.date=date;
};
window.FormBuilder=FormBuilder;
})(window);
上述代码中:
a 最外层是一个自调用的匿名函数,在调用时传入的window对象用于控制FormBuilder库的作用范围,通过第5行代码将FormBuilder作为传入对象的属性。
b 由于window对象是全局的,因此当上述代码执行后,就可以直接使用FormBuilder。另一方面,在匿名函数中定义的变量、函数,都不会污染全局作用域,体现了面向对象的封装性。
接下来创建form.html,用于调用FormBuilder生成表单。
<form id="form"></form>
<script src="./FormBuilder.js"></script>
<script>
var elements=[
......//表单项对象
];
var html=new FormBuilder(element).create();
document.getElementById('form').innerHTML=html;
</script>
在上述代码中:
a 第4行定义的elements数组用于保存表单项,大家可以按照前面介绍的格式,将需要生成的表单项对象放入到数组中;
b 第7行通过new FormBuilder()实例化了表单生成器对象,将elements数组通过参数传入,然后调用了create()方法,该方法用于返回HTML生成结果,将在后面的步骤中实现;
c 第8行将生成的HTML结果放到了form表单中;
4 实现表单的自动生成
(1)编写create()方法
在FormBuilder.js中,为构造函数FormBuilder的原型对象添加create()方法。
FormBuilder.propertype.creat=function(){
var html='';
for(var k in this.data){
var item={tag:'',text:'',attr:{},option:null};
for(var n in this.data[k]){
item[n]=this.data[k][n];
}
html+=builder.toHTML(item);
}
return '<table>'+html+'</table>';
};
在上述代码中:
a this.data表示传入的elements数组;
b 第3~9行遍历了这个数组,每次只处理一个表单项;
c 第4-7行代码将传入的对象合并到item对象中,通过这种方式可以提高容错性,即当传入的对象中缺少某个成员时,自动补齐;
d 第8行代码调用了builder对象的toHTML方法,该方法接收item对象,用于该对象转换成HTML表单。
(2)编写builder对象
builder是封装到匿名函数内部的对象,专门用于对每一种表单项进行生成。下面为设计builder对象成员的代码,将功能划分到具体的方法中实现。
var builder={
toHTML:function(obj){},
attr:function(attr){},
item:{
input:function(attr,option){},
select:function (attr,option) {},
textarea:function (attr) {}
}
};
在将功能划分之后,编写toHTML()方法,实现根据表单项的tag属性调用相应的方法执行操作。由于属性部分是公共代码,因此通过attr()方法进行生成。
toHTML:function(obj){
var html=this.item[obj.tag](this.attr(obj.attr),obj.option);
return '<tr><th>'+obj.text+'</th><td>'+html+'</td></tr>';
},
在上述代码中:
a this.item[obj.tag]();
用于根据obj.tag的值来调用item()对象中的方法。例如,当obj.tag的值为input时,就表示builder.item.input()方法;
b item对象是builder对象的一个属性,该对象包含了input()、select()和textarea()3个方法,分别用于生成input、select、textarea表单项;
接下来编写attr()方法,实现将{type:'text',name:'user'}
形式的对象转换成type="text",name="user"
形式的HTML字符串;
attr:function(attr){
var html='';
for(var k in attr){
html+=k+'="'+attr[k]+'"';
}
return html;
},
(3)编写item对象
首先编写item()对象中的input()方法,该方法接收attr和option两个参数,attr表示属性字符串,option用于当input为单选框或复选框时,保存每一项的value属性和文本。该方法执行完成后,返回生成的HTML字符串。代码如下:
input:function(attr,option){
var html='';
if(option===null){
html+='<input '+attr+'>';
}else{
for(var k in option){
html+='<label><input '+attr+'value="'+k+'"'+'>';
html+=option[k]+'</tabel>';
}
}
return html;
},
上述代码中:
a 第3行通过判断option是否为null,来区分单个控件和组合控件;
b 第7-8行代码在生成组合控件时,使用label标签包裹了input标签,这样可以扩大选择范围,当单击提示文本时,相应的表单控件就会被选中;
接下来编写item对象中的select()方法
select:function(attr,option){
var html='';
for(var k in option){
html+='<option value="'+k+'">'+option[k]+'</option>';
}
return '<select '+attr+'>'+html+'</select>';
},
最后编写item对象中的textarea()方法
textarea:function(attr){
return '<textarea '+attr+'></textarea>';
}
5 测试程序
(图中的CSS样式自行设定)
二 继承性案例
继承是指一个对象继承另一个对象的成员,从而在不改变另一个对象的前提下进行扩展,这和Java中的继承是差不多的。
在JavaScript中,String对象就是对所有字符串的抽象,所有字符串都具有toUpperCase()方法,这个方法其实就是继承自String对象。
var str='HelloWorld';
str.toUpperCase(); //输出结果:HELLOWORLD
str.toLowerCase(); //输出结果:helloworld
str.split('l','3'); //输出结果:["He","","oWor"]
str.substr(5,2); //输出结果:Wo
其实,在JavaScript中关于继承有四种实现方式,现在我来一一简单的介绍一下:
1 利用原型对象实现继承
原型对象是JavaScript实现继承的传统方式。如果一个对象中本来没有某个实行或方法,但是可以从另一个对象中获得,就实现了继承。
function Person(name) {
this.name=name;
}
Person.prototype.sayHello=function () {
console.log('你好,我是'+this.name);
}
var p1=new Person('Jim');
var p2=new Person('Tom');
p1.sayHello(); //输出结果:你好,我是Jim
p2.sayHello(); //输出结果:你好,我是Tom
2 替换原型对象实现继承
我们可以将构造函数的原型对象替换成另一个对象A,基于该构造函数创建的对象就会继承新的原型对象。但应用此方法时,应注意代码编写的顺序
function Person() {} //构造函数Person原本有一个原型对象prototype
Person.prototype={ //将构造函数的prototype属性指向一个新的对象
sayHello:function () { //在新的对象中定义一个sayHello()方法用于测试
console.log('你好,我是新对象');
}
}
var p=new Person();
p.sayHello(); //输出结果:你好,我是新对象
需要注意的是,在基于构造函数创建对象时,代码应写在替换原型对象之后。
function Person() {}
Person.prototype.sayHello=function () {
console.log('原来的对象');
}
var p1=new Person();
Person.prototype={
sayHello:function () {
console.log('替换后的对象');
}
}
var p2=new Person();
p1.sayHello(); //输出结果:原来的对象
p2.sayHello(); //输出结果:替换后的对象
3 利用Object.create()实现继承
var obj={
sayHello:function () {
console.log('我是一个带有sayHello方法的对象');
}
};
var newObj=Object.create(obj);
newObj.sayHello(); //输出结果:我是一个带有sayHello方法的对象
newObj.__proto__===obj; //返回结果:true
上述代码实现了将obj对象作为newObj对象的原型,因此newObj对象继承了obj对象的sayHello()方法。
4 混入继承
混入就是将一个对象的成员加入到另一个对象中,实现对象功能的扩展。实现混入继承最简单的方法就是将一个对象的成员赋值给另一个对象。
var o1={};
var o2={name:'Jim'};
o1.name=o2.name; //o1继承o2的name属性
console.log(o1.name); //输出结果:Jim
当对象成员较多时,可以编写一个函数专门实现对象成员的赋值,函数通常命名为mix(混合)或extend(扩展)。
//编写extend函数
function extend(o1,o2) {
for(var k in o2){
o1[k]=o2[k];
}
}
//测试extend函数
var o1={name:'Jim'};
var o2={age:16,gender:'male'};
extend(o1,o2); //将o2的成员添加给o1
console.log(o1.name); //输出结果:Jim
console.log(o1.age); //输出结果:16
混入式继承和原型继承可以组合在一起使用,实现以对象的方式传递参数,或以对象的方式扩展原型对象的成员。
function Person(options) {
//调用前面编写的extend(),将传入的options对象的成员添加到实例对象中
extend(this,options);
}
Person.fn=Person.prototype; //将prototype属性简化为fn方便代码书写
Person.fn.extend=function (obj) {
extend(this,obj); //此处的this相当于Person.prototype
};
Person.fn.extend({
sayHello:function () {
console.log('你好,我是'+(this.name||'无名'));
}
});
var p1=new Person();
var p2=new Person({name:'张三',age:16});
p1.sayHello(); //输出结果:你好,我是无名
p2.sayHello(); //输出结果:你好,我是张三
在上述代码中:
a 第15行在通过Person构造函数创建对象时传入了对象形式的参数,这种传递参数的方式相比传递多个参数更加灵活;
b 第9-13行代码演示了以对象的方式扩展原型对象的成员,当需要为原型对象一次添加多个成员时,只需要将这些成员保存到一个对象中,然后调用extend()方法来继承即可。
由此可见,利用继承一方面可以在保持接口兼容的前提下对功能进行扩展,另一面增强了代码的复用性,为程序的修改和补充提供便利。
三 多态性案例
多态指的是同一个操作作用于不同的对象,会产生不同的执行结果。实际上JavaScript被设计成一种弱类型语言(即一个变量可以存储任意类型的数据),就是多态性的体现。例如,数字、数组、函数都具有toString()方法,当使用不同的对象调用该方法时,执行结果不同,示例代码如下:
var obj=123;
console.log(obj.toString()); //输出结果:123
obj=[1,2,3];
console.log(obj.toString()); //输出结果:1,2,3
obj=function(){};
console.log(obj.toString()); //输出结果:function(){}
在面向对象中,多态性的实现往往离不开继承,这是因为当多个对象继承了同一对象后,就获得了相同的方法,然后根据每个对象的需求来改变同名方法的执行结果。
希望我整理的这些可以帮助到大家