2 userObject.lastLoginTime = new Date();
3 alert(userObject.lastLoginTime);
的功能与下面的代码段完全相同:
userObject[“lastLoginTime”] = new Date();
alert(userObject[“lastLoginTime”]);
我们还可以直接在 userObject 的定义中定义 lastLoginTime 属性,如下所示:
alert(userObject.lastLoginTime);
alert(x);
}
func(“blah”);
这就是通常在 JavaScript 中定义函数的方法。但是,还可以按以下方法定义该函数,您在此创建匿名函数对象,并将它赋给变量 func
alert(x);
};
func(“blah2”);
甚至也可以像下面这样,使用 Function 构造函数:
func(“blah3”);
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 提供了这样一个示例。


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);
记住这一点后,向对象添加方法将是很容易的事情:只需选择名称,然后将函数赋给该名称。因此,我通过将匿名函数分别赋给相应的方法名称,在对象中定义了三 个方法:


“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!
C++/C# 开发人员应当很熟悉 displayFullName 函数中使用的“this”关键字 — 它引用一个对象,通过对象调用方法(使用 Visual Basic 的开发人员也应当很熟悉它,它在 Visual Basic 中叫做“Me”)。因此在上面的示例中,displayFullName 中的“this”的值是 myDog 对象。但是,“this”的值不是静态的。通过不同对象调用“this”时,它的值也会更改以便指向相应的对象,如图 2 所示。


// 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!


function x() {
this .isNaN = function () {
return “not anymore ! ”;
};
}
// alert!!! trampling the Global object!!!
x();
alert(“NaN is NaN: “ + isNaN(NaN));
将返回一个对象,该对象是 Dog 类的实例。但在 JavaScript 中,本来就没有类。与访问类最近似的方法是定义构造函数,如下所示:


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 函数定义,看一看这一行:
“new”运算符执行的操作很简单。首先,它创建一个新的空对象。然后执行紧随其后的函数调用,将新的空对象设置为该函数中“this”的值。换句话说, 可以认为上面这行包含“new”运算符的代码与下面两行代码的功能相当:
var spot = {};
// call the function as a method of the empty object
DogConstructor.call(spot, “Spot”);
正如在 DogConstructor 主体中看到的那样,调用此函数将初始化对象,在调用期间关键字“this”将引用此对象。这样,就可以为对象创建模板!只要需要创建类似的对象,就可以与 构造函数一起调用“new”,返回的结果将是一个完全初始化的对象。这与类非常相似,不是吗?实际上,在 JavaScript 中构造函数的名称通常就是所模拟的类的名称,因此在上面的示例中,可以直接命名构造函数 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”);
在上面的 Dog 定义中,我定义了名为 name 的实例变量。使用 Dog 作为其构造函数所创建的每个对象都有它自己的实例变量名称副本(前面提到过,它就是对象词典的条目)。这就是希望的结果。毕竟,每个对象都需要它自己的实 例变量副本来表示其状态。但如果看看下一行,就会发现每个 Dog 实例也都有它自己的 respondTo 方法副本,这是个浪费;您只需要一个可供各个 Dog 实例共享的 respondTo 实例!通过在 Dog 以外定义 respondTo,可以避免此问题,如下所示:
// respondTo definition
}
function Dog(name) {
this .name = name;
// attached this function as a method of the object
this .respondTo = respondTo;
}
buddy 所引用的对象将从它的原型继承属性和方法,尽管仅从这一行可能无法明确判断原型来自哪里。对象 buddy 的原型来自构造函数(在这里是函数 Dog)的属性。



// Dog.prototype is the prototype of spot
alert(Dog.prototype.isPrototypeOf(spot));
// spot inherits the constructor property
// from Dog.prototype
alert(spot.constructor == Dog.prototype.constructor);
alert(spot.constructor == Dog);
// But constructor property doesn’t belong
// to spot. The line below displays “false”
alert(spot.hasOwnProperty(“constructor”));
// The constructor property belongs to Dog.prototype
// The line below displays “true”
alert(Dog.prototype.hasOwnProperty(“constructor”));


- 继承原型对象的对象上可以立即呈现对原型所做的更改,即使是在创建这些对象之后。
- 如果在对象中定义了属性/方法 X,则该对象的原型中将隐藏同名的属性/方法。例如,通过在 Dog.prototype 中定义 toString 方法,可以改写 Object.prototype 的 toString 方法。
- 更改只沿一个方向传递,即从原型到它的派生对象,但不能沿相反方向传 递。


var rover = new GreatDane();
var spot = new GreatDane();
GreatDane.prototype.getBreed = function () {
return “Great Dane”;
};
// Works, even though at this point
// rover and spot are already created.
alert(rover.getBreed());
// this hides getBreed() in GreatDane.prototype
spot.getBreed = function () {
return “Little Great Dane”;
};
alert(spot.getBreed());
// but of course, the change to getBreed
// doesn’t propagate back to GreatDane.prototype
// and other objects inheriting from it,
// it only happens in the spot object
alert(rover.getBreed());
// set static method now()
DateTime.now = function () {
return new Date();
};
alert(DateTime.now());


