JavaScript中的函數 1. 函數的定義 兩種定義形式: 通過函數定義表達式來定義 通過函數聲明語句來定義 通過函數定義表達式來定義 通過函數聲明語句來定義 函數聲明語句定義一個函數 函數定義表達式定義一個函數 tips:以表達式方式定義的函數(特別適合用來定義那些只會用到一次的函數),函數名 ...
JavaScript中的函數
1. 函數的定義
- 兩種定義形式:
- 通過函數定義表達式來定義
- 通過函數聲明語句來定義
函數聲明語句定義一個函數
//計算階乘的遞歸函數
function factorial(x){
if (x<=1) return 1;
return x*factotial(x-1);
}
函數定義表達式定義一個函數
var s= function sum(x,y){
return x+y;
}
tips:以表達式方式定義的函數(特別適合用來定義那些只會用到一次的函數),函數名是可選的,
也就可以直接寫成這樣
var s= function(x,y){
return x+y;
}
2.函數命名
- 函數的名稱通常是動詞或者以動詞為首碼的片語 如:funciton saveMessage(){}
- 函數名的第一個字元通常為小寫
- 當我們命名的函數名比較長時,一種是駝峰式命名 如:function readSystemFile(){}
另一種是以下劃線分割單詞,如:function make_products_iPhones(){} - 有一些函數是用作內部函數或者私有函數的,通常以一條下劃線為首碼。 如: _login()
3.函數的調用
- 普通的函數調用
example:
printprops({x:1})
var total = distance(0,1,2)+ distance(3,4,5)
var probability = factorial(5)/factoral(10)
- 對象方法的調用
概念:如果函數表達式是一個屬性訪問表達式(即該函數是一個對象的屬性或者數組的一個函數),
那麼該調用表達式就是一個方法調用表用表達式。
example:
//定義一個對象
var person = {
name: lihua ,
age: 18 ,
sex: 女 ,
send: function(){//返回 person的name、age、sex
this.message = this.name + this.age + this.age;
}
};
person.send();//這條語句就是函數的方法調用
person.message; //得到name、age、sex相關信息
tips:
1.函數返回的值倘若是因為解釋器到達結尾,返回值就是undefined;倘若函數返回是因為解釋器執行到一條return語句,
則返回return語句後面的值;如果return語句後面沒有值則返回undefined。
2.在ECMAScript3和在非嚴格模式下的ECMAScript5的函數調用規定中,this的值(調用上下文)的值是全局對象,但是在
嚴格模式下this的值卻是undefined,因此可也用this來判斷當前環境是否為嚴格模式,此外ECMAScript2015 (簡稱es6)嚴格模式請參考以下地址。
ECMAScipt的第六個版本 https://developer.mozilla.org/zh-CN/
3.方法調用和普通的函數調用的一個重要區別就是:調用上下文(即this的值),通常this關鍵字指向成為調用上下文的對象,
屬性訪問表達式:對象名.方法名(通常使用.運算符訪問屬性)或者"[]"進行屬性訪問操作。(this關鍵字的相關內容是十分重要的)
更多this相關內容請參看此鏈接
- 構造函數的調用
構造函數就是用來初始化先創建的對象,通常使用關鍵字new來調用構造函數,當使用new關鍵字來調用構造函數的時候就會自動
創建一個新的的空對象,而構造函數只需要初始化這個新對象的狀態(屬性和方法),調用構造函數的話,新的對象的原型(prototype)等於
構造函數的原型(prototype)屬性,由此引出一個特性:通過同一個構造函數創建的所有對象都繼承同一個相同的對象。
凡是沒有形參的構造函數都可以省略圓括弧,以下兩行代碼是等價的。
var fn = new Object();
var fn = new Object;
tips:
1.構造函數通常不使用return關鍵字,進行初始化新對象,執行完函數體,就調用構造函數表達式的計算結果作為新對象的值,顯示返回。
2.倘若構造函數使用return語句返回一個對象,那麼調用表達式的值就是這個對象,而不是this指向的對象。
3.構造函數里沒有顯式調用return時,預設是返回this對象,也就是新創建的實例對象。
4.return的是五種簡單數據類型:String,Number,Boolean,Null,Undefined。這種情況下,忽視return值,依然返回this對象
- 間接調用
JavaScript中的函數也是對象,所以函數對象也可以包含方法。函數的間接調用用到的call()和apply()方法。
這兩個方法都能顯示指定調用所需的this的值,這就引出一個特性:任何函數都可以作為對象的方法來調用,
哪怕這個函數不是那個對象的方法。(這也就是在實際開發中我們也會常用這兩個方法的原因之一)
1.call()方法調用一個函數, 其具有一個指定的this值和分別地提供的參數(參數的列表) this的值並不一定就是該函數執行真正的this值,在非嚴格模式下,倘若this的值指向null和undefined的話
會自動指向全局對象(瀏覽器中就是window對象),同時值為原始值(數字,字元串,布爾值)的this會指向該原始值的自動包裝對象。
2.apply()方法調用一個具有給定this值的函數,以及作為一個數組(或類似數組對象)提供的參數。
為this指定一個對象,使用apply時只需要寫一次這個方法然後再另一個對象中繼承它,繼而不用在這個新的對象重新來寫它。
tips:call()方法的作用和 apply() 方法類似,區別就是call()方法接受的是參數列表,而apply()方法接受的是一個參數數組。
4.函數中的形參與實參
- 可選的形參
- 可變長的實參列表:實參對象 arguments 它是一個類數組對象,通過數字下表就能夠訪問傳入函數的實參值,它包含length屬性,讓函數可以操作任意數量的實參。
- callee和caller屬性
callee屬性指代當前正在執行的函數,caller指代調用當前正在執行的函數的函數,它可以訪問棧。而callee可以來遞歸調用自身。
5.函數的閉包(!important)
- 概念:通俗地講函數的閉包就是在一個函數內部定義另一個函數,而這個內部的函數(子函數)可以調用包裹它的函數(父函數等、"爺爺、太爺爺...")的變數。
也可以認為閉包就是能夠讀取其他函數內部變數的函數。 - 變數作用域:
全局變數:任何函數內部都可以訪問全局作用域
局部變數:在函數外部無法讀取變數
tips
javascript是沒有像Java、C++那樣用一對“{}”括起來的塊級作用域,但是在es6中可以使用let關鍵字實現塊級作用域。
- 詞法作用域:變數的作用域是在定義時決定而不是執行時決定,也就是說詞法作用域取決於源碼,通過靜態分析就能確定,因此詞法作用域也叫做靜態作用域。
with和eval除外,所以只能說JS的作用域機制非常接近詞法作用域(Lexical scope)。
var scope = "global scope";
function checkScope(){
var scope = "local scope";
function f(){return scope;}
return f();
}
checkScope(); //輸出可以得到 local scope
-
閉包的用途
i. 讀取函數內部的變數、函數嵌套函數
ii. 讓這些變數的值始終保持在記憶體中(全局變數的值不會在被函數調用過後自動清除,由GC回收)
iii. 避免全局變數的污染、讓私有成員存在 -
閉包的註意事項
i. 由於閉包會使得函數中的變數都被保存在記憶體中,記憶體消耗很大,所以不能濫用閉包,否則會造成網頁的性能問題,在IE中可能導致記憶體泄露。
解決方法是,在退出函數之前,將不使用的局部變數全部刪除。
ii. 閉包會在父函數外部,改變父函數內部變數的值。所以,如果你把父函數當作對象(object)使用,把閉包當作它的公用方法(Public Method),
把內部變數當作它的私有屬性(private value),這時一定要小心,不要隨便改變父函數內部變數的值。
以下例子來檢驗自己是否已經掌握了閉包的運行機制。
example 0ne:
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
return function(){
return this.name;
};
}
};
alert(object.getNameFunc()());
example Two:
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
var that = this;
return function(){
return that.name;
};
}
};
alert(object.getNameFunc()());
6.函數的屬性、方法以及構造函數
- length屬性
argument.length屬性表示傳入函數的實參的個數,而函數本身的length屬性則有不同含義,它表示函數定義時的實際形參個數。 - prototype(原型)屬性
這個屬性指向對象的引用,而這個對象就被稱為原型對象(prototype object),每個函數都包含這個屬性,都包含不同的原型對象;
在JavaScript中每個函數都有一個特殊的屬性叫作原型(prototype)
function doSomething(){}
console.log(doSomething.prototype);
-----------------------------------
結果: 這就是 原型對象
{
constructor: ƒ doSomething(), //構造函數
/* 這些又是這個構造函數裡面的方法或者屬性
arguments: null
caller: null
length: 0
name: "doSomething"
prototype: {constructor: ƒ} 構造函數的原型屬性
__proto__: ƒ ()*/
__proto__: { //原型屬性
constructor: ƒ Object(),
hasOwnProperty: ƒ hasOwnProperty(),
isPrototypeOf: ƒ isPrototypeOf(),
propertyIsEnumerable: ƒ propertyIsEnumerable(),
toLocaleString: ƒ toLocaleString(),
toString: ƒ toString(),
valueOf: ƒ valueOf()
}
}
-------------------------------
function doSomething(){}
doSomething.prototype.eat = "food" //doSomething函數的原型(prototype)屬性對象添加eat的屬性
var doSomeInstancing =new doSomething() //創建doSomething函數的實例 通過new關鍵字來調用該函數,它返回這個函數的實例化對象給doSomeInstancing
doSomeInstancing.prop = "add value"; //給對象doSomeInstancing添加一個“name為prop值為add value”的屬性
console.log(doSomeInstancing); //輸出這個doSomeIntancing對象 結果現實這個對象有兩個屬性,一個是:prop: "add value" 另一個是:__proto__: Object
console.log(doSomething.prototype); //輸出doSomething.prototype的值是一個對象{eat: "food",constructor: ƒ doSomething(), __proto__: Object
顯然doSomeInstancing.__proto__屬性與doSomething.prototype(構造函數的原型屬性的值是相同)它們的值是相等的。(可以通過===來判斷 __ptototype__為隱式原型)
- call()、apply()和bind()方法 在前面的函數的間接調用中已經介紹了call()以及apply(),在這裡就不再敘說,就詳細介紹ECMAScript5新增的bind()方法
bind()方法中的bind翻譯過來就是捆綁、綁定之意,作用就是將函數綁定至某個對象中,倘若一個函數調用了bind()方法並傳入一個對象作為參數,那麼這個方法將返回新的函數。
function f(y){return this.x+y;} //待綁定的函數
var o = {x :1};//將要被綁定的對象
var g = f.bind(o); //將函數f綁定至對象o 相當於var o={x:1, f:function f(y){return this.x+y;}}
console.log(g(2)); //通過g(y)調用o.f(y) 輸出3
bind()方法除了除了第一個實參之外,傳入bind()的實參也會綁定至this.這種應用是一種常見的函數式編程技術,被稱為柯里化(currying)
var sum = function(x,y){return x+y;}
var succ = sum.bind(null,1);
succ(2)
- toString()方法
該方法返回字元串 - Function()構造函數
- 高階函數
- 記憶(memorization)