常用寫法:
function add(a,b) { return a + b; } alert(add(1,2)); // 結果 3
當我們這麼定義函數的時候,函數內容會被編譯(但不會立即執行,除非我們去調用它)。而且,也許你不知道,當這個函數創建的時候有一個同名的對象也被創建。就我們的例子來說,我們現在有一個對象叫做“add”(要更深入了解,看底下函數:對象節。)
匿名函數:
我們也可以通過指派一個變量名給匿名函數的方式來定義它。
var add = function(a,b) { return a + b; } alert(add(1,2)); // 結果 3
這個代碼和前一個例子做了同樣的事情。也許語法看起來比較奇怪,但它應該更能讓你感覺到函數是一個對象,而且我們只是為這個對象指派了一個名稱。可以把它看做和 var myVar=[1,2,3]一樣的語句。以這種方式聲明的函數內容也一樣會被編譯。
當我們指派一個這樣的函數的時候,我們並不一定要求必須是匿名函數。在這裡,我作了和以上一樣的事情,但我加了函數名“theAdd”,而且我可以通過調用函數名或者是那個變量來引用函數。
var add = function theAdd(a,b) { return a + b; } alert(add(1,2)); // 結果 3 alert(theAdd(1,2)); // 結果也是 3
使用這種方式來定義函數在面向對象編程中是很有用的,因為我們能像底下這樣使一個函數成為一個對象的屬性。
var myObject = new Object(); myObject.add = function(a,b){return a+b}; // myObject 現在有一個叫做“add”的屬性(或方法)” // 而且我能夠象下面這樣使用它 myObject.add(1, 2);
函數:對象
函數是javascript中的一種特殊形式的對象。它是第一個[b〕類數據類型(classdata type)。這意味著我們能夠給它增加屬性。這裡有一些需要注意的有趣觀點
對象的創建
就像剛才提及的,當我們定義一個函數時,javascript實際上在後台為你創建了一個對象。這個對象的名稱就是函數名本身。這個對象的類型是function。在下面的例子,我們也許不會意識到這一點,但我們實際上已經創建了一個對象:它叫做Ball。
function ball() // 也許看起來有點奇怪,但是這個聲明 { // 創建了一個叫做Ball的對象 i = 1; } alert(typeof ball); // 結果 "function"
我們甚至能將這個對象的內容打印出來而且它會輸出這個函數的實際代碼
alert(ball); //結果為: //function ball() //{ // i = 1; //}
屬性的添加
我們能夠給Object添加屬性,包括對象function。因為定義一個函數的實質是創建一個對象。我們能夠“暗地裡”給函數添加屬性。比如,我們這裡定義了函數Ball,並添加屬性callsign。
function Ball() // 也許看起來有點奇怪,但是這個聲明創建了一個叫做Ball的對象,而且你能夠引用它或者象下面那樣給它增加屬性 { } ball.callsign="The ball"; // 給Ball增加屬性 alert(ball.callsign); // 輸出 "The ball"
指針
因為function是一個對象,我們能夠為一個function分配一個指針。如下例,變量ptr指向了對象myFunction。
function myFunction(message) { alert(message); } var ptr=myFunction; // ptr指向了myFunction ptr("hello"); // 這句會執行myFunction:輸出"hello"
我們能夠運行這個函數,就好像這個函數名已經被指針名代替了一樣。所以在上面,這行ptr("hello"); 和myFunction("hello");的意義是一樣的。
指向函數的指針在面向對象編程中相當有用。例如:當我們有多個對象指向同一個函數的時候(如下):
function sayName(name) { alert(name); } var object1=new Object(); // 創建三個對象 var object2=new Object(); var object3=new Object(); object1.sayMyName=sayName; // 將這個函數指派給所有對象 object2.sayMyName=sayName; object3.sayMyName=sayName; object1.sayMyName("object1"); // 輸出 "object1" object2.sayMyName("object2"); // 輸出 "object2" object3.sayMyName("object3"); // 輸出 "object3"
因為只有指針被保存(而不是函數本身),當我們改變函數對象自身的時候,所有指向那個函數的指針都會發生變化。我們能夠在底下看到:
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();
指針的指向
我們能夠在一個函數創建之後重新分配它,但是我們需要指向函數對象本身,而不是指向它的指針。在下例中,我將改變myfunction()的內容。
function myFunction() { alert("Old"); } myFunction(); // 輸出 "Old" myFunction=function() { alert("New"); }; myFunction(); // 輸出 "New"
舊函數哪裡去了??被拋棄了。
如果我們需要保留它,我們可以在改變它之前給它分配一個指針。
function myFunction() { alert("Old"); } var savedFuncion=myFunction; myFunction=function() { alert("New"); }; myFunction(); // 輸出 "New" savedFuncion(); // 輸出 "Old"
內嵌函數
function get(a,b,c) { function cal(n) { return n/2; } var result = “”; result+=cal(a)+” ”; result+=cal(b)+” ”; result+=cal(c); } var resultString = get(10,20,30); alert(resultString); // 輸出 "5 10 15"
你只能在內部調用嵌套的函數。就是說,你不能這麼調用:
getHalfOf.calculate(10),因為calculate只有當外部函數(getHalfOf())在運行的時候才會存在。這和我們前面的討論一致(函數會被編譯,但只有當你去調用它的時候才會執行)。
調用哪個函數?
你也許正在想命名沖突的問題。比如,下面哪一個叫做calculate的函數會被調用?
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); } var resultString=getHalfOf(10,20,30); alert(resultString); // 輸出 "5 10 15"
在這個例子中,編譯器會首先搜索局部內存地址,所以它會使用內嵌的calculate函數。如果我們刪除了這個內嵌(局部)的calculate函數,這個代碼會使用全局的calculate函數。
函數:數據類型及構造函數
讓我們來看看函數的另一個特殊功能--這讓它和其它對象類型截然不同。一個函數能夠用來作為一個數據類型的藍圖。這個特性通常被用在面向對象編程中來模擬用戶自定義數據類型(user defined data type)。使用用戶自定義數據類型創建的對象通常被成為用戶自定義對象(user defined object)。
數據類型
在定義了一個函數之後,我們也同時創建了一個新的數據類型。這個數據類型能夠用來創建一個新對象。下例,我創建了一個叫做Ball的新數據類型。
function Ball() { } var ball0=new Ball(); // ball0 現在指向一個新對象 alert(ball0); // 輸出 "Object",因為 ball0 現在是一個對象
這樣看來,ball0=new Ball()作了什麼?new關鍵字創建了一個類型是Object的新對象(叫做ball0)。然後它會執行Ball(),並將這個引用傳給ball0(用於調用對象)。下面,你會看到這條消息:“creating new Ball”,如果Ball()實際上被運行的話。
function Ball(message) { alert(message); } var ball0=new Ball("creating new Ball"); // 創建對象並輸出消息 ball0.name="ball-0"; // ball0現在有一個屬性:name alert(ball0.name); // 輸出 "ball-0"
我們可以把上面這段代碼的第6行看做是底下的代碼6-8行的一個簡寫:
function Ball(message) { alert(message); } var ball0=new Object(); ball0.construct=Ball; ball0.construct("creating new ball"); // 執行 ball0.Ball ("creating.."); ball0.name="ball-0"; alert(ball0.name);
這行代碼ball0.construct=Ball和以上中的ptr=myFunction語法一致。
添加屬性
當我們象上面那樣使用關鍵字new創建一個對象的時候,一個新的Object被創建了。我們可以在創建之後給這個對象添加屬性(就好像我在上面那樣添加屬性name。而接下來的問題就是如果我們創建了這個對象的另外一個實例,我們得象下面那樣再次給這個新對象添加這個屬性。)
function Ball() { } var ball0=new Ball(); // ball0 現在指向了類型Ball的一個新實例 ball0.name="ball-0"; // ball0 現在有一個屬性"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了,如果在正式的程序中這也許會引發問題。有什麼好辦法可以自動增加屬性呢?嗯,有一個:使用this關鍵字。this這個詞在function中有特別的意義。它指向了調用函數的那個對象。讓我們看看下面的另一個示例,這時候我們在構造函數中添加上這些屬性:
function Ball(message, specifiedName) { alert(message); this.name=specifiedName; } var ball0=new Ball("creating new Ball", "Soccer Ball"); alert(ball0.name); // prints "Soccer Ball"
請記住:是new關鍵字最終使得構造函數被執行。在這個例子中,它將會運行Ball("creating new Ball", "Soccer Ball");而關鍵字this將指向ball0。
因此,這行:this.name=specifiedName變成了ball0.name="Soccer Ball"。它主要是說:給ball0添加屬性name,屬性值是Soccer Ball。
我們現在只是添加了一個name屬性給ball0,看起來和上一個例子中所做的很象,但卻是一個更好更具擴展性的方法。現在,我們可以隨心所欲的創建許多帶有屬性的ball而無需我們手動添加它們。而且,人們也希望創建的Ball對象能夠清晰的看懂它的構造函數並且能夠輕松找出Ball的所有屬性。讓我們添加更多屬性到Ball裡。
function Ball(color, specifiedName, owner, weight) { this.name=specifiedName; this.color=color; this.owner=owner; this.weight=weigth; } 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"
嘿!使用面向對象術語,你能夠說Ball是一個擁有如下屬性的對象類型:name,color, owner, weight。
將對象賦給屬性我們並沒被限制只能添加形如字符串或者數字之類的簡單數據類型作為屬性。我們也能夠將對象賦給屬性。下面,supervisor是Employee的一個屬性.
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);
函數也是一個對象。所以你可以讓一個函數作為一個對象的一個屬性。下面,我將添加兩個函數getSalary和addSalary。
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 長了 10K 工資……為什麼老板工資可以長這麼多:'( alert(boss.getSalary()); // 輸出 210K……為什麼默認工資也那麼高……:'( addSalary和getSalary演示了幾種將函數賦給屬性的不同方法。 如前面數次提到的,一個函數聲明的結果是一個對象 被創建。 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);
當你創建這個對象的更多實例時(boss2和boss3),每一個實例都有一份getSalary代碼的單獨拷貝;而與此相反,addSalary則指向了同一個地方(即addSalaryFunction)。
看看下面的代碼來理解一下上面所描述的內容。
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函數對象添加屬性 boss1.getSalary.owner="boss1"; boss2.getSalary.owner="boss2"; alert(boss1.getSalary.owner); // 輸出 "boss1" alert(boss2.getSalary.owner); // 輸出 "boss2" // 如果兩個對象指向同一個函數對象,那麼 上面兩個輸出都應該是“boss2”。 // 給addSalary函數對象添加屬性 boss1.addSalary.owner="boss1"; boss1.addSalary.owner="boss2"; alert(boss1.addSalary.owner); // 輸出 "boss2" alert(boss2.addSalary.owner); // 輸出 "boss2" // 因為兩個對象都指向同一個函數 // 當修改其中一個的時候,會影響所有的實例(所以兩個都輸出“boss2”).
也許不是重要的事情,但這裡有一些關於運行類似上面的getSalary的內嵌函數的結論:
1) 需要更多的存儲空間來存儲對象(因為每一個對象實例都會有它自己的getSalary代碼拷貝);
2) javascript需要更多時間來構造這個對象。
讓我們重新寫這個示例來讓它更有效率些。
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; }
看這兒,兩個函數都指向同一個地方,這將會節約空間和縮短構造時間(特別是當你有一大堆內嵌函數在一個構造函數的時候)。這裡有另外一個函數的功能能夠來提升這個設計,它叫做prototype,而我們將在下一節討論它。
函數:原型
每一個構造函數都有一個屬性叫做原型(prototype,下面都不再翻譯,使用其原文)。這個屬性非常有用:為一個特定類聲明通用的變量或者函數。
prototype的定義
你不需要顯式地聲明一個prototype屬性,因為在每一個構造函數中都有它的存在。你可以看看下面的例子:
function Test() { } alert(Test.prototype); // 輸出 "Object"
給prototype添加屬性
就如你在上面所看到的,prototype是一個對象,因此,你能夠給它添加屬性。你添加給prototype的屬性將會成為使用這個構造函數創建的對象的通用屬性。
例如,我下面有一個數據類型Fish,我想讓所有的魚都有這些屬性:
livesIn="water"和price=20;為了實現這個,我可以給構造函數Fish的prototype添加那些屬性。
function Fish(name, color) { this.name=name; this.color=color; } Fish.prototype.livesIn="water"; Fish.prototype.price=20;
接下來讓我們作幾條魚:
var fish1=new Fish("mackarel", "gray"); var fish2=new Fish("goldfish", "orange"); var fish3=new Fish("salmon", "white");
再來看看魚都有哪些屬性:
for (int i=1; i<=3; i++) { var fish=eval("fish"+i); // 我只是取得指向這條魚的指針 alert(fish.name+","+fish.color+","+fish.livesIn+","+fish.price); }
輸出應該是:
"mackarel, gray, water, 20" "goldfish, orange, water, 20" "salmon, white water, 20"
你看到所有的魚都有屬性livesIn和price,我們甚至都沒有為每一條不同的魚特別聲明這些屬性。這時因為當一個對象被創建時,這個構造函數將會把它的屬性prototype賦給新對象的內部屬性__proto__。這個__proto__被這個對象用來查找它的屬性。
你也可以通過prototype來給所有對象添加共用的函數。這有一個好處:你不需要每次在構造一個對象的時候創建並初始化這個函數。
用prototype給對象添加函數
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; }
我們可以象通常那樣創建對象:
var boss1=new Employee("Joan", 200000); var boss2=new Employee("Kim", 100000); var boss3=new Employee("Sam", 150000);
並驗證它:
alert(boss1.getSalary()); // 輸出 200000 alert(boss2.getSalary()); // 輸出 100000 alert(boss3.getSalary()); // 輸出 150000
這裡有一個圖示來說明prototype是如何工作的。這個對象的每一個實例(boss1, boss2, boss3)都有一個內部屬性叫做__proto__,這個屬性指向了它的構造器 (Employee)的屬性prototype。當你執行getSalary或者addSalary的時候,這個對象會在它的__proto__找到並執行這個代碼。
以上這篇關於js函數解釋(包括內嵌,對象等) 就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支持。