javascript的函數(shù)
更新時(shí)間:2007年01月31日 00:00:00 作者:
作者:F. Permadi
譯者:Sheneyan(子烏)
時(shí)間:2006.01.03
英文原文: INTRODUCTION TO JavaScript Functions
中文譯文(包括示例):javascript的函數(shù)
子烏注:一篇相當(dāng)不錯(cuò)的function入門文章,個(gè)人感覺(jué)相當(dāng)經(jīng)典。
詞語(yǔ)翻譯列表
function:函數(shù)(Function未翻譯)
declare:定義
assign:指派,分配
functionbody:函數(shù)體(就是函數(shù)的內(nèi)容)
object:對(duì)象
property:屬性
unnamed:匿名(在這里沒(méi)翻譯成未命名)
object oriented programming:面相對(duì)相編程
class:類(比如后面的class data type我翻譯成類數(shù)據(jù)類型)
pointer:指針
reassign:重新分配
nest:嵌套
feature:功能,特性
local/global:局部/全局
blueprint:藍(lán)圖(?)
user defined:用戶自定義
instance:實(shí)例
prototype:原型(除了標(biāo)題都不翻譯)
internal:內(nèi)部
constructor:構(gòu)造器
duplication:
函數(shù):定義
有以下這些方法可以定義一個(gè)函數(shù)。所有這些都是有效的,但是它們?cè)诤笈_(tái)如何實(shí)現(xiàn)的則有一些差別。
一般大家都用這個(gè)寫(xiě)法來(lái)定義一個(gè)函數(shù):
[Copy to clipboard]CODE:
functionName([parameters]){functionBody};
Example D1:
[Copy to clipboard]CODE:
function add(a, b)
{
return a+b;
}
alert(add(1,2)); // 結(jié)果 3
當(dāng)我們這么定義函數(shù)的時(shí)候,函數(shù)內(nèi)容會(huì)被編譯(但不會(huì)立即執(zhí)行,除非我們?nèi)フ{(diào)用它)。而且,也許你不知道,當(dāng)這個(gè)函數(shù)創(chuàng)建的時(shí)候有一個(gè)同名的對(duì)象也被創(chuàng)建。就我們的例子來(lái)說(shuō),我們現(xiàn)在有一個(gè)對(duì)象叫做“add”(要更深入了解,看底下函數(shù):對(duì)象節(jié)。)
我們也可以通過(guò)指派一個(gè)變量名給匿名函數(shù)的方式來(lái)定義它。
Example D2
[Copy to clipboard]CODE:
var add=function(a, b)
{
return a+b;
}
alert(add(1,2)); // 結(jié)果 3
這個(gè)代碼和前一個(gè)例子做了同樣的事情。也許語(yǔ)法看起來(lái)比較奇怪,但它應(yīng)該更能讓你感覺(jué)到函數(shù)是一個(gè)對(duì)象,而且我們只是為這個(gè)對(duì)指派了一個(gè)名稱??梢园阉醋龊?nbsp;var myVar=[1,2,3]一樣的語(yǔ)句。以這種方式聲明的函數(shù)內(nèi)容也一樣會(huì)被編譯。
當(dāng)我們指派一個(gè)這樣的函數(shù)的時(shí)候,我們并不一定要求必須是匿名函數(shù)。在這里,我作了和ExampleD2一樣的事情,但我加了函數(shù)名“theAdd”,而且我可以通過(guò)調(diào)用函數(shù)名或者是那個(gè)變量來(lái)引用函數(shù)。
Example D2A
[Copy to clipboard]CODE:
var add=function theAdd(a, b)
{
return a+b;
}
alert(add(1,2)); // 結(jié)果 3
alert(theAdd(1,2)); // 結(jié)果也是 3
使用這種方式來(lái)定義函數(shù)在面向?qū)ο缶幊讨惺呛苡杏玫?,因?yàn)槲覀兡芟竦紫逻@樣使一個(gè)函數(shù)成為一個(gè)對(duì)象的屬性。
[Copy to clipboard]CODE:
var myObject=new Object();
myObject.add=function(a,b){return a+b};
// myObject 現(xiàn)在有一個(gè)叫做“add”的屬性(或方法)
// 而且我能夠象下面這樣使用它
myObject.add(1, 2);
我們也能夠通過(guò)使用運(yùn)算符new來(lái)定義一個(gè)函數(shù)。這是一個(gè)最少見(jiàn)的定義函數(shù)的方式并且并不推薦使用這種方式除非有特殊的理由(可能的理由見(jiàn)下)。語(yǔ)法如下:
[Copy to clipboard]CODE:
varName=new Function([param1Name, param2Name,...paramNName], functionBody);
Example D3:
[Copy to clipboard]CODE:
var add=new Function("a", "b", "return a+b;");
alert(add(3,4)); // 結(jié)果 7
我在這里有兩個(gè)參數(shù)叫做a和b,而函數(shù)體返回a和b的和。請(qǐng)注意new Function(...)使用了大寫(xiě)F,而不是小寫(xiě)f。 這就告訴javascript,我們將要?jiǎng)?chuàng)建一個(gè)類型是Function的對(duì)象。 還要注意到,參數(shù)名和函數(shù)體都是作為字符串而被傳遞。我們可以隨心所欲的增加參數(shù),javascript知道函數(shù)體會(huì)是右括號(hào)前的最后一個(gè)字符串(如果沒(méi)有參數(shù),你能夠只寫(xiě)函數(shù)體)。你沒(méi)必要將所有東西都寫(xiě)在一行里(使用\或者使用字符串連接符+來(lái)分隔長(zhǎng)代碼)。\標(biāo)記告訴JavaScript在下一行查找字符串的其余部分。例子如下:
Example D4
[Copy to clipboard]CODE:
// 注意 "+"
// 和 "\"的不同用法
var add=new Function("a", "b",
"alert" +
"('adding '+a+' and ' +b);\
return a+b;");
alert(add(3,4)); // 結(jié)果 7
采用這種方式定義函數(shù)會(huì)導(dǎo)致函數(shù)并沒(méi)被編譯,而且它有可能會(huì)比用其它方式定義的函數(shù)要慢。至于為什么,看一下這個(gè)代碼:
Example D5
[Copy to clipboard]CODE:
function createMyFunction(myOperator)
{
return new Function("a", "b", "return a" + myOperator + "b;");
}
var add=createMyFunction("+"); // 創(chuàng)建函數(shù) "add"
var subtract=createMyFunction("-"); // 創(chuàng)建函數(shù) "subtract"
var multiply=createMyFunction("*"); // 創(chuàng)建函數(shù) "multiply"
// test the functions
alert("加的結(jié)果="+add(10,2)); // 結(jié)果是 12
alert("減的結(jié)果="+subtract(10,2)); // 結(jié)果是 8
alert("乘的結(jié)果="+multiply(10,2)); // 結(jié)果是 20
alert(add);
這個(gè)有趣的例子創(chuàng)建了三個(gè)不同的function,通過(guò)實(shí)時(shí)傳遞不同的參數(shù)來(lái)創(chuàng)建一個(gè)新Function。因?yàn)榫幾g器沒(méi)法知道最終代碼會(huì)是什么樣子的,所以new Function(...)的內(nèi)容不會(huì)被編譯。那這有什么好處呢?嗯,舉個(gè)例子,如果你需要用戶能夠創(chuàng)建他們自己的函數(shù)的時(shí)候這個(gè)功能也許很有用,比如在游戲里。我們也許需要允許用戶添加“行為”給一個(gè)“player”。但是,再說(shuō)一次,一般情況下,我們應(yīng)該避免使用這種形式,除非有一個(gè)特殊的目的。
函數(shù):對(duì)象
函數(shù)是javascript中的一種特殊形式的對(duì)象。它是第一個(gè)[b〕類數(shù)據(jù)類型(class data type)。這意味著我們能夠給它增加屬性。這里有一些需要注意的有趣觀點(diǎn):
就像剛才提及的,當(dāng)我們定義一個(gè)函數(shù)時(shí),javascript實(shí)際上在后臺(tái)為你創(chuàng)建了一個(gè)對(duì)象。這個(gè)對(duì)象的名稱就是函數(shù)名本身。這個(gè)對(duì)象的類型是function。在下面的例子,我們也許不會(huì)意識(shí)到這一點(diǎn),但我們實(shí)際上已經(jīng)創(chuàng)建了一個(gè)對(duì)象:它叫做Ball。
Example 1
[Copy to clipboard]CODE:
function Ball() // 也許看起來(lái)有點(diǎn)奇怪,但是這個(gè)聲明
{ // 創(chuàng)建了一個(gè)叫做Ball的對(duì)象
i=1;
}
alert(typeof Ball); // 結(jié)果 "function"
我們甚至能將這個(gè)對(duì)象的內(nèi)容打印出來(lái)而且它會(huì)輸出這個(gè)函數(shù)的實(shí)際代碼,Example 2: 點(diǎn)擊 alert(Ball);來(lái)看看Ball的內(nèi)容。
我們能夠添加給Object添加屬性,包括對(duì)象function。因?yàn)槎x一個(gè)函數(shù)的實(shí)質(zhì)是創(chuàng)建一個(gè)對(duì)象。我們能夠“暗地里”給函數(shù)添加屬性。比如,我們這里定義了函數(shù)Ball,并添加屬性callsign。
[Copy to clipboard]CODE:
function Ball() // 也許看起來(lái)有點(diǎn)奇怪,但是這個(gè)聲明
{ // 創(chuàng)建了一個(gè)叫做Ball的對(duì)象,而且你能夠
} // 引用它或者象下面那樣給它增加屬性
Ball.callsign="The Ball"; // 給Ball增加屬性
alert(Ball.callsign); // 輸出 "The Ball"
因?yàn)閒unction是一個(gè)對(duì)象,我們能夠?yàn)橐粋€(gè)function分配一個(gè)指針。如下例,變量ptr指向了對(duì)象myFunction。
[Copy to clipboard]CODE:
function myFunction(message)
{
alert(message);
}
var ptr=myFunction; // ptr指向了myFunction
ptr("hello"); // 這句會(huì)執(zhí)行myFunction:輸出"hello"
我們能夠運(yùn)行這個(gè)函數(shù),就好像這個(gè)函數(shù)名已經(jīng)被指針名代替了一樣。所以在上面,這行ptr("hello"); 和myFunction("hello");的意義是一樣的。
指向函數(shù)的指針在面向?qū)ο缶幊讨邢喈?dāng)有用。例如:當(dāng)我們有多個(gè)對(duì)象指向同一個(gè)函數(shù)的時(shí)候(如下):
Example 4A
[Copy to clipboard]CODE:
function sayName(name)
{
alert(name);
}
var object1=new Object(); // 創(chuàng)建三個(gè)對(duì)象
var object2=new Object();
var object3=new Object();
object1.sayMyName=sayName; // 將這個(gè)函數(shù)指派給所有對(duì)象
object2.sayMyName=sayName;
object3.sayMyName=sayName;
object1.sayMyName("object1"); // 輸出 "object1"
object2.sayMyName("object2"); // 輸出 "object2"
object3.sayMyName("object3"); // 輸出 "object3"
因?yàn)橹挥兄羔槺槐4妫ǘ皇呛瘮?shù)本身),當(dāng)我們改變函數(shù)對(duì)象自身的時(shí)候,所有指向那個(gè)函數(shù)的指針都會(huì)發(fā)生變化。我們能夠在底下看到:
Example 5:
[Copy to clipboard]CODE:
function myFunction()
{
alert(myFunction.message);
}
myFunction.message="old";
var ptr1=myFunction; // ptr1 指向 myFunction
var ptr2=myFunction; // ptr2 也指向 myFunction
ptr1(); // 輸出 "old"
ptr2(); // 輸出 "old"
myFunction.message="new";
ptr1(); // 輸出 "new"
ptr2(); // 輸出 "new"
我們能夠在一個(gè)函數(shù)創(chuàng)建之后重新分配它,但是我們需要指向函數(shù)對(duì)象本身,而不是指向它的指針。在下例中,我將改變myfunction()的內(nèi)容。
Example 6:
[Copy to clipboard]CODE:
function myFunction()
{
alert("Old");
}
myFunction(); // 輸出 "Old"
myFunction=function()
{
alert("New");
};
myFunction(); // 輸出 "New"
舊函數(shù)哪里去了??被拋棄了。
如果我們需要保留它,我們可以在改變它之前給它分配一個(gè)指針。
Example 6A:
[Copy to clipboard]CODE:
function myFunction()
{
alert("Old");
}
var savedFuncion=myFunction;
myFunction=function()
{
alert("New");
};
myFunction(); // 輸出 "New"
savedFuncion(); // 輸出 "Old"
不過(guò)要小心,象下面這樣的例子并不會(huì)有作用,因?yàn)槭莿?chuàng)建了另一個(gè)叫做myFunctionPtr的函數(shù)而不是修改它。
Example 6B:
[Copy to clipboard]CODE:
function myFunction()
{
alert("Old");
}
var savedFunc=myFunction;
savedFunc=function()
{
alert("New");
};
myFunction(); // 輸出 "Old"
savedFunc(); // 輸出 "New"
我們還能夠在一個(gè)函數(shù)中嵌套一個(gè)函數(shù)。下例,我有一個(gè)叫做getHalfOf的函數(shù),而在它里面,我有另一個(gè)叫做calculate的函數(shù)。
Example 7
[Copy to clipboard]CODE:
function getHalfOf(num1, num2, num3)
{
function calculate(number)
{
return number/2;
}
var result="";
result+=calculate(num1)+" ";
result+=calculate(num2)+" ";
result+=calculate(num3);
return result;
}
var resultString=getHalfOf(10,20,30);
alert(resultString); // 輸出 "5 10 15"
你只能在內(nèi)部調(diào)用嵌套的函數(shù)。就是說(shuō),你不能這么調(diào)用:getHalfOf.calculate(10),因?yàn)閏alculate只有當(dāng)外部函數(shù)(getHalfOf())在運(yùn)行的時(shí)候才會(huì)存在。這和我們前面的討論一致(函數(shù)會(huì)被編譯,但只有當(dāng)你去調(diào)用它的時(shí)候才會(huì)執(zhí)行)。
你也許正在想命名沖突的問(wèn)題。比如,下面哪一個(gè)叫做calculate的函數(shù)會(huì)被調(diào)用?
Example 8
[Copy to clipboard]CODE:
function calculate(number)
{
return number/3;
}
function getHalfOf(num1, num2, num3)
{
function calculate(number)
{
return number/2;
}
var result="";
result+=calculate(num1)+" ";
result+=calculate(num2)+" ";
result+=calculate(num3);
return result;
}
var resultString=getHalfOf(10,20,30);
alert(resultString); // 輸出 "5 10 15"
在這個(gè)例子中,編譯器會(huì)首先搜索局部?jī)?nèi)存地址,所以它會(huì)使用內(nèi)嵌的calculate函數(shù)。如果我們刪除了這個(gè)內(nèi)嵌(局部)的calculate函數(shù),這個(gè)代碼會(huì)使用全局的calculate函數(shù)。
函數(shù):數(shù)據(jù)類型及構(gòu)造函數(shù)
讓我們來(lái)看看函數(shù)的另一個(gè)特殊功能--這讓它和其它對(duì)象類型截然不同。一個(gè)函數(shù)能夠用來(lái)作為一個(gè)數(shù)據(jù)類型的藍(lán)圖。這個(gè)特性通常被用在面向?qū)ο缶幊讨衼?lái)模擬用戶自定義數(shù)據(jù)類型(user defined data type)。使用用戶自定義數(shù)據(jù)類型創(chuàng)建的對(duì)象通常被成為用戶自定義對(duì)象(user defined object)。
在定義了一個(gè)函數(shù)之后,我們也同時(shí)創(chuàng)建了一個(gè)新的數(shù)據(jù)類型。這個(gè)數(shù)據(jù)類型能夠用來(lái)創(chuàng)建一個(gè)新對(duì)象。下例,我創(chuàng)建了一個(gè)叫做Ball的新數(shù)據(jù)類型。
Example DT1
[Copy to clipboard]CODE:
function Ball()
{
}
var ball0=new Ball(); // ball0 現(xiàn)在指向一個(gè)新對(duì)象
alert(ball0); // 輸出 "Object",因?yàn)?nbsp;ball0 現(xiàn)在是一個(gè)對(duì)象
這樣看來(lái),ball0=new Ball()作了什么?new關(guān)鍵字創(chuàng)建了一個(gè)類型是Object的新對(duì)象(叫做ball0)。然后它會(huì)執(zhí)行Ball(),并將這個(gè)引用傳給ball0(用于調(diào)用對(duì)象)。下面,你會(huì)看到這條消息:“creating new Ball”,如果Ball()實(shí)際上被運(yùn)行的話。
Example DT2
[Copy to clipboard]CODE:
function Ball(message)
{
alert(message);
}
var ball0=new Ball("creating new Ball"); // 創(chuàng)建對(duì)象并輸出消息
ball0.name="ball-0"; // ball0現(xiàn)在有一個(gè)屬性:name
alert(ball0.name); // 輸出 "ball-0"
我們可以把上面這段代碼的第6行看做是底下的代碼6-8行的一個(gè)簡(jiǎn)寫(xiě):
[Copy to clipboard]CODE:
function Ball(message)
{
alert(message);
}
var ball0=new Object();
ball0.construct=Ball;
ball0.construct("creating new ball"); // 執(zhí)行 ball0.Ball("creating..");
ball0.name="ball-0";
alert(ball0.name);
這行代碼ball0.construct=Ball和Example 4中的ptr=myFunction語(yǔ)法一致。
如果你還是不明白這行的含義那就回過(guò)頭再?gòu)?fù)習(xí)一下Example 4。注意:你也許考慮直接運(yùn)行ball0.Ball("..."),但是它不會(huì)起作用的,因?yàn)閎all0并沒(méi)有一個(gè)叫做Ball("...")的屬性,并且它也不知道你究竟想作些什么。
當(dāng)我們象上面那樣使用關(guān)鍵字new創(chuàng)建一個(gè)對(duì)象的時(shí)候,一個(gè)新的Object被創(chuàng)建了。我們可以在創(chuàng)建之后給這個(gè)對(duì)象添加屬性(就好像我在上面那樣添加屬性name。而接下來(lái)的問(wèn)題就是如果我們創(chuàng)建了這個(gè)對(duì)象的另外一個(gè)實(shí)例,我們得象下面那樣再次給這個(gè)新對(duì)象添加這個(gè)屬性。)
Example DT3 (creates 3 ball objects)
[Copy to clipboard]CODE:
function Ball()
{
}
var ball0=new Ball(); // ball0 現(xiàn)在指向了類型Ball的一個(gè)新實(shí)例
ball0.name="ball-0"; // ball0 現(xiàn)在有一個(gè)屬性"name"
var ball1=new Ball();
ball1.name="ball-1";
var ball2=new Ball();
alert(ball0.name); // 輸出 "ball-0"
alert(ball1.name); // 輸出 "ball-1"
alert(ball2.name); // 哦,我忘記給ball2添加“name”了!
我忘記給ball2添加屬性name了,如果在正式的程序中這也許會(huì)引發(fā)問(wèn)題。有什么好辦法可以自動(dòng)增加屬性呢?嗯,有一個(gè):使用this關(guān)鍵字。this這個(gè)詞在function中有特別的意義。它指向了調(diào)用函數(shù)的那個(gè)對(duì)象。讓我們看看下面的另一個(gè)示例,這時(shí)候我們?cè)跇?gòu)造函數(shù)中添加上這些屬性:
Example DT4
[Copy to clipboard]CODE:
function Ball(message, specifiedName)
{
alert(message);
this.name=specifiedName;
}
var ball0=new Ball("creating new Ball", "Soccer Ball");
alert(ball0.name); // prints "Soccer Ball"
請(qǐng)記?。菏莕ew關(guān)鍵字最終使得構(gòu)造函數(shù)被執(zhí)行。在這個(gè)例子中,它將會(huì)運(yùn)行Ball("creating new Ball", "Soccer Ball");而關(guān)鍵字this將指向ball0。
因此,這行:this.name=specifiedName變成了ball0.name="Soccer Ball"。
它主要是說(shuō):給ball0添加屬性name,屬性值是Soccer Ball。
我們現(xiàn)在只是添加了一個(gè)name屬性給ball0,看起來(lái)和上一個(gè)例子中所做的很象,但卻是一個(gè)更好更具擴(kuò)展性的方法。現(xiàn)在,我們可以隨心所欲的創(chuàng)建許多帶有屬性的ball而無(wú)需我們手動(dòng)添加它們。而且,人們也希望創(chuàng)建的Ball對(duì)象能夠清晰的看懂它的構(gòu)造函數(shù)并且能夠輕松找出Ball的所有屬性。讓我們添加更多屬性到Ball里。
Example DT5
[Copy to clipboard]CODE:
function Ball(color, specifiedName, owner, weight)
{
this.name=specifiedName;
this.color=color;
this.owner=owner;
this.weight=weight;
}
var ball0=new Ball("black/white", "Soccer Ball", "John", 20);
var ball1=new Ball("gray", "Bowling Ball", "John", 30);
var ball2=new Ball("yellow", "Golf Ball", "John", 55);
var balloon=new Ball("red", "Balloon", "Pete", 10);
alert(ball0.name); // 輸出 "Soccer Ball"
alert(balloon.name); // 輸出 "Balloon"
alert(ball2.weight); // 輸出 "55"
嘿!使用面向?qū)ο笮g(shù)語(yǔ),你能夠說(shuō)Ball是一個(gè)擁有如下屬性的對(duì)象類型:name, color, owner, weight。
我們并沒(méi)被限制只能添加形如字符串或者數(shù)字之類的簡(jiǎn)單數(shù)據(jù)類型作為屬性。我們也能夠?qū)?duì)象賦給屬性。下面,supervisor是Employee的一個(gè)屬性.
Example DT6
[Copy to clipboard]CODE:
function Employee(name, salary, mySupervisor)
{
this.name=name;
this.salary=salary;
this.supervisor=mySupervisor;
}
var boss=new Employee("John", 200);
var manager=new Employee("Joan", 50, boss);
var teamLeader=new Employee("Rose", 50, boss);
alert(manager.supervisor.name+" is the supervisor of "+manager.name);
alert(manager.name+"\'s supervisor is "+manager.supervisor.name);
會(huì)輸出什么呢?
就像你在上面這個(gè)例子中看到的那樣,manager和teamLeader都有一個(gè)supervisor屬性,而這個(gè)屬性是類型Employee的一個(gè)對(duì)象。
任何類型的對(duì)象都可以作為一個(gè)屬性,回憶一下前面的Example 4(不是Example DT4),函數(shù)也是一個(gè)對(duì)象。所以你可以讓一個(gè)函數(shù)作為一個(gè)對(duì)象的一個(gè)屬性。下面,我將添加兩個(gè)函數(shù)getSalary和addSalary。
Example DT7
[Copy to clipboard]CODE:
function Employee(name, salary)
{
this.name=name;
this.salary=salary;
this.addSalary=addSalaryFunction;
this.getSalary=function()
{
return this.salary;
};
}
function addSalaryFunction(addition)
{
this.salary=this.salary+addition;
}
var boss=new Employee("John", 200000);
boss.addSalary(10000); // boss 長(zhǎng)了 10K 工資……為什么老板工資可以長(zhǎng)這么多:'(
alert(boss.getSalary()); // 輸出 210K……為什么默認(rèn)工資也那么高……:'(
addSalary和getSalary演示了幾種將函數(shù)賦給屬性的不同方法。如果你記得我們最開(kāi)始的討論;我討論了三種聲明函數(shù)的不同方式。所有那些在這里都是適用的,但是上面展示的兩個(gè)最常用。
讓我們看看有什么不同。下面,注意一下9-12行的代碼。當(dāng)這部分代碼執(zhí)行的時(shí)候,函數(shù)getSalary被聲明。如前面數(shù)次提到的,一個(gè)函數(shù)聲明的結(jié)果是一個(gè)對(duì)象被創(chuàng)建。所以這時(shí)候boss被創(chuàng)建(接下來(lái)的第19行),而boss里有一個(gè)getSalary屬性。
[Copy to clipboard]CODE:
function Employee(name, salary)
{
this.name=name;
this.salary=salary;
this.addSalary=addSalaryFunction;
this.getSalary=function()
{
return this.salary;
};
}
function addSalaryFunction(addition)
{
this.salary=this.salary+addition;
}
var boss=new Employee("John", 200000);
var boss2=new Employee("Joan", 200000);
var boss3=new Employee("Kim", 200000);
當(dāng)你創(chuàng)建這個(gè)對(duì)象的更多實(shí)例時(shí)(boss2和boss3),每一個(gè)實(shí)例都有一份getSalary代碼的單獨(dú)拷貝;而與此相反,addSalary則指向了同一個(gè)地方(即addSalaryFunction)。
看看下面的代碼來(lái)理解一下上面所描述的內(nèi)容。
Example DT8
[Copy to clipboard]CODE:
function Employee(name, salary)
{
this.name=name;
this.salary=salary;
this.addSalary=addSalaryFunction;
this.getSalary=function()
{
return this.salary;
};
}
function addSalaryFunction(addition)
{
this.salary=this.salary+addition;
}
var boss1=new Employee("John", 200000);
var boss2=new Employee("Joan", 200000);
// 給getSalary函數(shù)對(duì)象添加屬性
boss1.getSalary.owner="boss1";
boss2.getSalary.owner="boss2";
alert(boss1.getSalary.owner); // 輸出 "boss1"
alert(boss2.getSalary.owner); // 輸出 "boss2"
// 如果兩個(gè)對(duì)象指向同一個(gè)函數(shù)對(duì)象,那么
// 上面兩個(gè)輸出都應(yīng)該是“boss2”。
// 給addSalary函數(shù)對(duì)象添加屬性
boss1.addSalary.owner="boss1";
boss1.addSalary.owner="boss2";
alert(boss1.addSalary.owner); // 輸出 "boss2"
alert(boss2.addSalary.owner); // 輸出 "boss2"
// 因?yàn)閮蓚€(gè)對(duì)象都指向同一個(gè)函數(shù),(子烏注:原文寫(xiě)are not pointing to the same function,疑為筆誤)
// 當(dāng)修改其中一個(gè)的時(shí)候,會(huì)影響所有的實(shí)例(所以兩個(gè)都輸出“boss2”).
也許不是重要的事情,但這里有一些關(guān)于運(yùn)行類似上面的getSalary的內(nèi)嵌函數(shù)的結(jié)論: 1) 需要更多的存儲(chǔ)空間來(lái)存儲(chǔ)對(duì)象(因?yàn)槊恳粋€(gè)對(duì)象實(shí)例都會(huì)有它自己的getSalary代碼拷貝);2) javascript需要更多時(shí)間來(lái)構(gòu)造這個(gè)對(duì)象。
讓我們重新寫(xiě)這個(gè)示例來(lái)讓它更有效率些。
Example DT9
[Copy to clipboard]CODE:
function Employee(name, salary)
{
this.name=name;
this.salary=salary;
this.addSalary=addSalaryFunction;
this.getSalary=getSalaryFunction;
}
function getSalaryFunction()
{
return this.salary;
}
function addSalaryFunction(addition)
{
this.salary=this.salary+addition;
}
看這兒,兩個(gè)函數(shù)都指向同一個(gè)地方,這將會(huì)節(jié)約空間和縮短構(gòu)造時(shí)間(特別是當(dāng)你有一大堆內(nèi)嵌函數(shù)在一個(gè)構(gòu)造函數(shù)的時(shí)候)。這里有另外一個(gè)函數(shù)的功能能夠來(lái)提升這個(gè)設(shè)計(jì),它叫做prototype,而我們將在下一節(jié)討論它。
函數(shù):原型
每一個(gè)構(gòu)造函數(shù)都有一個(gè)屬性叫做原型(prototype,下面都不再翻譯,使用其原文)。這個(gè)屬性非常有用:為一個(gè)特定類聲明通用的變量或者函數(shù)。
你不需要顯式地聲明一個(gè)prototype屬性,因?yàn)樵诿恳粋€(gè)構(gòu)造函數(shù)中都有它的存在。你可以看看下面的例子:
Example PT1
[Copy to clipboard]CODE:
function Test()
{
}
alert(Test.prototype); // 輸出 "Object"
就如你在上面所看到的,prototype是一個(gè)對(duì)象,因此,你能夠給它添加屬性。你添加給prototype的屬性將會(huì)成為使用這個(gè)構(gòu)造函數(shù)創(chuàng)建的對(duì)象的通用屬性。
例如,我下面有一個(gè)數(shù)據(jù)類型Fish,我想讓所有的魚(yú)都有這些屬性:livesIn="water"和price=20;為了實(shí)現(xiàn)這個(gè),我可以給構(gòu)造函數(shù)Fish的prototype添加那些屬性。
Example PT2
[Copy to clipboard]CODE:
function Fish(name, color)
{
this.name=name;
this.color=color;
}
Fish.prototype.livesIn="water";
Fish.prototype.price=20;
接下來(lái)讓我們作幾條魚(yú):
[Copy to clipboard]CODE:
var fish1=new Fish("mackarel", "gray");
var fish2=new Fish("goldfish", "orange");
var fish3=new Fish("salmon", "white");
再來(lái)看看魚(yú)都有哪些屬性:
[Copy to clipboard]CODE:
for (var i=1; i<=3; i++)
{
var fish=eval("fish"+i); // 我只是取得指向這條魚(yú)的指針
alert(fish.name+","+fish.color+","+fish.livesIn+","+fish.price);
}
輸出應(yīng)該是:
[Copy to clipboard]CODE:
"mackarel, gray, water, 20"
"goldfish, orange, water, 20"
"salmon, white water, 20"
你看到所有的魚(yú)都有屬性livesIn和price,我們甚至都沒(méi)有為每一條不同的魚(yú)特別聲明這些屬性。這時(shí)因?yàn)楫?dāng)一個(gè)對(duì)象被創(chuàng)建時(shí),這個(gè)構(gòu)造函數(shù)將會(huì)把它的屬性prototype賦給新對(duì)象的內(nèi)部屬性__proto__。這個(gè)__proto__被這個(gè)對(duì)象用來(lái)查找它的屬性。
你也可以通過(guò)prototype來(lái)給所有對(duì)象添加共用的函數(shù)。這有一個(gè)好處:你不需要每次在構(gòu)造一個(gè)對(duì)象的時(shí)候創(chuàng)建并初始化這個(gè)函數(shù)。為了解釋這一點(diǎn),讓我們重新來(lái)看Example DT9并使用prototype來(lái)重寫(xiě)它:
Example PT3
[Copy to clipboard]CODE:
function Employee(name, salary)
{
this.name=name;
this.salary=salary;
}
Employee.prototype.getSalary=function getSalaryFunction()
{
return this.salary;
}
Employee.prototype.addSalary=function addSalaryFunction(addition)
{
this.salary=this.salary+addition;
}
我們可以象通常那樣創(chuàng)建對(duì)象:
[Copy to clipboard]CODE:
var boss1=new Employee("Joan", 200000);
var boss2=new Employee("Kim", 100000);
var boss3=new Employee("Sam", 150000);
并驗(yàn)證它:
[Copy to clipboard]CODE:
alert(boss1.getSalary()); // 輸出 200000
alert(boss2.getSalary()); // 輸出 100000
alert(boss3.getSalary()); // 輸出 150000
這里有一個(gè)圖示來(lái)說(shuō)明prototype是如何工作的。這個(gè)對(duì)象的每一個(gè)實(shí)例(boss1, boss2, boss3)都有一個(gè)內(nèi)部屬性叫做__proto__,這個(gè)屬性指向了它的構(gòu)造器(Employee)的屬性prototype。當(dāng)你執(zhí)行g(shù)etSalary或者addSalary的時(shí)候,這個(gè)對(duì)象會(huì)在它的__proto__找到并執(zhí)行這個(gè)代碼。注意這點(diǎn):這里并沒(méi)有代碼的復(fù)制(和Example DT8的圖表作一下對(duì)比)。
譯者:Sheneyan(子烏)
時(shí)間:2006.01.03
英文原文: INTRODUCTION TO JavaScript Functions
中文譯文(包括示例):javascript的函數(shù)
子烏注:一篇相當(dāng)不錯(cuò)的function入門文章,個(gè)人感覺(jué)相當(dāng)經(jīng)典。
詞語(yǔ)翻譯列表
function:函數(shù)(Function未翻譯)
declare:定義
assign:指派,分配
functionbody:函數(shù)體(就是函數(shù)的內(nèi)容)
object:對(duì)象
property:屬性
unnamed:匿名(在這里沒(méi)翻譯成未命名)
object oriented programming:面相對(duì)相編程
class:類(比如后面的class data type我翻譯成類數(shù)據(jù)類型)
pointer:指針
reassign:重新分配
nest:嵌套
feature:功能,特性
local/global:局部/全局
blueprint:藍(lán)圖(?)
user defined:用戶自定義
instance:實(shí)例
prototype:原型(除了標(biāo)題都不翻譯)
internal:內(nèi)部
constructor:構(gòu)造器
duplication:
函數(shù):定義
有以下這些方法可以定義一個(gè)函數(shù)。所有這些都是有效的,但是它們?cè)诤笈_(tái)如何實(shí)現(xiàn)的則有一些差別。
一般大家都用這個(gè)寫(xiě)法來(lái)定義一個(gè)函數(shù):
[Copy to clipboard]CODE:
functionName([parameters]){functionBody};
Example D1:
[Copy to clipboard]CODE:
function add(a, b)
{
return a+b;
}
alert(add(1,2)); // 結(jié)果 3
當(dāng)我們這么定義函數(shù)的時(shí)候,函數(shù)內(nèi)容會(huì)被編譯(但不會(huì)立即執(zhí)行,除非我們?nèi)フ{(diào)用它)。而且,也許你不知道,當(dāng)這個(gè)函數(shù)創(chuàng)建的時(shí)候有一個(gè)同名的對(duì)象也被創(chuàng)建。就我們的例子來(lái)說(shuō),我們現(xiàn)在有一個(gè)對(duì)象叫做“add”(要更深入了解,看底下函數(shù):對(duì)象節(jié)。)
我們也可以通過(guò)指派一個(gè)變量名給匿名函數(shù)的方式來(lái)定義它。
Example D2
[Copy to clipboard]CODE:
var add=function(a, b)
{
return a+b;
}
alert(add(1,2)); // 結(jié)果 3
這個(gè)代碼和前一個(gè)例子做了同樣的事情。也許語(yǔ)法看起來(lái)比較奇怪,但它應(yīng)該更能讓你感覺(jué)到函數(shù)是一個(gè)對(duì)象,而且我們只是為這個(gè)對(duì)指派了一個(gè)名稱??梢园阉醋龊?nbsp;var myVar=[1,2,3]一樣的語(yǔ)句。以這種方式聲明的函數(shù)內(nèi)容也一樣會(huì)被編譯。
當(dāng)我們指派一個(gè)這樣的函數(shù)的時(shí)候,我們并不一定要求必須是匿名函數(shù)。在這里,我作了和ExampleD2一樣的事情,但我加了函數(shù)名“theAdd”,而且我可以通過(guò)調(diào)用函數(shù)名或者是那個(gè)變量來(lái)引用函數(shù)。
Example D2A
[Copy to clipboard]CODE:
var add=function theAdd(a, b)
{
return a+b;
}
alert(add(1,2)); // 結(jié)果 3
alert(theAdd(1,2)); // 結(jié)果也是 3
使用這種方式來(lái)定義函數(shù)在面向?qū)ο缶幊讨惺呛苡杏玫?,因?yàn)槲覀兡芟竦紫逻@樣使一個(gè)函數(shù)成為一個(gè)對(duì)象的屬性。
[Copy to clipboard]CODE:
var myObject=new Object();
myObject.add=function(a,b){return a+b};
// myObject 現(xiàn)在有一個(gè)叫做“add”的屬性(或方法)
// 而且我能夠象下面這樣使用它
myObject.add(1, 2);
我們也能夠通過(guò)使用運(yùn)算符new來(lái)定義一個(gè)函數(shù)。這是一個(gè)最少見(jiàn)的定義函數(shù)的方式并且并不推薦使用這種方式除非有特殊的理由(可能的理由見(jiàn)下)。語(yǔ)法如下:
[Copy to clipboard]CODE:
varName=new Function([param1Name, param2Name,...paramNName], functionBody);
Example D3:
[Copy to clipboard]CODE:
var add=new Function("a", "b", "return a+b;");
alert(add(3,4)); // 結(jié)果 7
我在這里有兩個(gè)參數(shù)叫做a和b,而函數(shù)體返回a和b的和。請(qǐng)注意new Function(...)使用了大寫(xiě)F,而不是小寫(xiě)f。 這就告訴javascript,我們將要?jiǎng)?chuàng)建一個(gè)類型是Function的對(duì)象。 還要注意到,參數(shù)名和函數(shù)體都是作為字符串而被傳遞。我們可以隨心所欲的增加參數(shù),javascript知道函數(shù)體會(huì)是右括號(hào)前的最后一個(gè)字符串(如果沒(méi)有參數(shù),你能夠只寫(xiě)函數(shù)體)。你沒(méi)必要將所有東西都寫(xiě)在一行里(使用\或者使用字符串連接符+來(lái)分隔長(zhǎng)代碼)。\標(biāo)記告訴JavaScript在下一行查找字符串的其余部分。例子如下:
Example D4
[Copy to clipboard]CODE:
// 注意 "+"
// 和 "\"的不同用法
var add=new Function("a", "b",
"alert" +
"('adding '+a+' and ' +b);\
return a+b;");
alert(add(3,4)); // 結(jié)果 7
采用這種方式定義函數(shù)會(huì)導(dǎo)致函數(shù)并沒(méi)被編譯,而且它有可能會(huì)比用其它方式定義的函數(shù)要慢。至于為什么,看一下這個(gè)代碼:
Example D5
[Copy to clipboard]CODE:
function createMyFunction(myOperator)
{
return new Function("a", "b", "return a" + myOperator + "b;");
}
var add=createMyFunction("+"); // 創(chuàng)建函數(shù) "add"
var subtract=createMyFunction("-"); // 創(chuàng)建函數(shù) "subtract"
var multiply=createMyFunction("*"); // 創(chuàng)建函數(shù) "multiply"
// test the functions
alert("加的結(jié)果="+add(10,2)); // 結(jié)果是 12
alert("減的結(jié)果="+subtract(10,2)); // 結(jié)果是 8
alert("乘的結(jié)果="+multiply(10,2)); // 結(jié)果是 20
alert(add);
這個(gè)有趣的例子創(chuàng)建了三個(gè)不同的function,通過(guò)實(shí)時(shí)傳遞不同的參數(shù)來(lái)創(chuàng)建一個(gè)新Function。因?yàn)榫幾g器沒(méi)法知道最終代碼會(huì)是什么樣子的,所以new Function(...)的內(nèi)容不會(huì)被編譯。那這有什么好處呢?嗯,舉個(gè)例子,如果你需要用戶能夠創(chuàng)建他們自己的函數(shù)的時(shí)候這個(gè)功能也許很有用,比如在游戲里。我們也許需要允許用戶添加“行為”給一個(gè)“player”。但是,再說(shuō)一次,一般情況下,我們應(yīng)該避免使用這種形式,除非有一個(gè)特殊的目的。
函數(shù):對(duì)象
函數(shù)是javascript中的一種特殊形式的對(duì)象。它是第一個(gè)[b〕類數(shù)據(jù)類型(class data type)。這意味著我們能夠給它增加屬性。這里有一些需要注意的有趣觀點(diǎn):
就像剛才提及的,當(dāng)我們定義一個(gè)函數(shù)時(shí),javascript實(shí)際上在后臺(tái)為你創(chuàng)建了一個(gè)對(duì)象。這個(gè)對(duì)象的名稱就是函數(shù)名本身。這個(gè)對(duì)象的類型是function。在下面的例子,我們也許不會(huì)意識(shí)到這一點(diǎn),但我們實(shí)際上已經(jīng)創(chuàng)建了一個(gè)對(duì)象:它叫做Ball。
Example 1
[Copy to clipboard]CODE:
function Ball() // 也許看起來(lái)有點(diǎn)奇怪,但是這個(gè)聲明
{ // 創(chuàng)建了一個(gè)叫做Ball的對(duì)象
i=1;
}
alert(typeof Ball); // 結(jié)果 "function"
我們甚至能將這個(gè)對(duì)象的內(nèi)容打印出來(lái)而且它會(huì)輸出這個(gè)函數(shù)的實(shí)際代碼,Example 2: 點(diǎn)擊 alert(Ball);來(lái)看看Ball的內(nèi)容。
我們能夠添加給Object添加屬性,包括對(duì)象function。因?yàn)槎x一個(gè)函數(shù)的實(shí)質(zhì)是創(chuàng)建一個(gè)對(duì)象。我們能夠“暗地里”給函數(shù)添加屬性。比如,我們這里定義了函數(shù)Ball,并添加屬性callsign。
[Copy to clipboard]CODE:
function Ball() // 也許看起來(lái)有點(diǎn)奇怪,但是這個(gè)聲明
{ // 創(chuàng)建了一個(gè)叫做Ball的對(duì)象,而且你能夠
} // 引用它或者象下面那樣給它增加屬性
Ball.callsign="The Ball"; // 給Ball增加屬性
alert(Ball.callsign); // 輸出 "The Ball"
因?yàn)閒unction是一個(gè)對(duì)象,我們能夠?yàn)橐粋€(gè)function分配一個(gè)指針。如下例,變量ptr指向了對(duì)象myFunction。
[Copy to clipboard]CODE:
function myFunction(message)
{
alert(message);
}
var ptr=myFunction; // ptr指向了myFunction
ptr("hello"); // 這句會(huì)執(zhí)行myFunction:輸出"hello"
我們能夠運(yùn)行這個(gè)函數(shù),就好像這個(gè)函數(shù)名已經(jīng)被指針名代替了一樣。所以在上面,這行ptr("hello"); 和myFunction("hello");的意義是一樣的。
指向函數(shù)的指針在面向?qū)ο缶幊讨邢喈?dāng)有用。例如:當(dāng)我們有多個(gè)對(duì)象指向同一個(gè)函數(shù)的時(shí)候(如下):
Example 4A
[Copy to clipboard]CODE:
function sayName(name)
{
alert(name);
}
var object1=new Object(); // 創(chuàng)建三個(gè)對(duì)象
var object2=new Object();
var object3=new Object();
object1.sayMyName=sayName; // 將這個(gè)函數(shù)指派給所有對(duì)象
object2.sayMyName=sayName;
object3.sayMyName=sayName;
object1.sayMyName("object1"); // 輸出 "object1"
object2.sayMyName("object2"); // 輸出 "object2"
object3.sayMyName("object3"); // 輸出 "object3"
因?yàn)橹挥兄羔槺槐4妫ǘ皇呛瘮?shù)本身),當(dāng)我們改變函數(shù)對(duì)象自身的時(shí)候,所有指向那個(gè)函數(shù)的指針都會(huì)發(fā)生變化。我們能夠在底下看到:
Example 5:
[Copy to clipboard]CODE:
function myFunction()
{
alert(myFunction.message);
}
myFunction.message="old";
var ptr1=myFunction; // ptr1 指向 myFunction
var ptr2=myFunction; // ptr2 也指向 myFunction
ptr1(); // 輸出 "old"
ptr2(); // 輸出 "old"
myFunction.message="new";
ptr1(); // 輸出 "new"
ptr2(); // 輸出 "new"
我們能夠在一個(gè)函數(shù)創(chuàng)建之后重新分配它,但是我們需要指向函數(shù)對(duì)象本身,而不是指向它的指針。在下例中,我將改變myfunction()的內(nèi)容。
Example 6:
[Copy to clipboard]CODE:
function myFunction()
{
alert("Old");
}
myFunction(); // 輸出 "Old"
myFunction=function()
{
alert("New");
};
myFunction(); // 輸出 "New"
舊函數(shù)哪里去了??被拋棄了。
如果我們需要保留它,我們可以在改變它之前給它分配一個(gè)指針。
Example 6A:
[Copy to clipboard]CODE:
function myFunction()
{
alert("Old");
}
var savedFuncion=myFunction;
myFunction=function()
{
alert("New");
};
myFunction(); // 輸出 "New"
savedFuncion(); // 輸出 "Old"
不過(guò)要小心,象下面這樣的例子并不會(huì)有作用,因?yàn)槭莿?chuàng)建了另一個(gè)叫做myFunctionPtr的函數(shù)而不是修改它。
Example 6B:
[Copy to clipboard]CODE:
function myFunction()
{
alert("Old");
}
var savedFunc=myFunction;
savedFunc=function()
{
alert("New");
};
myFunction(); // 輸出 "Old"
savedFunc(); // 輸出 "New"
我們還能夠在一個(gè)函數(shù)中嵌套一個(gè)函數(shù)。下例,我有一個(gè)叫做getHalfOf的函數(shù),而在它里面,我有另一個(gè)叫做calculate的函數(shù)。
Example 7
[Copy to clipboard]CODE:
function getHalfOf(num1, num2, num3)
{
function calculate(number)
{
return number/2;
}
var result="";
result+=calculate(num1)+" ";
result+=calculate(num2)+" ";
result+=calculate(num3);
return result;
}
var resultString=getHalfOf(10,20,30);
alert(resultString); // 輸出 "5 10 15"
你只能在內(nèi)部調(diào)用嵌套的函數(shù)。就是說(shuō),你不能這么調(diào)用:getHalfOf.calculate(10),因?yàn)閏alculate只有當(dāng)外部函數(shù)(getHalfOf())在運(yùn)行的時(shí)候才會(huì)存在。這和我們前面的討論一致(函數(shù)會(huì)被編譯,但只有當(dāng)你去調(diào)用它的時(shí)候才會(huì)執(zhí)行)。
你也許正在想命名沖突的問(wèn)題。比如,下面哪一個(gè)叫做calculate的函數(shù)會(huì)被調(diào)用?
Example 8
[Copy to clipboard]CODE:
function calculate(number)
{
return number/3;
}
function getHalfOf(num1, num2, num3)
{
function calculate(number)
{
return number/2;
}
var result="";
result+=calculate(num1)+" ";
result+=calculate(num2)+" ";
result+=calculate(num3);
return result;
}
var resultString=getHalfOf(10,20,30);
alert(resultString); // 輸出 "5 10 15"
在這個(gè)例子中,編譯器會(huì)首先搜索局部?jī)?nèi)存地址,所以它會(huì)使用內(nèi)嵌的calculate函數(shù)。如果我們刪除了這個(gè)內(nèi)嵌(局部)的calculate函數(shù),這個(gè)代碼會(huì)使用全局的calculate函數(shù)。
函數(shù):數(shù)據(jù)類型及構(gòu)造函數(shù)
讓我們來(lái)看看函數(shù)的另一個(gè)特殊功能--這讓它和其它對(duì)象類型截然不同。一個(gè)函數(shù)能夠用來(lái)作為一個(gè)數(shù)據(jù)類型的藍(lán)圖。這個(gè)特性通常被用在面向?qū)ο缶幊讨衼?lái)模擬用戶自定義數(shù)據(jù)類型(user defined data type)。使用用戶自定義數(shù)據(jù)類型創(chuàng)建的對(duì)象通常被成為用戶自定義對(duì)象(user defined object)。
在定義了一個(gè)函數(shù)之后,我們也同時(shí)創(chuàng)建了一個(gè)新的數(shù)據(jù)類型。這個(gè)數(shù)據(jù)類型能夠用來(lái)創(chuàng)建一個(gè)新對(duì)象。下例,我創(chuàng)建了一個(gè)叫做Ball的新數(shù)據(jù)類型。
Example DT1
[Copy to clipboard]CODE:
function Ball()
{
}
var ball0=new Ball(); // ball0 現(xiàn)在指向一個(gè)新對(duì)象
alert(ball0); // 輸出 "Object",因?yàn)?nbsp;ball0 現(xiàn)在是一個(gè)對(duì)象
這樣看來(lái),ball0=new Ball()作了什么?new關(guān)鍵字創(chuàng)建了一個(gè)類型是Object的新對(duì)象(叫做ball0)。然后它會(huì)執(zhí)行Ball(),并將這個(gè)引用傳給ball0(用于調(diào)用對(duì)象)。下面,你會(huì)看到這條消息:“creating new Ball”,如果Ball()實(shí)際上被運(yùn)行的話。
Example DT2
[Copy to clipboard]CODE:
function Ball(message)
{
alert(message);
}
var ball0=new Ball("creating new Ball"); // 創(chuàng)建對(duì)象并輸出消息
ball0.name="ball-0"; // ball0現(xiàn)在有一個(gè)屬性:name
alert(ball0.name); // 輸出 "ball-0"
我們可以把上面這段代碼的第6行看做是底下的代碼6-8行的一個(gè)簡(jiǎn)寫(xiě):
[Copy to clipboard]CODE:
function Ball(message)
{
alert(message);
}
var ball0=new Object();
ball0.construct=Ball;
ball0.construct("creating new ball"); // 執(zhí)行 ball0.Ball("creating..");
ball0.name="ball-0";
alert(ball0.name);
這行代碼ball0.construct=Ball和Example 4中的ptr=myFunction語(yǔ)法一致。
如果你還是不明白這行的含義那就回過(guò)頭再?gòu)?fù)習(xí)一下Example 4。注意:你也許考慮直接運(yùn)行ball0.Ball("..."),但是它不會(huì)起作用的,因?yàn)閎all0并沒(méi)有一個(gè)叫做Ball("...")的屬性,并且它也不知道你究竟想作些什么。
當(dāng)我們象上面那樣使用關(guān)鍵字new創(chuàng)建一個(gè)對(duì)象的時(shí)候,一個(gè)新的Object被創(chuàng)建了。我們可以在創(chuàng)建之后給這個(gè)對(duì)象添加屬性(就好像我在上面那樣添加屬性name。而接下來(lái)的問(wèn)題就是如果我們創(chuàng)建了這個(gè)對(duì)象的另外一個(gè)實(shí)例,我們得象下面那樣再次給這個(gè)新對(duì)象添加這個(gè)屬性。)
Example DT3 (creates 3 ball objects)
[Copy to clipboard]CODE:
function Ball()
{
}
var ball0=new Ball(); // ball0 現(xiàn)在指向了類型Ball的一個(gè)新實(shí)例
ball0.name="ball-0"; // ball0 現(xiàn)在有一個(gè)屬性"name"
var ball1=new Ball();
ball1.name="ball-1";
var ball2=new Ball();
alert(ball0.name); // 輸出 "ball-0"
alert(ball1.name); // 輸出 "ball-1"
alert(ball2.name); // 哦,我忘記給ball2添加“name”了!
我忘記給ball2添加屬性name了,如果在正式的程序中這也許會(huì)引發(fā)問(wèn)題。有什么好辦法可以自動(dòng)增加屬性呢?嗯,有一個(gè):使用this關(guān)鍵字。this這個(gè)詞在function中有特別的意義。它指向了調(diào)用函數(shù)的那個(gè)對(duì)象。讓我們看看下面的另一個(gè)示例,這時(shí)候我們?cè)跇?gòu)造函數(shù)中添加上這些屬性:
Example DT4
[Copy to clipboard]CODE:
function Ball(message, specifiedName)
{
alert(message);
this.name=specifiedName;
}
var ball0=new Ball("creating new Ball", "Soccer Ball");
alert(ball0.name); // prints "Soccer Ball"
請(qǐng)記?。菏莕ew關(guān)鍵字最終使得構(gòu)造函數(shù)被執(zhí)行。在這個(gè)例子中,它將會(huì)運(yùn)行Ball("creating new Ball", "Soccer Ball");而關(guān)鍵字this將指向ball0。
因此,這行:this.name=specifiedName變成了ball0.name="Soccer Ball"。
它主要是說(shuō):給ball0添加屬性name,屬性值是Soccer Ball。
我們現(xiàn)在只是添加了一個(gè)name屬性給ball0,看起來(lái)和上一個(gè)例子中所做的很象,但卻是一個(gè)更好更具擴(kuò)展性的方法。現(xiàn)在,我們可以隨心所欲的創(chuàng)建許多帶有屬性的ball而無(wú)需我們手動(dòng)添加它們。而且,人們也希望創(chuàng)建的Ball對(duì)象能夠清晰的看懂它的構(gòu)造函數(shù)并且能夠輕松找出Ball的所有屬性。讓我們添加更多屬性到Ball里。
Example DT5
[Copy to clipboard]CODE:
function Ball(color, specifiedName, owner, weight)
{
this.name=specifiedName;
this.color=color;
this.owner=owner;
this.weight=weight;
}
var ball0=new Ball("black/white", "Soccer Ball", "John", 20);
var ball1=new Ball("gray", "Bowling Ball", "John", 30);
var ball2=new Ball("yellow", "Golf Ball", "John", 55);
var balloon=new Ball("red", "Balloon", "Pete", 10);
alert(ball0.name); // 輸出 "Soccer Ball"
alert(balloon.name); // 輸出 "Balloon"
alert(ball2.weight); // 輸出 "55"
嘿!使用面向?qū)ο笮g(shù)語(yǔ),你能夠說(shuō)Ball是一個(gè)擁有如下屬性的對(duì)象類型:name, color, owner, weight。
我們并沒(méi)被限制只能添加形如字符串或者數(shù)字之類的簡(jiǎn)單數(shù)據(jù)類型作為屬性。我們也能夠?qū)?duì)象賦給屬性。下面,supervisor是Employee的一個(gè)屬性.
Example DT6
[Copy to clipboard]CODE:
function Employee(name, salary, mySupervisor)
{
this.name=name;
this.salary=salary;
this.supervisor=mySupervisor;
}
var boss=new Employee("John", 200);
var manager=new Employee("Joan", 50, boss);
var teamLeader=new Employee("Rose", 50, boss);
alert(manager.supervisor.name+" is the supervisor of "+manager.name);
alert(manager.name+"\'s supervisor is "+manager.supervisor.name);
會(huì)輸出什么呢?
就像你在上面這個(gè)例子中看到的那樣,manager和teamLeader都有一個(gè)supervisor屬性,而這個(gè)屬性是類型Employee的一個(gè)對(duì)象。
任何類型的對(duì)象都可以作為一個(gè)屬性,回憶一下前面的Example 4(不是Example DT4),函數(shù)也是一個(gè)對(duì)象。所以你可以讓一個(gè)函數(shù)作為一個(gè)對(duì)象的一個(gè)屬性。下面,我將添加兩個(gè)函數(shù)getSalary和addSalary。
Example DT7
[Copy to clipboard]CODE:
function Employee(name, salary)
{
this.name=name;
this.salary=salary;
this.addSalary=addSalaryFunction;
this.getSalary=function()
{
return this.salary;
};
}
function addSalaryFunction(addition)
{
this.salary=this.salary+addition;
}
var boss=new Employee("John", 200000);
boss.addSalary(10000); // boss 長(zhǎng)了 10K 工資……為什么老板工資可以長(zhǎng)這么多:'(
alert(boss.getSalary()); // 輸出 210K……為什么默認(rèn)工資也那么高……:'(
addSalary和getSalary演示了幾種將函數(shù)賦給屬性的不同方法。如果你記得我們最開(kāi)始的討論;我討論了三種聲明函數(shù)的不同方式。所有那些在這里都是適用的,但是上面展示的兩個(gè)最常用。
讓我們看看有什么不同。下面,注意一下9-12行的代碼。當(dāng)這部分代碼執(zhí)行的時(shí)候,函數(shù)getSalary被聲明。如前面數(shù)次提到的,一個(gè)函數(shù)聲明的結(jié)果是一個(gè)對(duì)象被創(chuàng)建。所以這時(shí)候boss被創(chuàng)建(接下來(lái)的第19行),而boss里有一個(gè)getSalary屬性。
[Copy to clipboard]CODE:
function Employee(name, salary)
{
this.name=name;
this.salary=salary;
this.addSalary=addSalaryFunction;
this.getSalary=function()
{
return this.salary;
};
}
function addSalaryFunction(addition)
{
this.salary=this.salary+addition;
}
var boss=new Employee("John", 200000);
var boss2=new Employee("Joan", 200000);
var boss3=new Employee("Kim", 200000);
當(dāng)你創(chuàng)建這個(gè)對(duì)象的更多實(shí)例時(shí)(boss2和boss3),每一個(gè)實(shí)例都有一份getSalary代碼的單獨(dú)拷貝;而與此相反,addSalary則指向了同一個(gè)地方(即addSalaryFunction)。
看看下面的代碼來(lái)理解一下上面所描述的內(nèi)容。
Example DT8
[Copy to clipboard]CODE:
function Employee(name, salary)
{
this.name=name;
this.salary=salary;
this.addSalary=addSalaryFunction;
this.getSalary=function()
{
return this.salary;
};
}
function addSalaryFunction(addition)
{
this.salary=this.salary+addition;
}
var boss1=new Employee("John", 200000);
var boss2=new Employee("Joan", 200000);
// 給getSalary函數(shù)對(duì)象添加屬性
boss1.getSalary.owner="boss1";
boss2.getSalary.owner="boss2";
alert(boss1.getSalary.owner); // 輸出 "boss1"
alert(boss2.getSalary.owner); // 輸出 "boss2"
// 如果兩個(gè)對(duì)象指向同一個(gè)函數(shù)對(duì)象,那么
// 上面兩個(gè)輸出都應(yīng)該是“boss2”。
// 給addSalary函數(shù)對(duì)象添加屬性
boss1.addSalary.owner="boss1";
boss1.addSalary.owner="boss2";
alert(boss1.addSalary.owner); // 輸出 "boss2"
alert(boss2.addSalary.owner); // 輸出 "boss2"
// 因?yàn)閮蓚€(gè)對(duì)象都指向同一個(gè)函數(shù),(子烏注:原文寫(xiě)are not pointing to the same function,疑為筆誤)
// 當(dāng)修改其中一個(gè)的時(shí)候,會(huì)影響所有的實(shí)例(所以兩個(gè)都輸出“boss2”).
也許不是重要的事情,但這里有一些關(guān)于運(yùn)行類似上面的getSalary的內(nèi)嵌函數(shù)的結(jié)論: 1) 需要更多的存儲(chǔ)空間來(lái)存儲(chǔ)對(duì)象(因?yàn)槊恳粋€(gè)對(duì)象實(shí)例都會(huì)有它自己的getSalary代碼拷貝);2) javascript需要更多時(shí)間來(lái)構(gòu)造這個(gè)對(duì)象。
讓我們重新寫(xiě)這個(gè)示例來(lái)讓它更有效率些。
Example DT9
[Copy to clipboard]CODE:
function Employee(name, salary)
{
this.name=name;
this.salary=salary;
this.addSalary=addSalaryFunction;
this.getSalary=getSalaryFunction;
}
function getSalaryFunction()
{
return this.salary;
}
function addSalaryFunction(addition)
{
this.salary=this.salary+addition;
}
看這兒,兩個(gè)函數(shù)都指向同一個(gè)地方,這將會(huì)節(jié)約空間和縮短構(gòu)造時(shí)間(特別是當(dāng)你有一大堆內(nèi)嵌函數(shù)在一個(gè)構(gòu)造函數(shù)的時(shí)候)。這里有另外一個(gè)函數(shù)的功能能夠來(lái)提升這個(gè)設(shè)計(jì),它叫做prototype,而我們將在下一節(jié)討論它。
函數(shù):原型
每一個(gè)構(gòu)造函數(shù)都有一個(gè)屬性叫做原型(prototype,下面都不再翻譯,使用其原文)。這個(gè)屬性非常有用:為一個(gè)特定類聲明通用的變量或者函數(shù)。
你不需要顯式地聲明一個(gè)prototype屬性,因?yàn)樵诿恳粋€(gè)構(gòu)造函數(shù)中都有它的存在。你可以看看下面的例子:
Example PT1
[Copy to clipboard]CODE:
function Test()
{
}
alert(Test.prototype); // 輸出 "Object"
就如你在上面所看到的,prototype是一個(gè)對(duì)象,因此,你能夠給它添加屬性。你添加給prototype的屬性將會(huì)成為使用這個(gè)構(gòu)造函數(shù)創(chuàng)建的對(duì)象的通用屬性。
例如,我下面有一個(gè)數(shù)據(jù)類型Fish,我想讓所有的魚(yú)都有這些屬性:livesIn="water"和price=20;為了實(shí)現(xiàn)這個(gè),我可以給構(gòu)造函數(shù)Fish的prototype添加那些屬性。
Example PT2
[Copy to clipboard]CODE:
function Fish(name, color)
{
this.name=name;
this.color=color;
}
Fish.prototype.livesIn="water";
Fish.prototype.price=20;
接下來(lái)讓我們作幾條魚(yú):
[Copy to clipboard]CODE:
var fish1=new Fish("mackarel", "gray");
var fish2=new Fish("goldfish", "orange");
var fish3=new Fish("salmon", "white");
再來(lái)看看魚(yú)都有哪些屬性:
[Copy to clipboard]CODE:
for (var i=1; i<=3; i++)
{
var fish=eval("fish"+i); // 我只是取得指向這條魚(yú)的指針
alert(fish.name+","+fish.color+","+fish.livesIn+","+fish.price);
}
輸出應(yīng)該是:
[Copy to clipboard]CODE:
"mackarel, gray, water, 20"
"goldfish, orange, water, 20"
"salmon, white water, 20"
你看到所有的魚(yú)都有屬性livesIn和price,我們甚至都沒(méi)有為每一條不同的魚(yú)特別聲明這些屬性。這時(shí)因?yàn)楫?dāng)一個(gè)對(duì)象被創(chuàng)建時(shí),這個(gè)構(gòu)造函數(shù)將會(huì)把它的屬性prototype賦給新對(duì)象的內(nèi)部屬性__proto__。這個(gè)__proto__被這個(gè)對(duì)象用來(lái)查找它的屬性。
你也可以通過(guò)prototype來(lái)給所有對(duì)象添加共用的函數(shù)。這有一個(gè)好處:你不需要每次在構(gòu)造一個(gè)對(duì)象的時(shí)候創(chuàng)建并初始化這個(gè)函數(shù)。為了解釋這一點(diǎn),讓我們重新來(lái)看Example DT9并使用prototype來(lái)重寫(xiě)它:
Example PT3
[Copy to clipboard]CODE:
function Employee(name, salary)
{
this.name=name;
this.salary=salary;
}
Employee.prototype.getSalary=function getSalaryFunction()
{
return this.salary;
}
Employee.prototype.addSalary=function addSalaryFunction(addition)
{
this.salary=this.salary+addition;
}
我們可以象通常那樣創(chuàng)建對(duì)象:
[Copy to clipboard]CODE:
var boss1=new Employee("Joan", 200000);
var boss2=new Employee("Kim", 100000);
var boss3=new Employee("Sam", 150000);
并驗(yàn)證它:
[Copy to clipboard]CODE:
alert(boss1.getSalary()); // 輸出 200000
alert(boss2.getSalary()); // 輸出 100000
alert(boss3.getSalary()); // 輸出 150000
這里有一個(gè)圖示來(lái)說(shuō)明prototype是如何工作的。這個(gè)對(duì)象的每一個(gè)實(shí)例(boss1, boss2, boss3)都有一個(gè)內(nèi)部屬性叫做__proto__,這個(gè)屬性指向了它的構(gòu)造器(Employee)的屬性prototype。當(dāng)你執(zhí)行g(shù)etSalary或者addSalary的時(shí)候,這個(gè)對(duì)象會(huì)在它的__proto__找到并執(zhí)行這個(gè)代碼。注意這點(diǎn):這里并沒(méi)有代碼的復(fù)制(和Example DT8的圖表作一下對(duì)比)。
相關(guān)文章
簡(jiǎn)介JavaScript中valueOf()方法的使用
這篇文章主要介紹了簡(jiǎn)介JavaScript中valueOf()方法的使用,是JS入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-06-06JavaScript學(xué)習(xí)筆記之JS事件對(duì)象
本文介紹了javascript的事件對(duì)象的相關(guān)資料,需要的朋友可以參考下2015-01-01Javascript入門學(xué)習(xí)第九篇 Javascript DOM 總結(jié)
作為一個(gè)js-DOM開(kāi)發(fā)者,你必須知道的一些DOM方法:2008-07-07深入理解JavaScript系列(17):面向?qū)ο缶幊讨耪撛敿?xì)介紹
這篇文章主要介紹了深入理解JavaScript系列(17):面向?qū)ο缶幊讨耪撛敿?xì)介紹,本文講解了概論、范式與思想、基于類特性和基于原型、基于靜態(tài)類、層次繼承等內(nèi)容,需要的朋友可以參考下2015-03-03JavaScript for循環(huán) if判斷語(yǔ)句(學(xué)習(xí)筆記)
下面小編就為大家?guī)?lái)一篇JavaScript for循環(huán) if判斷語(yǔ)句(學(xué)習(xí)筆記)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-10-10