使用面向对象技术创建高级Web应用程序 已翻译 100%

时过境迁_ 投递于 2013/06/20 14:40 (共 29 段, 翻译完成于 06-21)
阅读 11714
收藏 465
46
加载中

最近,我面试了一位具有5年Web应用开发经验的软件开发人员。她有4年半的JavaScript编程经验,自认为自己具有非常优秀的JavaScript技能,可是,随后我很快发现,实际上她对JavaScript却知之甚少。然而,我并不是要为此而责怪她。JavaScript就是这么不可思议。有很多人(也包括我自己,这种情况直到最近才有所改观)都自以为是,觉得因为他们懂C/C++/C#或者具有编程经验,便以为他们非常擅长JavaScript这门语言。

从某个角度讲,这种自以为是也并非毫无道理。用JavaScript做一些简单的事情是非常容易的。其入门的门槛非常低;这个语言待人宽厚,并不苛求你必须懂它很多才能开始用它编写代码。甚至对于非程序员来说,也可以仅花个把小时就能够上手用它为他的网站编写几段或多或少都有些用的脚本。

fbm
fbm
翻译于 2013/06/20 16:53
9

实际上直到最近,无论懂的JavaScript有多么少,仅仅在MSDN® DHTML参考资料以及我在C++/C#方面编程经验的帮助下,我都能够凑合过下去。直到我在工作中真正开始编写AJAX应用时,我才发现我对JavaScript的了解有多么欠缺。这种新一代的Web应用复杂的交互特性要求使用一种完全不同的方式来编写JavaScript代码。这些都是非常严肃的JavaScript应用!我们以往那种漫不经心编写脚本的方法不灵了。

面向对象的编程(OOP)这种方法广泛用于多种JavaScript库,采用这种方法可使代码库更加易于管理和维护。JavaScript支持OOP,但它的支持方式同流行的Microsoft® .NET框架下的C++、C#、Visual Basic®等语言完全不同,所以,大量使用这些语言的开发者起初可能会发现,JavaScript中的OOP比较怪异,同直觉不符。我写这篇文章就是要对JavaScript到底是如何支持面向对象编程的以及如何高效利用这种支进行面向对象的JavaScript开发进行深入讨论。接下来让我们开始谈谈对象(除了对象还能有别的吗?)吧。

fbm
fbm
翻译于 2013/06/20 17:20
6

JavaScript对象是字典

在C++或C#中,当谈及对象时,我们指的是类或者结构的实例。对象根据实例化出它的模版(也即,类)的不同而具有不同的属性和方法。JavaScript对象不是这样的。在JavaScript中,对象仅仅是name/value对的集合,我们可以把JavaScript对象看作字典,字典中的键为字符串。我们可以用我们熟悉的"." (点)操作符或者一般用于字典的"[]"操作符,来获取或者设置对象的属性。下面的代码片段

var userObject = new Object();
userObject.lastLoginTime = new Date();
alert(userObject.lastLoginTime);

同这段代码所做的完全是同样的事情:

var userObject = {}; // equivalent to new Object()
userObject["lastLoginTime"] = new Date();
alert(userObject["lastLoginTime"]);

我们还可以用这样的方式,直接在userObject的定义中定义lastLoginTime属性:

var userObject = { "lastLoginTime": new Date() };
alert(userObject.lastLoginTime);

fbm
fbm
翻译于 2013/06/20 17:33
7

请注意这同C# 3.0的对象初始化表达式是多么的相似。另外,熟悉Python的读者会发现,在第二段和第三段代码中,我们实例化userObject的方式就是Python中指定字典的方式。这里唯一的区别的就是,JavaScript中的对象/字典只接受字符串作为键,而Python中字典则无此限制。

这些例子也表明,同C++或者C#对象相比,JavaScript对象是多么地更加具有可塑性。属性lastLoginTime不必事先声明,如果在使用这个属性的时候userObject还不具有以此为名的属性,就会在userObject中把这个属性添加进来。如果记住了JavaScript对象就是字典的话,你就不会对此大惊小怪了 —— 毕竟我们随时都可以把新键(及其对应的值)添加到字典中去。

JavaScript对象的属性就是这个样子的。那么,JavaScript对象的方法呢?和属性一样,JavaScript仍然和C++/C#不同。为了理解对象的方法,就需要首先仔细看看JavaScript函数。

fbm
fbm
翻译于 2013/06/20 17:53
5

JavaScript中的函数具有首要地位

在许多编程语言中,函数和对象一般都认为是两种不同的东西。可在JavaScript中,它们之间的区别就没有那么明显了 —— JavaScript中的函数实际上就是对象,只不过这个对象具有同其相关联的一段可执行代码。请看下面这段再普通不过的代码:

