传统的Web开发模式中,JavaScript一直被当作过程化的语言使用,比如表单验证之类。其实,JavaScript提供了完善的机制来实现面向对象的开发思想,这在Ajax技术上,表现得淋漓尽致。
总结下JavaScript面向对象的基础:(结合了《Web 2.0 开发技术详解》这本书,希望对大家有帮助)
1 定义类
用定义函数的方式(即用“function”关键字定义):
function class01 () { //……. }
此处class01是一个函数,也是一个类。作为函数,可以理解为类的构造函数(初始化)。在JavaScript中,函数和类就是一个概念。
BTW:函数声明的方法:
(1)function fun1(…) {…}
(2)var fun1 = function(…) {…}; // 注意无名函数在调用之前,必需先定义
(3)var fun1 = function fun2(…) {…};
(4)var fun1 = new Function(p1, p2, …, pN, body);
2 定义对象
1)使用new操作符。– 适用内部类(如Date类,Array类,String类,RegExp,Math,Error,Object)和用户自定义类。
如:
function class01() { // 类成员的定义 }
var obj = new class01(); // 即所有函数都可以用new来操作。
e.g:
var obj = new Object();
obj.name = "ljlwill"; // 定义name属性
obj.hello = function(…) {….} // 定义obj的hello方法
有个小技巧,以下大家发现没有
var namespace = new Object();
var namespace.class1 = function() {…..}
var obj1 = new namespace.class1();
>> 对象名起到一个命名空间的作用。先用new出一个namespace对象,把namespace对象的方法定义为类的形式,再new出一个新的obj1
2)使用{}大括号创建无类型对象
传统的面向对象语言中,每个对象都会对应到一个类,而JavaScript中的对象,其实就是属性(方法)的一个集合,无严格意义的类的概念。故可用{}的语法创建一个对象。
如:
var user = { name: "ljwill", // 定义了name属性,初始化为“ljlwill
hobby: ['basketball', 'football', 'ping-pong'], // 定义了爱好,类型为数组
// 定义hello方法
hello: function() { alert("Hello, " + this.name); },
…… }
可看到形式,其中属性名和其定义之间用:(冒号)隔开,以,(逗号)结尾。很是紧凑,清晰,减少代码体积。
3 对象的属性和方法的引用
1) 对象名.属性名(方法名) — 即用.符号引用
2) 对象名["属性(方法)名"] — 即用[]符号引用
如:
var arr = new Array(); // 为数组添加一个元素a
arr["push"]("a"); arr.push("a");
// 计算数组长度
var len = arr["length"];
var len = arr.length;
用方括号[]的引用对象属性(方法),类似于数组,体现了一个JavaScript对象就是一组属性(方法)的集合这个性质。适用于不确定具体要引用哪个属性(方法)的场合。 在document的集合对象中,也有类似[]的用法,如果引用某一个“myForm"的表单对象:document.forms["myForm"],当然,也可以写成document.forms.myForm。(注意,这里的forms对象是一个内部对象)
4 对象的属性和方法的操作
如何为一个对象添加,修改或删除其属性和方法呢?
在一些语言中,要修改一个已生成的对象的属性或方法,必须在对应的类修改,重新实例化,再重新编译。JavaScript却可以灵活的动态添加,修改或删除其属性和方法。
如:先创建一个空对象user:var user = new Object();
1)添加属性。
user.name = "jinlie"; // 为其添加name属性
user.age = 25; // 为其添加age属性
user["name"] = "jinle" // 使用[]
user["age"] = "25"
// 可否发现[]方式有个而外特点, 可以使用非标识符字符串作为属性名称,如数字开头或出现,user["007 name"] = "jinlie",如果user.007 name就错误了。
// 这一属性可以很容易实现一个简单的哈希表。在此不详述。 输出试试:alert(user.name);
2)添加方法
user.getName = function() {
// 可添加参数 return this.name;
} // alert(user.getName());
3)修改属性
即用新的属性替换旧的属性
user.name = "hello"; // 测试:alert(user.getName());
4)删除属性和方法
将其置为undefined即可。
user.name = undefined;
user.getName = undefined;
到此,我们可以感受JavaScript的强大的灵活性。
5 prototype原型对象
在JavaScript中,每个函数(function)其实也是一个对象,他们对应的类是“Function”,而每个函数对象都具有一个子对象prototype。所以,prototype表示了该函数的原型,也即是表示一个类的成员的集合。 prototype是一个对象,也可以对其方法进行动态修改:
如:
function class01 { // ….. }
// 对类的prototype对象增加方法
hello class01.prototype.method = function() { alert("Hello Word!"); }
var obj = new class01();
obj.hello(); // 调用hello()方法
看个有趣的例子:
// 为所有的Function函数对象添加method1方法
Function.prototype.method1 = function() {
alert("This is a function.");
}
function func1() { alert("this is func1"); }
func1.method1(); // 执行了alert("This is a function")
func1.method1.method1(); // 执行了alert("This is a function")
func1.method1.method1()是个递归定义,因为method1本身也是一个函数,便具有了函数对象的属性和方法,所以它调用了method1这个函数对象的method1方法。结论:所有对Function类型的方法扩充都具有这样的递归性质。
6 类成员的访问机制
1)公有成员,私有成员
在JavaScript中,并没有特殊的机制(比如用像一些语言中有关键字private,public等)来访问其成员,但可以通过变量的作用域性质来实现这些功能。– 在一函数内部定义的变量称为局部变量,它不可以被函数外边的程序所使用,却可以被其内部定义的嵌套函数所使用。
有思路了吧?如:
function class1() {
var name = 'ljlwill'; // 私有属性
// 私有方法
function privateMethod(…) {
….
}
// 公有方法
this.pubMethod = function(…) {
….
}
}
// 建以对象obj
var obj = new class1();
obj.pubMethod();
注意,prototype定义的类成员却不可以访问其构造体中定义的局部变量(私有成员)。
2)静态成员
它可以通过“class.staticMemberName(类名.静态成员名)”访问。
实现:
function class1() {
….
}
calss1.staticName = "ljlwil";
class1.staticMethod = function(…) { …. }
如果想给所有的对象添加静态成员(比如静态方法):
Function.prototype.staticMethod = function(…) { … }
7 类的继承
继承是OOP中很重要的IDEA。很抱歉,JavaScript没有专门的机制(extends)来实现,但还是可以使用以下方式实现继续
1)拷贝一个类的prototype到另外一个类来实现继承。
如:
function class1() {
…
}
function class2() {
..
}
class2.prototype = class1.prototype; // 这样class2有了其公有类成员的属性或方法
下边的语句是成立的:
var obj = new class2();
obj instanceof class1; // intanceof 操作符判断一个对象是否是某一类的实例。
obj instanceof class2;
注意:在JavaScript中,除了基本数据类型(数字,字符串,布尔等),所有的赋值或函数传参都是引用传递,所以class2的prototype引用了class2的prototype,如果改变class2的prototype,class2的prototype也随之改变。所以,注意要不允许类成员的重新定义,用prototype实现继承,还是蛮好用的。
2)使用发射机制实现正确继承
发射机制指的是程序在运行时能够获取自身的信息。
如:
// 为Object类添加静态方法extend
Object.extend = function(destination, source) {
for(p in source) {
destination[p] = source[p];
}
return destination;
}
// 通过Object类为所有对象添加extend方法
Object.prototype.extend = function(object) {
return Object.extend.apply(this, [this, object]);
}
// 定义class1
function class1() {…)
class1.prototype = {
method1: function(…) {…},
method2: function(…) {….}
}
// 定义class2
function class2() {….}
// 继承
class2.prototype = (new class1()).extend({ // 不要把new class1()换成class1,这样和prototype实现继承一样的错误哦!
// 重新定义method1
method1: function() {
….
}
}
);
// 定义对象
var obj1 = new class1();
var obj2 = new class2();
// 试试吧
obj1.method1();
obj1.method2();
obj2.method1();
obj2.method2();
8 抽象类
JavaScript的抽象类也是没任何机制支持的。但却还是可以实现。
传统的面向对象语言中,抽象类的概念是,具有虚函数的类。而虚函数指的是,只做了一个声明而未实现的方法。在JavaScript中,虚函数可以看作该类中没有定义的方法,但已经通过this指针使用了(在其他语言中,虚函数必须先声明)。
如:
Object.extend = function(destination, source) {
for(p in source) {
destination[p] = source[p];
}
return destination;
}
// 定义一个抽象基类base,无构造函数
function base() {…}
base.prototype = {
initialize: function() {
this.oninit(); // 调用了一个虚方法
}
// 如果要在基类中添加虚方法的一个定义,也可以的:
// oninit: function() {} // 虚方法是一个空方法,由派生类实现
}
function class1() {….}
class1.prototype = (new base()).extend({
// 实现抽象类中的oninit虚方法
oninit: function() {
// 实现
}
});
好,今天就谈到这,JavaScript一个非常灵活的设计语言。学好基础,看看那些类的设计模式,和一些出名的框架,不禁要一下!