翻译:有点高级的JavaScript面向对象特征指南快速教程,呵呵
翻译说明:本文是我从Prado框架的《快速入门指南》翻译中摘录出来的,感觉对JavaScript的学习很有帮助,特贴出备忘。
Javascript简介
本指引基于Sergio Pereira的 Quick guide to somewhat advanced JavaScript tour of some OO features。
嘿,我不知道你能那样做
如果你是一个Web开发者而且与我来自同一个地方,你也许在你的Web页面中使用过相当多的Javascript,大部分用来作用户界面(UI)的粘合。
直到现在,我才知道Javascript具有比我过去使用的更多的面向对象能力,只是我没有如我所需的去使用它。当浏览器开始支持更标准的 Javascript和DOM的功能集时,要为客户端上的运行而编写更复杂和多功能的代码就变得可行了。它助长了AJAX现象的产生。
当我们都开始学习它适合编写什么酷的AJAX应用时,我们开始关注到我们过去知道的Javascript不过只是冰山一角而已。我们现在知道 Javascript不仅仅被用于象输入校验等简单的用户界面杂务和其他琐碎任务。现在的客户端代码更高级和分层,更象一个真正的桌面应用或一个客户端- 服务器的富客户端。我们在其中看到了过去仅在服务器端的代码中见到的类库、对象模型、继承关系、模式和许多其他的东西。
突然在我们能讲到的许多方面被放到了比以前更高的位置。要为新的Web编写应用,我们必需提高我们的Javascript水平来跟上它而且要更加熟 练。假如你尝试运用许多现有的javascript库,如:Prototype.js、Scriptaculous、moo.fx、Behaviour、 YUI等,你最终会发现自己要阅读JS代码。或许因为你想要学习它们如何实现,或因为你的好奇,或通常因为那是知道如何使用它的唯一办法,大多数这些库象 是不太注重文档。无论个案会是什么,假如你之前不了解那样的东西,你将要面对一些引起恐慌的外部技术。
本文的目的正好是解说许多我们还不熟悉的构造类型。
JSON (JavaScript Object Notation-Javascript对象表示法)
Javascript对象表示法(JSON),是围绕AJAX主题突然出现的一个新的时髦词。JSON,简单的说就是一种在Javascript中定义对象的方法。让我们马上来看看一个实例并体会它如何简单。
var myPet = { color: 'black', leg_count: 4, communicate: function(repeatCount){
for(i=0;i<repeatCount;i++) alert('Woof!');} };
就让我们加入些许格式让它看起来象我们平常所认识的那样:
var myPet =
{
color: 'black',
legCount: 4,
communicate: function(repeatCount)
{
for(i=0;i<repeatCount;i++)
alert('Woof!');
}
};
在这我们创建了一个带两个属性(color和legCount)和一个方法(communicate)的对象引用。不难发现,该对象的属性和方法用一个逗
号区隔的列表来定义。每个成员通过名称来引入,接着是冒号和定义。对于属性来说很容易,仅仅是属性值。方法通过指派一个匿名函数来创建,我们下面会更好地
说明。在该对象被创建并指派给变量myPet之后,我们可以如下使用它:
alert('my pet is ' + myPet.color);
alert('my pet has ' + myPet.legCount + ' legs');
//if you are a dog, bark three times:
myPet.communicate(3);
You'll see JSON used pretty much everywhere in JS these days, as
arguments to functions, as return values, as server responses (in
strings,) etc.
你想说什么?函数也是一个对象?
对于开发者来说这可能与众不同,但在JS中一个函数也是一个对象。你可以象参数一样把一个函数传送给另一个函数,就象你可以传送一个字符串一样。这些被广泛应用而且非常便利。
看看这个实例。我们将把函数传送到另一个使用它们的函数去。
var myDog =
{
bark: function()
{
alert('Woof!');
}
};
var myCat =
{
meow: function()
{
alert('I am a lazy cat. I will not meow for you.');
}
};
function annoyThePet(petFunction)
{
//let's see what the pet can do
petFunction();
}
//annoy the dog:
annoyThePet(myDog.bark);
//annoy the cat:
annoyThePet(myCat.meow);
注意,我们把没有添加"()"的myDog.bark和myCat.meow传送给它们。假如我们那样做了,我们不是传送函数,而是调用那些方法并传送它们的返回值,这里的两个个案中都未定义。
要是你想另我的懒猫起来嗥叫,你可以简单地这样做:
myCat.meow = myDog.bark;
myCat.meow(); //alerts 'Woof!'
数组、项目和对象成员
在JS中下列两行做了同样的事。
var a = new Array();
var b = [];
正如我确定你已经知道的那样,你可以通过使用方括号在一个数组中访问个别项目:
var a = ['first', 'second', 'third'];
var v1 = a[0];
var v2 = a[1];
var v3 = a[2];
但你还不限于数字索引。你可以通过使用它的名字以字符串来访问一个JS对象的任何成员。下面的例子创建了一个空对象,并通过名字加入了一些成员。
var obj = {}; //new, empty object
obj['member_1'] = 'this is the member value';
obj['flag_2'] = false;
obj['some_function'] = function(){ /* do something */};
上述代码的作用与下面的相同:
var obj =
{
member_1:'this is the member value',
flag_2: false,
some_function: function(){ /* do something */}
};
在许多情形下,在JS中对象的概念和关联的数组(杂凑)没有区别。下面两行代码都做了同样的事情。
obj.some_function();
obj['some_function']();
关于对象已经够了,我现在能有一个类了吗?
面向对象编程语言的巨大力量来源于类的使用。我不认为仅应用我之前使用其他语言的经验就能推测出在JS中如何定义类。这由你自己来判断。
//defining a new class called Pet
var Pet = function(petName, age)
{
this.name = petName;
this.age = age;
};
//let's create an object of the Pet class
var famousDog = new Pet('Santa/'s Little Helper', 15);
alert('This pet is called ' + famousDog.name);
让我们看看如何加入一个方法到我们的Pet类。我们将使用所有类都具有的prototype属性。prototype属性是一个包含任何对象的类会具有的
所有成员的对象。甚至默认的JS类,如String、Number和Date拥有一个的prototype对象,它能让我们可以添加方法和属性并使那个类
的任何对象自动获取这个新成员。
Pet.prototype.communicate = function()
{
alert('I do not know what I should say, but my name is ' + this.name);
};
那就是一个类似于prototype.js的库会派上用场的时候。如果我们使用prototype.js,我们能使我们的代码看起来更清晰(至少在我看来是这样)。
var Pet = Class.create();
Pet.prototype =
{
//our 'constructor'
initialize: function(petName, age)
{
this.name = petName;
this.age = age;
},
communicate: function()
{
alert('I do not know what I should say, but my name is ' + this.name);
}
};
函数作为参数,一个有趣的模式
假如你从来没有接触过支持闭合的语言,你也许会发现下面的惯用法太令人震惊了。
var myArray = ['first', 'second', 'third'];
myArray.each( function(item, index)
{
alert('The item in the position #' + index + ' is:' + item);
});
哇!在你断定我已经离题太远并且想转去比这篇更好点的文章之前,让我们在这解释一下要继续干些什么。
首先,在上述实例中我们使用了prototype.js库,它添加了each函数到Array类。该each函数接到一个函数对象的参数。这个函数 对于数组中的每个项目都将被依次调用一次,对于当前项目调用时传送两个参数,item和index。让我们称这个函数为iterator函数。我们也能象 这样编写代码:
function myIterator(item, index)
{
alert('The item in the position #' + index + ' is:' + item);
}
var myArray = ['first', 'second', 'third'];
myArray.each( myIterator );
我们不会象那些学校里的那些天真孩子那样做的,对吧?虽然更严格地说,后面的形式更易于理解但导致我们要进入代码中四处找寻myIterator函数。最
好在它调用的同一个地方那里有迭代函数的逻辑。在本案例中,我们也不会在其他任何地方需要迭代函数,所以我们无损地把它转化成一个匿名函数。
这是this,但有时this也是那个
当使用JS开始编写我们的代码时,我们最普遍的一个麻烦就是this关键字的使用。它是一个真正的牵绊。
就象我们之前提及的那样,在JS中一个函数也是一个对象,而且有时候我们不注意我们正在传出一个函数。
拿这小段代码举个例:
function buttonClicked()
{
alert('button ' + this.id + ' was clicked');
}
var myButton = document.getElementById('someButtonID');
var myButton2 = document.getElementById('someOtherButtonID');
myButton.onclick = buttonClicked;
myButton2.onclick = buttonClicked;
由于buttonClicked函数在任何对象之外被定义,我们也许以为this关键字会包含一个window或document对象(假设这些代码位于一个在浏览器中被浏览的HTML页面中间)的引用。
但是当我们运行这段代码时,我们看到它如愿工作并显示被点击按钮的id。究竟其中发生了什么另我们使每个按钮的onclick方法包含buttonClicked对象引用,而无论之前那里有什么。现在无论按钮什么时候被点击,浏览器都会运行一些类似下行的东西:
myButton.onclick();
那毕竟不是这么混乱,是吧?只要你了解有别的对象要进行处理并想在这些对象的事件上,如点击按钮,进行什么行动后发生了什么就行了。
var myHelper =
{
formFields: [ ],
emptyAllFields: function()
{
for(i=0; i < this.formFields.length; i++)
{
var elementID = this.formFields[i];
var field = document.getElementById(elementID);
field.value = '';
}
}
};
//tell which form fields we want to work with
myHelper.formFields.push('txtName');
myHelper.formFields.push('txtEmail');
myHelper.formFields.push('txtAddress');
//clearing the text boxes:
myHelper.emptyAllFields();
var clearButton = document.getElementById('btnClear');
clearButton.onclick = myHelper.emptyAllFields;
因此你想:好了,现在我可以在我的页面上点击Clear按钮,那三个文本框就将被清空。然后你尝试点击该按钮后仅得到一个运行时错误。该错误将涉及(猜猜
是什么?)this关键字。该问题是this.formFields没被定义即便this包含了一个到按钮的引用,那正是现在所发生的。一个快速的解决方
案是重写我们最后一行的代码。
clearButton.onclick = function()
{
myHelper.emptyAllFields();
};
我们创建一个全新的函数,那种方法在协助对象的场景中调用了我们的协助方法。