function func(x) {
    alert(x);
}
func("blah");

这是JavaScript中定义函数最常用的方式了。但是,你还可以先创建一个匿名函数对象再将该对象赋值给变量func,也即,象下面那样,定义出完全相同的函数

var func = function(x) {
    alert(x);
};
func("blah2");

或者甚至通过使用Function构造器,向下面这样来定义它:

var func = new Function("x", "alert(x);");
func("blah3");

这表明,函数实际上就是一个支持函数调用操作的对象。最后这种使用Function构造器来定义函数的方式并不常用,但却为我们带来很多很有趣的可能,其原因可能你也已经发现了,在这种函数定义的方式中,函数体只是Function构造器的一个字符串型的参数。这就意味着,你可以在JavaScript运行的时候构造出任意的函数。

fbm
fbm
翻译于 2013/06/20 22:04
7

要进一步证明函数是对象,你可以就象为任何其它JavaScript对象一样,为函数设置或添加属性:

function sayHi(x) {
    alert("Hi, " + x + "!");
}

sayHi.text = "Hello World!";
sayHi["text2"] = "Hello World... again.";

alert(sayHi["text"]); // displays "Hello World!"
alert(sayHi.text2); // displays "Hello World... again."

作为对象,函数还可以赋值给变量、作为参数传递给其它函数、作为其它函数的返回值、保存为对象的属性或数组中的一员等等。图1所示为其中一例。

图1 函数在JavaScript具有首要地位

// assign an anonymous function to a variable
var greet = function(x) {
    alert("Hello, " + x);
};

greet("MSDN readers");

// passing a function as an argument to another
function square(x) {
    return x * x;
}

function operateOn(num, func) {
    return func(num);
}

// displays 256
alert(operateOn(16, square));

// functions as return values
function makeIncrementer() {
    return function(x) { return x + 1; };
}

var inc = makeIncrementer();
// displays 8
alert(inc(7));

// functions stored as array elements
var arr = [];
arr[0] = function(x) { return x * x; };
arr[1] = arr[0](2);
arr[2] = arr[0](arr[1]);
arr[3] = arr[0](arr[2]);

// displays 256
alert(arr[3]);

// functions as object properties
var obj = { "toString" : function() { return "This is an object."; } };

// calls obj.toString()
alert(obj);

fbm
fbm
翻译于 2013/06/20 22:11
5
记住这一点后,为对象添加方法就简单了,只要选择一个函数名并把一个函数赋值为这个函数名即可。接下来我通过将三个匿名函数分别赋值给各自相应的方法名,为一个对象定义了三个方法:
var myDog = {
    "name" : "Spot",
    "bark" : function() { alert("Woof!"); },
    "displayFullName" : function() {
        alert(this.name + " The Alpha Dog");
    },

    "chaseMrPostman" : function() { 
        // implementation beyond the scope of this article 
    }    
};

myDog.displayFullName(); 
myDog.bark(); // Woof!

函数displayFullName中"this"关键字的用法对C++/C#开发者来说并不陌生 —— 该方法是通过哪个对象调用的,它指的就是哪个对象(使用Visual Basic的开发者也应该熟悉这种用法 —— 只不过"this"在Visual Basic称作"Me")。因此在上面的例子中,displayFullName中"this"的值指的就是myDog对象。但是,"this"的值不是静态的。如果通过别的对象对函数进行调用,"this"的值也会随之指向这个别的对象,如图2所示。

图2 “this”随着对象的改变而改变

function displayQuote() {
    // the value of "this" will change; depends on 
    // which object it is called through
    alert(this.memorableQuote);    
}

var williamShakespeare = {
    "memorableQuote": "It is a wise father that knows his own child.", 
    "sayIt" : displayQuote
};

var markTwain = {
    "memorableQuote": "Golf is a good walk spoiled.", 
    "sayIt" : displayQuote
};

var oscarWilde = {
    "memorableQuote": "True friends stab you in the front." 
    // we can call the function displayQuote
    // as a method of oscarWilde without assigning it 
    // as oscarWilde’s method. 
    //"sayIt" : displayQuote
};

williamShakespeare.sayIt(); // true, true
markTwain.sayIt(); // he didn’t know where to play golf

// watch this, each function has a method call()
// that allows the function to be called as a 
// method of the object passed to call() as an
// argument. 
// this line below is equivalent to assigning
// displayQuote to sayIt, and calling oscarWilde.sayIt().

displayQuote.call(oscarWilde); // ouch!