var len = arr.length;
var filtered = []; // shorter version of new Array();
// iterate through every element in the array...
for ( var i = 0 ; i < len; i ++ ) {
var val = arr[i];
// if the element satisfies the predicate let it through
if (pred(val)) {
filtered.push(val);
}
}
return filtered;
}
var someRandomNumbers = [ 12 , 32 , 1 , 3 , 2 , 2 , 234 , 236 , 632 , 7 , 8 ];
var numbersGreaterThan100 = filter(
function (x) { return (x > 100 ) ? true : false ; },
someRandomNumbers);
// displays 234, 236, 632
alert(numbersGreaterThan100);
但是,现在要创建不同的筛选条件,假设这次只有大于 300 的数字才能通过筛选,则可以编写下面这样的函数:
function (x) { return (x > 300 ) ? true : false ; },
someRandomNumbers);
然后,也许需要筛选大于 50、25、10、600 如此等等的数字,但作为一个聪明人,您会发现它们全部都有相同的谓词“greater than”,只有数字不同。因此,可以用类似下面的函数分开各个数字:
return function (numberToCheck) {
return (numberToCheck > lowerBound) ? true : false ;
};
}
这样,您就可以编写以下代码:


var greaterThan100 = makeGreaterThanPredicate( 100 );
alert(filter(greaterThan10, someRandomNumbers));
alert(filter(greaterThan100, someRandomNumbers));


this .getName = function () { return name; };
this .setName = function (newName) { name = newName; };
this .getAge = function () { return age; };
this .setAge = function (newAge) { age = newAge; };
}
参数 name 和 age 是构造函数 Person 的本地变量。Person 返回时,name 和 age 应当永远消失。但是,它们被作为 Person 实例的方法而分配的四个内部函数捕获,实际上这会使 name 和 age 继续存在,但只能严格地通过这四个方法访问它们。因此,您可以:


alert(ray.getName());
alert(ray.getAge());
ray.setName(“Younger Ray”);
// Instant rejuvenation!
ray.setAge( 22 );
alert(ray.getName() + “ is now “ + ray.getAge() +
“ years old.”);
未在构造函数中初始化的私有成员可以成为构造函数的本地变量,如下所示:


var occupation;
this .getOccupation = function () { return occupation; };
this .setOccupation = function (newOcc) { occupation =
newOcc; };
// accessors for name and age
}
注意,这些私有成员与我们期望从 C# 中产生的私有成员略有不同。在 C# 中,类的公用方法可以访问它的私有成员。但在 JavaScript 中,只能通过在其闭包内拥有这些私有成员的方法来访问私有成员(由于这些方法不同于普通的公用方法,它们通常被称为特权方法)。因此,在 Person 的公用方法中,仍然必须通过私有成员的特权访问器方法才能访问私有成员:
// doesn’t work!
// alert(this.name);
// this one below works
alert( this .getName());
};



function Pet(name) {
this .getName = function () { return name; };
this .setName = function (newName) { name = newName; };
}
Pet.prototype.toString = function () {
return “This pet’s name is: “ + this .getName();
};
// end of class Pet
var parrotty = new Pet(“Parrotty the Parrot”);
alert(parrotty);
现在,如何创建从 Pet 派生的类 Dog 呢?在图 9 中可以看到,Dog 有另一个属性 breed,它改写了 Pet 的 toString 方法(注意,JavaScript 的约定是方法和属性名称使用 camel 大小写,而不是在 C# 中建议的 Pascal 大小写)。图 10 显示如何这样做。


// public Dog(string name, string breed)
function Dog(name, breed) {
// think Dog : base(name)
Pet.call( this , name);
this .getBreed = function () { return breed; };
// Breed doesn’t change, obviously! It’s read only.
// this.setBreed = function(newBreed) { name = newName; };
}
// this makes Dog.prototype inherits
// from Pet.prototype
Dog.prototype = new Pet();
// remember that Pet.prototype.constructor
// points to Pet. We want our Dog instances’
// constructor to point to Dog.
Dog.prototype.constructor = Dog;
// Now we override Pet.prototype.toString
Dog.prototype.toString = function () {
return “This dog’s name is: “ + this .getName() +
“, and its breed is: “ + this .getBreed();
};
// end of class Dog
var dog = new Dog(“Buddy”, “Great Dane”);
// test the new toString()
alert(dog);
// Testing instanceof (similar to the is operator)
// (dog is Dog)? yes
alert(dog instanceof Dog);
// (dog is Pet)? yes
alert(dog instanceof Pet);
// (dog is Object)? yes
alert(dog instanceof Object);
MSDNMagNS.Pet = function (name) { // code here };
MSDNMagNS.Pet.prototype.toString = function () { // code };
var pet = new MSDNMagNS.Pet(“Yammer”);
命名空间的一个级别可能不是唯一的,因此可以创建嵌套的命名空间:


// nested namespace “Examples”
MSDNMagNS.Examples = {};
MSDNMagNS.Examples.Pet = function (name) { // code };
MSDNMagNS.Examples.Pet.prototype.toString = function () { // code };
var pet = new MSDNMagNS.Examples.Pet(“Yammer”);
可以想象,键入这些冗长的嵌套命名空间会让人很累。 幸运的是,库用户可以很容易地为命名空间指定更短的别名:
// think “using Eg = MSDNMagNS.Examples;”
var Eg = MSDNMagNS.Examples;
var pet = new Eg.Pet(“Yammer”);
alert(pet);
function F() {}
F.prototype = o;
return new F();
}
然后,由于 JavaScript 中的对象是可延展的,因此可以方便地在创建对象之后,根据需要用新字段和新方法增大对象。