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 Petvar Pet = function(petName, age){this.name = petName;
this.age = age;
};
//let's create an object of the Pet classvar 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对象引用,而无论之前那里有什么。现在无论按钮什么时候被点击,浏览器都会运行一些类似下行的东西:
那毕竟不是这么混乱,是吧?只要你了解有别的对象要进行处理并想在这些对象的事件上,如点击按钮,进行什么行动后发生了什么就行了。
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();
};
我们创建一个全新的函数,那种方法在协助对象的场景中调用了我们的协助方法。