图2最后一行的代码是将函数作为一个对象的方法进行调用的另外一种方式。别忘了,JavaScript中的函数是对象。每个函数对象都有一个叫做call的方法,这个方法会将函数作为该方法的第一个参数的方法进行调用。也就是说,无论将哪个对象作为第一个参数传递给call方法,它都会成为此次函数调用中"this"的值。后面我们就会看到,这个技术在调用基类构造器时会非常有用。

fbm
fbm
翻译于 2013/06/20 22:55
8

有一点要记住,那就是永远不要调用不属于任意对象却包含有"this"的函数。如果调用了的话,就会搅乱全局命名空间。这是因为在这种调用中,"this"将指向Global对象,此举将严重损害你的应用。例如,下面的脚本将会改变JavaScript的全局函数isNaN的行为。我们不推荐这么干。

alert("NaN is NaN: " + isNaN(NaN));

function x() {
    this.isNaN = function() { 
        return "not anymore!";
    };
}

// alert!!! trampling the Global object!!!
x();

alert("NaN is NaN: " + isNaN(NaN));

到此我们已经看过了创建对象并为其添加熟悉和方法的几种方式。但是,如果你仔细看了以上所举的所以代码片段就会发现,所有的熟悉和方法都是在对象的定义之中通过硬性编码定义的。要是你需要对对象的创建进行更加严格的控制,那该怎么办?例如,你可能会需要根据某些参数对对象属性中的值进行计算,或者你可能需要将对象的属性初始化为只有到代码运行时才会得到的值,你还有可能需要创建一个对象的多个实例,这些要求也是非常常见的。

在C#中,我们使用类类实例化出对象实例。但是JavaScript不一样,它并没有类的概念。相反, 在下一小节你将看到,你可以利用这一点:将函数同"new"操作符一起使用就可以把函数当着构造器来用。

fbm
fbm
翻译于 2013/06/20 23:14
5

有构造函数但没有类

JavaScript中的OOP最奇怪的事,如前所述,就是JavaScript没有C#和C++ 中所具有的类。在C#中,通过如下这样的代码

Dog spot = new Dog();

能够得到一个对象,这个对象就是Dog类的一个实例。但在JavaScript中根本就没有类。要想得到同类最近似的效果,可以象下面这样定义一个构造器函数:

function DogConstructor(name) {
    this.name = name;
    this.respondTo = function(name) {
        if(this.name == name) {
            alert("Woof");        
        }
    };
}

var spot = new DogConstructor("Spot");
spot.respondTo("Rover"); // nope
spot.respondTo("Spot"); // yeah!

好吧,这里都发生了什么?先请不要管DogConstructor 函数的定义,仔细看看这行代码:

var spot = new DogConstructor("Spot");

"new"操作符所做的事情很简单。首先,它会创建出一个新的空对象。然后,紧跟其后的函数调用就会得到执行,并且会将那个新建的空对象设置为该函数中"this"的值。换句话说,这行带有"new"操作符的代码可以看作等价于下面这两行代码:

// create an empty object
var spot = {}; 
// call the function as a method of the empty object
DogConstructor.call(spot, "Spot");

fbm
fbm
翻译于 2013/06/20 23:34
5
在DogConstructor的函数体中可以看出,调用该函数就会对调用中关键字"this"所指的对象进行初始化。采用这种方式,你就可以为对象创建模版了!无论何时当你需要创建类似的对象时,你就可以用"new"来调用该构造器函数,然后你就能够得到一个完全初始化好的对象。这和类看上去非常相似,不是吗?实际上,JavaScript中构造器函数的名字往往就是你想模拟的类的名字,所以上面例子中的构造函数你就可以直接命名为Dog:
// Think of this as class Dog
function Dog(name) {
    // instance variable 
    this.name = name;

    // instance method? Hmmm...
    this.respondTo = function(name) {
        if(this.name == name) {
            alert("Woof");        
        }
    };
}

var spot = new Dog("Spot");
fbm
fbm
翻译于 2013/06/20 23:41
5
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
加载中

评论(41)

Troland
Troland
好文章。写得不错。。以前大部分都看过了。。特别是对象的扩展重载。很容易陷入继承的东西是原型继承肯定会继承原来的原型方法。这个有可能是个问题。类也一样。。
no_user
no_user
好文章
p
pongor8
学习了,谢谢
黎开宇
黎开宇
讲的最好的一篇了,收藏
changdp
changdp
好东西,很棒。谢了。必须收藏。
tinderfang
tinderfang
是没看出高级在哪,被标题骗了。
赵世传
赵世传
jack-up
呆萌虎
呆萌虎
最都是我懂的,我还以为是说我这样的人自以为事呢。。。
无忌
无忌
很好
isware
isware
Great
返回顶部
顶部