layout: post
title: JavaScript函數(shù)
tags: [JavaScript, 函數(shù)]
author: 楊比軒
函數(shù)定義和調用
簡單的函數(shù)定義
//求絕對值
function abs(x){
if(x < 0)
return -x;
else
return x;
}
和java語法類似:
-
function
函數(shù)關鍵字申明 -
abs
函數(shù)名 -
(x)
參數(shù) -
{....}
函數(shù)體
JS如果沒有return語句袜漩,函數(shù)執(zhí)行完畢會返回
undefined
本著萬物皆對象的原則玷氏,function也是對象统求,所以:
var abs = function(x){
....
};
此時函數(shù)是一個匿名函數(shù)仪芒,變量abs指向匿名函數(shù)爷恳,本質上和上面那種方式沒有區(qū)別尔邓,但是這里有一點要注意付秕,此處其實是一個賦值語句兰珍,所以末尾要加上;
函數(shù)的調用
正常調用不做贅述,說說比較特殊的地方询吴。
JS函數(shù)允許傳遞多個參數(shù)或者不傳遞參數(shù)掠河,所以:
abs(1,2,“123”,4,5);//后面的參數(shù)不會生效,結果返回 1
abs(); //結果返回 NaN
返回NaN解釋:函數(shù)傳遞的x變成
undefined
汰寓,計算結果變成NaN
所以口柳,為了避免這種情況的產生,可以做健壯性處理:
function abs(x){
if(x !== 'number'){
throw 'not a number';
}
if(x > 0)
return -x;
else
return x;
}
arguments
JS的函數(shù)默認會有一個arguments
參數(shù)有滑,此參數(shù)指向函數(shù)的參數(shù)集跃闹,有點類似于array,但是不是array毛好。
可以使用arguments的length屬性望艺,來判斷參數(shù)的個數(shù)和對參數(shù)進行訪問
demo:
function getSum(x,y,z){
var num = 0;
console.log("arguments length:" + arguments.callee);
for(var i = 0 ; i < arguments.length ; i++){
console.log(arguments[i]);
num = num + arguments[i];
}
return num;
}
console.log(getSum(1,2,3,4,5,6,7,8,9,10));
//輸出:1,2,3,4,5,6,7,8,9,10,55
rest
ES6引入的新標準肌访,用于處理多余傳入函數(shù)內的參數(shù)找默,用法:
function restDemo(x, y, ...rest){
console.log("x="+x);
console.log("y="+y);
console.log("rest=" + rest);
}
restDemo(1,2,3,4,5,6);
//輸出:
//x=1
//y=2
//rest=3,4,5,6
關于return
JS有自動在行末加;
的機制,所以要小心return語句
demo:
//return了一個匿名對象吼驶,這種是常規(guī)情況惩激,可以正常運行
function returnDemo(){
return {name:'luke'};
}
//return的代碼過長時,需要換行蟹演,此時js會自動加`;`在行末风钻,所以返回undefined
function returnDemo(){
return
{ name:'luke'};
}
//這種的也正常,{ 表示此行未結束酒请,會阻斷添加 ;
function returnDemo(){
return {
name:'luke'
};
}
變量的作用域
JS的函數(shù)可以嵌套骡技,所有當兩個嵌套的函數(shù)的變量重名后,內部可以調用外部變量羞反,外部則不能訪問內部布朦。
'use strict';
function foo() {
var x = 1;
function bar() {
var y = x + 1; // bar可以訪問foo的變量x!
}
var z = y + 1; // ReferenceError! foo不可以訪問bar的變量y!
此時如果兩個參數(shù)名字相同,則內部參數(shù)相當于重寫了外部參數(shù)昼窗,會達到屏蔽的效果是趴。
'use strict';
function foo() {
var x = 1;
function bar() {
var x = 'A';
console.log('x in bar() = ' + x); // 'A'
}
console.log('x in foo() = ' + x); // 1
bar();
}
變量的自動提升
在JS中可以引用稍后申明的變量而不會引發(fā)異常就是因為有自動變量提升(variable hoisting),但是提升也會帶來一些問題膏秫。
console.log(x === undefined);//logs true
var x = 3;
//上述代碼相當于
var x;
console.log(x === undefined);
x = 3;
分析:
var x = 3;
相當于一個申明變量語句和一個賦值語句右遭,但是自動提升只會提升申明語句var x;
僅此而已做盅。所以log輸出就是true
所以,一段代碼或者函數(shù)中的var語句應該盡可能的放在接近代碼段的頂部位置,一避免產生麻煩窘哈。
全局變量
全局變量相當于全局對象的屬性吹榴,在網頁中全局對象是window
,所以可是使用window.variable
來訪問和設置全局變量滚婉。
所以在函數(shù)體之用var
或者const
申明的變量既是全局變量图筹,也是window
對象的屬性,让腹。
無論是function還是var聲明的變量远剩,在js中都是變量,也就是window的屬性骇窍,當前前提是function和var都全局的瓜晤。
局部變量
在for
或者if
中聲明的變量其實在{}
也可以引用,因為js的變量作用域其實是函數(shù)內部腹纳,而for
等不是函數(shù)痢掠。
ES6引入了新標準,let
關鍵字可以替代var
可以申明塊級的作用域的變量嘲恍。
{
let abc = "qweqweqweqweqweqweq";
}
console.log(abc);//logs:Uncaught ReferenceError: abc is not defined(…)
常量
ES6添加的新特性足画,const
關鍵字申明一個固定值的常量。
命名空間
javascript實際上只有一個全局作用域佃牛。任何變量如果在當前函數(shù)作用域中找到淹辞,就會繼續(xù)往上找(上指的是父類),最后再全局作用中也沒有找到俘侠,就匯報ReferenceError錯誤象缀。
全局變量會綁定到window
上,不同的js文件如果使用了相同的全局變量爷速,就會造成明明沖突攻冷,并且難以發(fā)現(xiàn)。
避免這個問題的方法就是把所有的變量和函數(shù)全部綁定到一個全局變量中遍希,如:
//全局變量
var MMP = {};
//其它變量
MMP.a='a';
MMP.b = function(){
console.log("this is function");
}
這樣就可以將需要的變量申明在MMP
下。
對象方法
在對象內部定義的函數(shù)也叫方法里烦,和普通的函數(shù)其實沒啥區(qū)別凿蒜,但是此種方法可以使用this
關鍵字來指向當前調用方法的對象。
var person = {
name:"bixuan",
age:23
//此方法就是返回對象的age屬性的value
getAge:function(){
return this.age;
}
}
如果此時getAge()
方法寫在person對象之外時胁黑,當person.getAge()
種調用時废封,可以獲得和上面一樣的結果,但是要是單獨的調用getAge()
var person = {
name:"bixuan",
age:23,
getage:getAge
}
function getAge(){
return this.age;
}
person.getage();// 結果就是23
getAge();//結果是 undefined
person.getage
指向的就是函數(shù)getAge()
丧蘸,函數(shù)中的this
指向的就是person
對象漂洋,所以返回的就是23,而直接運行getAge()
時this
指向的是window
對象,因為沒有age
屬性刽漂,所以結果是undefined
演训。
還有一種情況就是函數(shù)的多層嵌套,也會導致this的調用出現(xiàn)問題贝咙。
var person = {
name:"bixuan",
age:23,
getage:function(){
function getAge(){
return this.age;
}
}
}
person.getage();//結果是 undefined
原因就是this
又重新指向了undefined
了样悟,當然如果處于非strict
模式下,thi又會指向window
庭猩。為了避免這種情況的發(fā)生窟她,可以在外層加上一句:
...
var that = this; //用that來存儲this變量,供內層調用
function getAge(){
return that.age;
}
...
apply()
函數(shù)對象都會有一個apply()
方法蔼水,此方法接收兩個參數(shù)震糖,一個是需要綁定的this對象,第二個參數(shù)是一個Array
,存儲的是函數(shù)本來的參數(shù)趴腋。
function sum(x, y){
console.log(this.age);
console.log( x + y);
}
sum.apply({age:23},[1,2]);
//輸出 23,3
call()
和apply類似吊说,不同的是call
第二個參數(shù)不是array,而是按順序將原函數(shù)所有的參數(shù)順序傳入于样。`call({},參數(shù)1,參數(shù)2,...)
利用apply
的這個特性疏叨,可以用來實現(xiàn)裝飾器。
裝飾器:對已有的函數(shù)方法進行裝飾穿剖,在實現(xiàn)原有功能的基礎上蚤蔓,添加額外的功能,或者增強原有功能糊余。
function pri(){
console.log("this is a demo");
}
var count = 0;
(function newPro(){
count++;
pri.apply(null,[]);
})();
上述例子就實現(xiàn)了對函數(shù)pri
的裝飾秀又,在執(zhí)行原有代碼的同時,對其運行的次數(shù)進行了統(tǒng)計贬芥。
高階函數(shù)
普通函數(shù)接受的參數(shù)都是數(shù)據(jù)類型吐辙,高階函數(shù)就是把函數(shù)對象當作參數(shù)進行傳遞。
function sum(x, y){
return x + y;
}
function useSum(x, y, sum){
return sum(x, y);
}
- map/reduce
map()
方法相當于對arr
中的數(shù)據(jù)按照傳入的參數(shù)函數(shù)進行一次迭代蘸劈。
function pow(x){
return x*x;
}
var arr = [1, 2, 3, 4, 5];
arr.map(pow);// [1,2,9,16,25]
reduce()
其實也是對arr
中的數(shù)據(jù)進行迭代昏苏,但是和map
不同的是,reduce()
相當于嵌套迭代威沫。
需要注意的是:
reduce()
必須接收兩個參數(shù)的函數(shù)
function sum(x, y){
return x + y;
}
var arr = [1, 2, 3, 4, 5];
arr.reduce(sum);//15
**2. filter **
不知道官方的叫法是什么贤惯,應該翻譯做過濾器吧。使用arr
調用棒掠,并傳入返回true/false
來對arr中的元素進行判定從而進行過濾孵构。
var arr = [1,2,3,4,5];
var newArr = arr.filter(function(x){
if(x%2 === 0){
return true;
}else{
return false;
}
})
console.log(arr);//[1,2,3,4,5]
console.log(newArr);//[2,4]
filter()
不會更改調用數(shù)組的值,而是返回一個新的arr
對象烟很。
回調函數(shù)
filter
接受回調函數(shù)颈墅。
- 當回調函數(shù)一個參數(shù)時蜡镶,代表數(shù)組的元素
- 當對調函數(shù)三個參數(shù)時,分別為:數(shù)組的元素恤筛,元素索引官还,數(shù)組本身
arr.filter(function(a,b,c){
console.log(a);
console.log(b);
console.log(c);
})
所以可以利用filter
來實現(xiàn)對arr的去重復,去空元素等等操作叹俏。
//去掉arr中的重復元素
//這里利用的就是indexOf返回的是數(shù)組中該元素第一次出現(xiàn)的索引
//來實現(xiàn)對arr的去重讀
var strs = ['a','b','c','d','e','b','c','e','g','a'];
var newStrs = strs.filter(function(element, index, arr){
if(arr.indexOf(element) == index){
return true;
}
else{
return false;
}
})
console.log(newStrs);
3.sort
sort
應該可以翻譯為排序器吧妻枕,類似java中的comparable
,只需要傳入一個排序的方法,就能對調用的數(shù)組進行排序粘驰。
sort默認是將數(shù)組的元素轉換為
String
對象后進行排序屡谐,排序的依據(jù)是ASCII值的大小。
//按從小到大的循序排序
var nums = [12312,345345,24654356,234234,2342,4564,2342,6786,34,85,24558,62,48,4,512,5,95623];
console.log(nums.sort(function(x,y){
if(x > y){
return 1;
}else{
return -1;
}
return 0;
}))
通過傳入的比較方法的不同蝌数,可實現(xiàn)不同的實現(xiàn)效果愕掏,所以重點在于傳入的比較函數(shù)。
閉包
一般的方法和函數(shù)都是返回計算的結果顶伞,也就是基本數(shù)據(jù)類型和對象饵撑。當函數(shù)返回的是包含參數(shù)的函數(shù)對象時,這種的就稱為閉包唆貌。
var nums = [12312,345345,24654356,234234,2342,4564,2342,6786,34,85,24558,62,48,4,512,5,95623];
function lazy_sum(arr){
var sum = function(){
return arr.reduce(function(x,y){
return x + y;
})
}
return sum;
}
var result = lazy_sum(nums);
console.log(result);//[Function: sum]
console.log(result());//25383212
打印結果說明滑潘,result
此時是一個函數(shù),調用此函數(shù)锨咙,輸出結果為sum()
函數(shù)執(zhí)行的結果语卤。
若此時繼續(xù)執(zhí)行代碼:
nums.push(123123);
console.log(result());//25506335
但需要注意的是,此時因為傳入的引用數(shù)據(jù)類型酪刀,所以當此數(shù)據(jù)的值發(fā)生變化時粹舵,最終的結果也會受到影響。
箭頭函數(shù)
箭頭函數(shù)有點類似于java 8中的新特性骂倘。
var f = x => x+1;
//和下面的寫法實現(xiàn)了相同的功能
var f = function(x){
return x+1;`
}
此處由于函數(shù)體比較簡單眼滤,所以省略了{...} return
,并且當參數(shù)不止一個時历涝,也需要使用(...)
括起來诅需。所以,箭頭函數(shù)也有以下幾種寫法:
(x,y) => x + y; //多個參數(shù)需要用括號括起來
(x,y) => { //實現(xiàn)復復雜邏輯體時荧库,需要使用{}括起來
if(x > y)
return true;
else
return false;
}
/*空參也需要括號诱担。像這種直接返回對象的時候,需要使用()把括號
把對象括起來电爹,否則引起函數(shù)體和對象體的語法沖突而報錯
*/
() => ({name:"bixuan"}) 。
箭頭函數(shù)同時也修復了this的歷史遺留問題料睛,不是在多層調用時指向window或者undefined了丐箩,而是指向詞法作用域摇邦,也就是外層調用這的OBJ。
//以前的寫法會出現(xiàn)undefined或者指向的了window
var obj = {
birth: 1990,
getAge: function () {
var b = this.birth; // 1990
var fn = function () {
return new Date().getFullYear() - this.birth; // this指向window或undefined
};
return fn();
}
};
//而箭頭函數(shù)則不會有這個問題出現(xiàn)
var obj = {
birth: 1990,
getAge: function () {
var b = this.birth; // 1990
var fn = () => new Date().getFullYear() - this.birth; // this指向obj對象
return fn();
}
};
obj.getAge(); // 25
但是有一點也需要注意屎勘,就是箭頭函數(shù)因為綁定了this
,所以再調用調用call()
或者apply()
的時候施籍,傳入的第一個參數(shù)無法對this
進行綁定,還是會和上面一樣概漱,綁定在外層調用者上丑慎,所以call({...},X,Y)
的第一個對象參數(shù)會被忽略掉。
var obj = {
birth: 1990,
getAge: function(year){
var b = this.birth;//1990
var fn = (y) => y - this.birth;//此時瓤摧,this.birth還是1990
return fn.call({birth:3000},year);
}
};
var obj2 = {
birth: 1990,
getAge: function(year){
var b = this.birth;//1990
// var fn = (y) => y - this.birth;//此時竿裂,this.birth還是1990
var fn = function(y){
return y - this.birth;
};
return fn.call({birth:3000},year);
}
};
console.log(obj.getAge(2016));//結果是26
console.log(obj2.getAge(2016));//-984