您的位置:首页 > Web前端 > JavaScript

基于对象的JavaScript编程

2009-10-28 10:28 399 查看
基于对象的JavaScript编程
-JavaScript Object-Oriented Programming
By-Ryan Frishberg
接触ajax一直不是很深入,然后对于JavaScript的所谓基于对象的理解更是肤浅的很,g了很多中文的文章看着一头雾水,找了一篇2001年老外的文章看了看,我*!讲的既简单又涉及到了精髓,忍不住花了两天翻译了一下,这是第一部分,第二部分抽时间也去翻一下,本人英文水平处于"伦敦郊区"水平,大家多喷。-.-
这个可能让你很震惊,但是JavaScript是一种很强大的基于对象(object-based或者prototype-based,随便你怎么称呼)的编程语言。的确如此,JavaScript是一种强大的编程语言,并不是只能制造图片滚动效果和平淡或华丽的展现效果。可是,很少使用者能认识到JavaScript潜在的强大的功能。如果你恰恰属于这个行列,那这篇文章正好适合你。
首先,JavaScript跟java一样不是一门纯粹的OOP(Object-Oriented-Programming)语言,但是它是一门基于对象的编程语言。但是,你为什么要用JavaScript的对象呢?原因是这样你不仅能熟悉JavaScript的工作原理,而且在编写脚本时,你还可以自己创建JavaScript对象,而不总是像你现在这样只编写一些顺序执行的代码。这些编写的JavaScript对象你也可以在以后重复利用。
我希望这篇文章能够促成那些渴望学习面向对象技术的中级JavaScript工程师形成对JavaScript面向对象世界的持续兴趣,从而成为专家。

在这篇导读里,你能学到:


JavaScript的基本数据类型(JavaScript's primitive data types )


在JavaScript中什么是对象(What an object is in JavaScript )


怎样创建一个普通的对象(How to create custom objects )


构造器是什么(What a constructor is )


对象的基本属性是什么(What an object's prototype property is )
JavaScript的基本数据类型
JavaScript有五种基本数据类型:
Undefined,
Null,
Boolean,
Number,
String。
在这篇导读中,我们会大量的用到后面三种类型
Boolean 类型是一种包含或者true或者false两种值的逻辑实体。例如:
var BooleanValue = true;
Number类型是一种用来描述数的大小的一组数值。例如:
var NumericalValue = 354;
String 类型是一组包含零到多个字符的类型。例如:
var StringValue = "This is a String";
Typeof
typeof 是JavaScript中一个常用的操作符。它能告诉你正在操作的数据的类型。这有意义吗?让我们来看几个例子:
var BooleanValue = true;
var NumericalValue = 354;
var StringValue = "This is a String";
alert(typeof BooleanValue) // 显示 "boolean"
alert(typeof NumericalValue) // 显示 "number"
alert(typeof StringValue) // 显示 "string"
对象
对象是属性的集合。这些属性可以是基本数据类型,其他的对象或者是函数(这里先暂时称为方法,后面会有别的名称)。构造函数(简单说是构造器)是一种用来创建一个对象的方法——这个我们会在后面详细讨论。JavaScript有许多内置的对象,如:Array, Image, 和Date对象。你们很多人都很熟悉怎样使用Image对象来构建华丽的滚动效果。那么,当你使用这些代码:
var Image1 = new Image();
Image1.src = "myDog.gif";
事实上你已经创建了一个Image对象,并且给你自己的 Image对象设置了一个属性值:src属性。Image1是一个新的Image对象;换句话说,它是一个Image类型的实例。使用JavaScript中的‘.’符号,上面代码就取得了你自己的image对象的src属性并设置了它的值。现在,我们来学习怎么创建自己的对象:
function myFunc(){
}

var myObject = new myFunc();
alert(typeof myObject); // 显示 "object"
我们刚刚创建了自己的对象。事实上我们已经构建了一个myFunc对象的实例,myFunc()是一个构造方法;构造方法指出了该对象在被创建时要进行的初始化工作,尽管我们这个构造方法里没有什么初始化的工作(原文:it lays out the blueprint from which objects that are created from it will follow (although, in this case, it doesn't lay out much of a blueprint).)。这样,JavaScript是怎样知道要创建一个myFunc的对象,而不是返回这个方法的返回值的呢?让我们来比较一下上面的例子和下面这个,一般方法更经常的用法:
function myFunc(){
return 5;
}

var myObject = myFunc();
alert(typeof myObject); // 显示 "number"
当前的这个例子里,我们把5赋值给了myObject,这样,这两段脚本的区别在哪呢?答案:new关键字。就是它告诉了JavaScript的编译器遵循myFunc()构造方法中设置的初始化内容创建了一个对象。事实上,当我们创建了一个Image对象,我们也进行了相同的过程,只不过这里用了自己的构造方法而已,而在创建Image对象是用了JavaScript内置的Image()构造方法。
到目前为止,我们已经学习了怎样建一个构造函数,怎样用这个构造函数去构建一个对象实例。在我们的例子里,我们创建了一个myFunc()构造器,并且创建了一个myFunc对象的实例,并把它赋给了变量myObject。
这些都很好很强大,但是什么才是关键呢?是的,就像我们给Image对象添加属性那样,myObject也可以被分配各种各样的属性:
function myFunc(){
}

var myObject = new myFunc();
myObject.StringValue = "This is a String";
alert(myObject.StringValue); // 显示 "This is a String"
瞧,现在我们已经给我们自己的对象添加了一个属性。可是,当我们新建一个myFunc对象的实例时(用构造方法myFunc()),我们也必须要手动的将同样的属性赋值给新的实例,例如:
function myFunc(){
}

var myObject = new myFunc();
myObject.StringValue = "This is a String";
var myObject2 = new myFunc();
alert(myObject2.StringValue); // 显示 "undefined"
那么,我们应该怎么操作才能给所有的myFunc对象实例添加属性呢?在操作方法内部,我们就可以做到。this这个关键字在构造方法内部的时候指向当前正被创建的对象实例。例如:
function myFunc(){
this.StringValue = "This is a String";
}

var myObject = new myFunc();
var myObject2 = new myFunc();
alert(myObject2.StringValue); // 显示 "This is a String"
现在,所有的myFunc对象都会有一个初始值为“this is a String”的字符串型的属性StringValue,同时每个对象实例的属性StringValue都可以拥有自己独特的值。也就是说,我们可以改变任何一个myFunc对象实例的stringValue属性的值,而不影响其他的myFunc实例:
function myFunc(){
this.StringValue = "This is a String";
}

var myObject = new myFunc();
myObject.StringValue = "This is myObject's string";
var myObject2 = new myFunc();
alert(myObject.StringValue); // 显示 "This is myObject's string"
alert(myObject2.StringValue); // 显示 "This is a String"
通过给构造方法添加参数的方法我们也可以达到相同的效果:
function myFunc(StringValue){
this.StringValue = StringValue;
}

var myObject = new myFunc("This is myObject's string");
var myObject2 = new myFunc("This is a String");
alert(myObject.StringValue); // 显示 "This is myObject's string"
alert(myObject2.StringValue); // 显示 "This is a String"
在myFunc()构造方法中,this.StringValue 指向当前创建的对象实例的StringValue属性,而StringValue则指向做为参数传过来的该方法的局部变量。这样,我们就把属性指向了对象实例,那方法有如何呢?(附:In the myFunc() constructor, this.StringValue refers to the property being assigned to the newly created object, while StringValue refers to the function's local variable that was passed as an argument. So, now that we've assigned properties to objects, what about methods?
原文里function和method翻译起来费劲,还是看一下原文吧要是没读懂。-、-)
对象方法
除了属性以外,对象也可以包含方法,对象的方法是一个可以执行的函数。来看看这个例子。下面这个例子中,我们创建一个Circle对象。首先我们要定义几个函数,然后将他们指定给我们的Circle对象。现在我们来定义Circle()构造函数,同时创建一到两个它的具体实例:,
function Circle(radius){
this.radius = radius;
}

var bigCircle = new Circle(100);
var smallCircle = new Circle(2);
然后,我们再定义两个可能用到的函数:
function getArea(){
return (this.radius*this.radius*3.14);
}

function getCircumference(){
var diameter = this.radius*2;
var circumference = diameter*3.14;
return circumference;
}
如果你要进行精确的计算的话,你可以用Math.PI来替代3.14,但这里我们就用pi的近似值来增强例子的可读性。
除了一个内容外这些函数都很简单:this.radius指向什么内容?this这个关键字总是指向当前对象,在这个例子里就是这个Circle对象。所以this.radius就指向了这个Circle对象的radius属性。那么,我们怎样将这两个函数添加到我们的对象中呢?事实上它没有你想象的那么麻烦。我们先来改改我们的Circle()构造器:
function Circle(radius){
this.radius = radius;
this.getArea = getArea;
this.getCircumference = getCircumference;
}
上面的代码将函数getArea和getCircumference添加到我们的Circle对象上,从而他们就成了我们的Circle对象中的方法。我们可以像使用其他函数一样使用对象的方法,但是我们必须通过一个包含它的具体的对象实例来调用它:
alert(bigCircle.getArea()); // 显示 31400
alert(bigCircle.getCircumference()); // 显示 618
alert(smallCircle.getArea()); // 显示 12.56
alert(smallCircle.getCircumference()); // 显示 12.56
保持整洁
如果我们想保持我们的属性和方法在相同的地方-Circle构造方法内部。有很多方法可以做到。先来看一下内部函数。内部函数就是在一个函数内部的函数。这是我们要做的:
function Circle(radius){
function getArea(){
return (this.radius*this.radius*3.14);
}
function getCircumference(){
var diameter = this.radius*2;
var circumference = diameter*3.14;
return circumference;
}
this.radius = radius;
this.getArea = getArea;
this.getCircumference = getCircumference;
}
除了方法的位置发生了变化其他都一样。现在在我们的两个函数内部,因为内部函数能取得包含它的外部函数的局部变量,我们用radius取代了this.radius。这样,它就能取得做为参数传递给外部函数Circle()构造器的局部变量了,这样我们就可以这样简单的使用了:
function Circle(radius){
function getArea(){
return (radius*radius*3.14);
}
function getCircumference(){
var diameter = radius*2;
var circumference = diameter*3.14;
return circumference;
}
this.radius = radius;
this.getArea = getArea;
this.getCircumference = getCircumference;
}
oK,现在让我们来改变一下一个对象的radius的值,然后去的它的面积:
bigCircle.radius=50;
alert(bigCircle.getArea()); // 显示 31400
瞧,先等等!返回值是31400,而不是期望的7850.哪里错了呢?好吧,radius引用了我们赋给构造方法的值,而不是对象的属性的值。这样我们虽然改变了对象的radius的值,但是方法getArea()和getCircumference(),还在使用旧的radius值。因此,我们应该使用指向了当前对象的this.radius,不管属性是在对象初始化的前后进行的改变。
OK,我们已经创建了一个完整的对象构造器(定义了一个对象的函数)。我们再来看看另一种在我们Circle()构造器内部建立方法的途径:
function Circle(radius){
this.radius = radius;
this.getArea = function(){
return (this.radius*this.radius*3.14);
}
this.getCircumference = function(){
var diameter = this.radius*2;
var circumference = diameter*3.14;
return circumference;
}
}

var bigCircle = new Circle(100);
var smallCircle = new Circle(2);

alert(bigCircle.getArea()); // displays 31400
alert(smallCircle.getCircumference()); // displays 12.56
在这里我们邂逅了另一种定义函数的方法,我们可以这样做:
functionName = function([parameters]){
// function body
}
我们可以这样来建参数:
functionName = function(parameter1,parameter2,parameter3){
//function body
}
尽管这种建立函数的方法不是很常用,但是在建立对象时,这却是一个很好用的捷径。这个过程同样避免了同名方法的出现。例如,另一个对象可以拥有一个同名的不同方法,如:getArea() 而不会产生冲突。这种可能是因为这些函数被嵌套在了类构造器里。
对象种类
JavaScript总共有三种对象:原生对象,宿主对象,以及用户自定义对象。
原生对象是JavaScript自己内建的对象,如String,Number,Array,Image,Date,Math,等等。
宿主对象是浏览器支持的对象,如:window,document,forms等等
用户自定义对象就是程序员自己编写的对象了。
在JavaScript中,除了基本数据类型任何一个包含属性和方法的元素都是对象是它的基本思想。我们可以使用JavaScript的内建构造方法(就像我们已经创建的那样)去创建对象。
var Image1 = new Image(50,100);
Image1.src = "myDog.gif";
在这里我们使用了JavaScript自有的Image()构造函数创建了一个具有如下属性值的对象:


width = 50


height = 100


src = "myDog.gif"
JavaScript中也包含了一个基本的Object()构造器,也可以用它来创建一个对象:
var myObj = new Object();
我们可以给一个基本对象添加属性和方法,JavaScript中的所有对象都继承自JavaScript的基本的Object对象。
回顾一下一个基本数据类型的String:
var myString = "This is my string";
alert(myString); // 显示 "This is my string"
alert(typeof myString); // 显示 "string"
同时,我们也可以创建一个String类型的对象,通过这样的构造方法:
var myString = new String("This is my string");
alert(myString); // 显示 "This is my string"
alert(typeof myString); // 显示 "object"
现在我们构建了一个String类型的对象。我们也可以同样创建Number和Boolean类型的对象。但这样做又有什么好处呢?是的,一旦我们这样做了,就可以给这些对象添加独特的属性和方法。一个基本数据类型的实例包含了它的构造函数定义的属性和方法,但是它却不能拥有任何自己独有的属性和方法。例如,一个String基本类型就拥有length这个属性以及substring方法以及其它的通过它的构造函数获得的属性和方法。但是,一个String对象可以含有String()构造函数设置的属性和方法,也可以包含任何自己独有的特性。我们来创建一个Number对象,并给他添加一个方法:
var myNumber = new Number(2);
myNumber.doubleIt = new Function("return this*2");
alert(myNumber.doubleIt()); // 显示: 4
alert(typeof myNumber); // 显示"object"
我们仅仅创建了一个新的Number的对象实例,为它定义了一个方法:doubleIt()。请注意myNumber是一个“类对象”。这是因为对象可以包含自己独有的属性和方法,但是基本数据类型,如String,Boolean,Number,undefined,还有null却不能同样拥有。这是他们之间的区别。
可以看到,在上面的例子中,我们实际上创建了另外一个对象---一个Function对象。然而,这个Function对象是与众不同的。一般情况下,我们创建一个对象时,首先输入new关键字,然后紧跟着对象的构造函数,这样就可以返回一个普通的对象实例。接着我们就可以在生成的对象(通常用变量来保存)上添加属性和方法。可是,因为Function对象同时也是一整块可以调用的代码,JavaScript将它设置的与众不同,并且指出它不仅仅是一个对象(可以随意添加属性和方法),而且是一个可以调用的代码块。这样,当我们输入:
alert(typeof myNumber.doubleIt) // 显示 "function"
就像你希望的那样,显示了“function”而不是“object”。Function()构造函数可以接受一些参数(arguments)。除了最后一个参数成为了这个函数的函数体,其他的参数都成了你的函数的参数(parameters):
var myFunc = new Function("parameter1","parameter2",
"parameter3"," // function body");
现在我们可以传入三个参数来调用这个函数:
myFunc("argument1","argument2","argument3");
函数对象
因为很多原因JavaScript的Function对象变得不同寻常。首先,它包含一整块可调用的代码。再者,一个函数对象还是一个完整的对象—它总是有能力包含独特的属性和方法。建立函数时就自动的建立了一个Function对象:
function myFuncObj(){}
myFuncObj.someVariable = "x";
alert(myFuncObj.someVariable) // 显示 "x"
即使没有使用new关键字,Function()构造函数还是创建了一个对象,一个普通的能包含属性和方法的对象。请注意Function()构造器是一个特殊的构造器—其他的构造器都必须使用new关键字来调用,或者有一个简单数据类型的返回值而不是一个对象。
我们来看一下一个基本数据类型String,和一个String类的比较:
var pimitiveString1 = "This is a primitive string";
var pimitiveString2 = String("This is a primitive string");
var stringObject = new String("This is a String object");

primitiveString1.prop = "This is a property";
primitiveString2.prop = "This is a property";
stringObject.prop = "This is a property";

alert(primitiveString1.prop) // 显示 "undefined"
alert(primitiveString2.prop) // 显示 "undefined"
alert(stringObject.prop) // 显示 "This is a property"

alert(typeof primitiveString1); // 显示 "string"
alert(typeof primitiveString2); // 显示 "string"
alert(typeof stringObject) // 显示 "object"
你可以看到,不适用new关键字,我们无法创建一个对象并将它赋给一个变量,取而代之的是它将一个返回的基本数据类型(String基本类型)赋给了那个变量。你也可以看到primitiveString1 和 primitiveString2都不是对象,这样我们就不能给他们添加属性。当使用typeof关键字时primitiveString1/primitiveString2 和 stringObject 返回了不一样的结果。其他的如Date, Image, Option, 还有其他的类也一样。例如:
var x = Date();
alert(typeof x); // 显示 "string"
无论你怎么创建一个函数(很多方法)你会自动创建一个对象:
var myFuncObj = new Function();
var myFuncObj = Function();
var myFuncObj = function(){}
function myFuncObj(){}
这里我们用了不同的方法创建了Function对象,他们都能够包含一块可以调用的代码,同时也可以包含自己的属性和方法。
小结
继续之前,让我们来看几个关键点:


JavaScript包含五种基本数据类型:Undefined, Null, Boolean, Number, 和 String.


在JavaScript中除了基本数据类型,一切都是对象。


对象是属性的无序集合。属性可以表现为基本数据类型,对象,函数对象(这里一般称为方法)。


总起来说JavaScript包含三种类别的对象,原生对象,宿主对象,以及用户自定义对象(就像我们前面定义的Circle对象)。


对象构造器/构造函数是一个用来创建一个新的对象类型的函数。在这个函数中我们定义了一个对象的基本属性和方法,通常也给他们赋了初始值。


可以使用new关键字调用构造器来创建一个对象的实例。我们既可以使用JavaScript内建的构造函数来创建一个原生对象,也可以使用自己定义的构造函数来创建一个自定义对象。
任何一个对象中都包含一个变量—this,它指向调用方法时候的那个实例对象,也就是当前对象,例如:


myObj.myFunc()


yourObj.myFunc()
第一个例子中,方法myFunc()中的this指向了myObj。而第二个例子中,myFunc()里的this是指向yourObj这个对象的。



原文地址:http://articles.sitepoint.com/article/oriented-programming-1/1
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: