2. 函數(shù)
2.1 函數(shù)定義和調(diào)用
定義函數(shù)
有兩種方法既荚,一種是:
function abs(x) {
if (x >= 0) {
return x;
} else {
return -x;
}
}
另一種是:
var abs = function (x) {
if (x >= 0) {
return x;
} else {
return -x;
}
};//末尾添加 ; 構(gòu)成完整語(yǔ)法
兩種定義方法完全等價(jià)待笑。
函數(shù)調(diào)用
按順序傳入?yún)?shù)即可乳幸。
js允許傳入任意個(gè)參數(shù)肝集,因此傳入的參數(shù)比定義的參數(shù)多也沒(méi)有問(wèn)題瞻坝,雖然函數(shù)內(nèi)部并不需要這些參數(shù)。
傳入的參數(shù)少也不會(huì)出錯(cuò)杏瞻。
arguments
js有一個(gè)關(guān)鍵字 arguments
,它只在函數(shù)內(nèi)部起作用所刀,利用 arguments
可以獲得調(diào)用者傳入的所有參數(shù)。
rest
略
2.2 變量作用域與解構(gòu)賦值
如果一個(gè)變量在函數(shù)體內(nèi)部申明捞挥,則該變量的作用域?yàn)檎麄€(gè)函數(shù)體浮创,在函數(shù)體外不可引用該變量。
如果兩個(gè)不同的函數(shù)各自申明了同一個(gè)變量砌函,那么該變量只在各自的函數(shù)體內(nèi)起作用斩披。換句話(huà)說(shuō),不同函數(shù)內(nèi)部的同名變量互相獨(dú)立讹俊,互不影響垦沉。
由于JavaScript的函數(shù)可以嵌套,此時(shí)仍劈,內(nèi)部函數(shù)可以訪(fǎng)問(wèn)外部函數(shù)定義的變量厕倍,反過(guò)來(lái)則不行。
JavaScript的函數(shù)在查找變量時(shí)從自身函數(shù)定義開(kāi)始贩疙,從“內(nèi)”向“外”查找讹弯。如果內(nèi)部函數(shù)定義了與外部函數(shù)重名的變量,則內(nèi)部函數(shù)的變量將“屏蔽”外部函數(shù)的變量屋群。
變量提升
JavaScript的函數(shù)定義會(huì)先掃描整個(gè)函數(shù)體的語(yǔ)句闸婴,把所有申明的變量“提升”到函數(shù)頂部。由于JavaScript的這一怪異的“特性”芍躏,我們?cè)诤瘮?shù)內(nèi)部定義變量時(shí),請(qǐng)嚴(yán)格遵守在函數(shù)內(nèi)部首先申明所有變量這一規(guī)則降狠。最常見(jiàn)的做法是用一個(gè)var申明函數(shù)內(nèi)部用到的所有變量:
function foo() {
var
x = 1, // x初始化為1
y = x + 1, // y初始化為2
z, i; // z和i為undefined
// 其他語(yǔ)句:
for (i=0; i<100; i++) {
...
}
}
全局作用域
不在任何函數(shù)內(nèi)定義的變量就具有全局作用域对竣。實(shí)際上,JavaScript默認(rèn)有一個(gè)全局對(duì)象window榜配,全局作用域的變量實(shí)際上被綁定到window的一個(gè)屬性否纬。
由于函數(shù)定義有兩種方式,以變量方式var foo = function () {}定義的函數(shù)實(shí)際上也是一個(gè)全局變量蛋褥,因此临燃,頂層函數(shù)的定義也被視為一個(gè)全局變量,并綁定到window對(duì)象。
'use strict';
function foo() {
alert('foo');
}
foo(); // 直接調(diào)用foo()
window.foo(); // 通過(guò)window.foo()調(diào)用
JavaScript實(shí)際上只有一個(gè)全局作用域膜廊。任何變量(函數(shù)也視為變量)乏沸,如果沒(méi)有在當(dāng)前函數(shù)作用域中找到,就會(huì)繼續(xù)往上查找爪瓜,最后如果在全局作用域中也沒(méi)有找到蹬跃,則報(bào)ReferenceError錯(cuò)誤。
名字空間
全局變量會(huì)綁定到window上铆铆,不同的JavaScript文件如果使用了相同的全局變量蝶缀,或者定義了相同名字的頂層函數(shù),都會(huì)造成命名沖突薄货,并且很難被發(fā)現(xiàn)翁都。減少?zèng)_突的一個(gè)方法是把自己的所有變量和函數(shù)全部綁定到一個(gè)全局變量中。
// 唯一的全局變量MYAPP:
var MYAPP = {};
// 其他變量:
MYAPP.name = 'myapp';
MYAPP.version = 1.0;
// 其他函數(shù):
MYAPP.foo = function () {
return 'foo';
};
局部作用域
由于JavaScript的變量作用域?qū)嶋H上是函數(shù)內(nèi)部谅猾,我們?cè)趂or循環(huán)等語(yǔ)句塊中是無(wú)法定義具有局部作用域的變量的:
'use strict';
function foo() {
for (var i=0; i<100; i++) {
//
}
i += 100; // 仍然可以引用變量i
}
為了解決塊級(jí)作用域荐吵,ES6引入了新的關(guān)鍵字let,用let替代var可以申明一個(gè)塊級(jí)作用域的變量:
'use strict';
function foo() {
var sum = 0;
for (let i=0; i<100; i++) {
sum += i;
}
// SyntaxError:
i += 1;
}
常量
由于var和let申明的是變量赊瞬,如果要申明一個(gè)常量先煎,在ES6之前是不行的,我們通常用全部大寫(xiě)的變量來(lái)表示“這是一個(gè)常量巧涧,不要修改它的值”薯蝎。ES6標(biāo)準(zhǔn)引入了新的關(guān)鍵字const來(lái)定義常量,const與let都具有塊級(jí)作用域谤绳。
解構(gòu)賦值
ES6引入了解構(gòu)賦值占锯,可以對(duì)多個(gè)變量同時(shí)賦值。
如果數(shù)組本身還有嵌套缩筛,也可以通過(guò)下面的形式進(jìn)行解構(gòu)賦值消略,注意嵌套層次和位置要保持一致。
解構(gòu)賦值還可以忽略某些元素瞎抛。
如果需要從一個(gè)對(duì)象中取出若干屬性艺演,也可以使用解構(gòu)賦值,便于快速獲取對(duì)象的指定屬性桐臊。
解構(gòu)賦值還可以使用默認(rèn)值胎撤,這樣就避免了不存在的屬性返回undefined的問(wèn)題。
var [x, y, z] = ['hello', 'JavaScript', 'ES6'];
let [x, [y, z]] = ['hello', ['JavaScript', 'ES6']];
let [, , z] = ['hello', 'JavaScript', 'ES6']; // 忽略前兩個(gè)元素断凶,只對(duì)z賦值第三個(gè)元素
var person = {
name: '小明',
age: 20,
gender: 'male',
passport: 'G-12345678',
school: 'No.4 middle school',
address: {
city: 'Beijing',
street: 'No.1 Road',
zipcode: '100001'
}
};
var {name, age, passport} = person;// name, age, passport分別被賦值為對(duì)應(yīng)屬性
var {name, address: {city, zip}} = person;//city:"Beijing", zip是undefined伤提,因?yàn)閷傩悦莦ipcode不是zip
let {name, passport:id} = person;//id:'G-12345678'
var {name, single=true} = person;// 如果person對(duì)象沒(méi)有single屬性,默認(rèn)賦值為true
2.3 方法
給 xiaoming
綁定一個(gè)函數(shù)认烁,就可以做更多的事情肿男。比如介汹,寫(xiě)個(gè) age()
方法,返回 xiaoming
的年齡舶沛。
var xiaoming = {
name: '小明',
birth: 1990,
age: function () {
var y = new Date().getFullYear();
return y - this.birth;
}
};
xiaoming.age; // function xiaoming.age()
xiaoming.age(); // 今年調(diào)用是25,明年調(diào)用就變成26了
在一個(gè)方法內(nèi)部嘹承, this
是一個(gè)特殊變量,它始終指向當(dāng)前對(duì)象冠王,也就是 xiaoming
這個(gè)變量赶撰。
JavaScript 的函數(shù)內(nèi)部如果調(diào)用了 this
,如果以對(duì)象的方式調(diào)用柱彻,比如 xiaoming.age()
豪娜,該函數(shù)的 this
指向被調(diào)用的對(duì)象,也就是 xiaoming
哟楷;如果單獨(dú)調(diào)用函數(shù)瘤载,此時(shí)該函數(shù)的 this
指向全局對(duì)象,即 window
卖擅。
apply
要指定函數(shù)的this指向哪個(gè)對(duì)象鸣奔,可以用函數(shù)本身的apply方法,它接收兩個(gè)參數(shù)惩阶,第一個(gè)參數(shù)就是需要綁定的this變量挎狸,第二個(gè)參數(shù)是Array,表示函數(shù)本身的參數(shù)断楷。
用apply修復(fù)getAge()調(diào)用:
function getAge() {
var y = new Date().getFullYear();
return y - this.birth;
}
var xiaoming = {
name: '小明',
birth: 1990,
age: getAge
};
xiaoming.age(); // 25
getAge.apply(xiaoming, []); // 25, this指向xiaoming, 參數(shù)為空
另一個(gè)與apply()相似的方法是call()锨匆,區(qū)別在于aplly()把參數(shù)打包成Array傳入,call()把參數(shù)按順序傳入冬筒。對(duì)普通函數(shù)調(diào)用恐锣,通常把this綁定為null,即:
Math.max.apply(null, [3, 5, 4]); // 5
Math.max.call(null, 3, 5, 4); // 5
2.4 高階函數(shù)
JavaScript的函數(shù)其實(shí)都指向某個(gè)變量舞痰。既然變量可以指向函數(shù)土榴,函數(shù)的參數(shù)能接收變量,那么一個(gè)函數(shù)就可以接收另一個(gè)函數(shù)作為參數(shù)响牛,這種函數(shù)就稱(chēng)之為高階函數(shù)玷禽。
map/reduce
map
由于map()方法定義在JavaScript的Array中,我們調(diào)用Array的map()方法娃善,傳入我們自己的函數(shù)论衍,就得到了一個(gè)新的Array作為結(jié)果。
'use strict';
function pow(x){
return x*x;
};
var arr = [1,2,3,4,5,6,7,8,9,10];
var results = arr.map(pow);//map傳入的參數(shù)是pow聚磺,即函數(shù)對(duì)象本身
console.log(results);//1,4,9,16,25,36,49,64,81,100
reduce
Array的reduce()把一個(gè)函數(shù)作用在這個(gè)Array的[x1, x2, x3...]上,這個(gè)函數(shù)必須接收兩個(gè)參數(shù)炬丸,reduce()把結(jié)果繼續(xù)和序列的下一個(gè)元素做累積計(jì)算瘫寝。
var arr = [1, 3, 5, 7, 9];
arr.reduce(function (x, y) {
return x + y;
}); // 25
fliter
filter用于把Array的某些元素過(guò)濾掉蜒蕾,然后返回剩下的元素。和map()類(lèi)似焕阿,Array的filter()也接收一個(gè)函數(shù)咪啡。和map()不同的是,filter()把傳入的函數(shù)依次作用于每個(gè)元素暮屡,然后根據(jù)返回值是true還是false決定保留還是丟棄該元素撤摸。
var
r,
arr = ['apple', 'strawberry', 'banana', 'pear', 'apple', 'orange', 'orange', 'strawberry'];
r = arr.filter(function (element, index, self) {
return self.indexOf(element) === index;//true的話(huà)過(guò)濾
});//indexOf返回第一個(gè)元素的位置,后續(xù)重復(fù)元素位置與indexOf返回的位置不相等褒纲,所以被fliter過(guò)濾掉了
sort
Array的sort()方法默認(rèn)把所有元素先轉(zhuǎn)換為String再排序准夷,幸運(yùn)的是,sort()方法也是一個(gè)高階函數(shù)莺掠,它還可以接收一個(gè)比較函數(shù)來(lái)實(shí)現(xiàn)自定義的排序衫嵌。sort()方法會(huì)直接對(duì)Array進(jìn)行修改,它返回的結(jié)果仍是當(dāng)前Array彻秆。
//倒序排序
var arr = [10, 20, 1, 2];
arr.sort(function (x, y) {
if (x < y) {
return 1;
}
if (x > y) {
return -1;
}
return 0;
}); // [20, 10, 2, 1]
every
every()方法可以判斷數(shù)組的所有元素是否滿(mǎn)足測(cè)試條件楔绞。
find
find()方法用于查找符合條件的第一個(gè)元素,如果找到了唇兑,返回這個(gè)元素酒朵,否則,返回undefined扎附。
findIndex
findIndex()和find()類(lèi)似蔫耽,也是查找符合條件的第一個(gè)元素,不同之處在于findIndex()會(huì)返回這個(gè)元素的索引帕棉,如果沒(méi)有找到针肥,返回-1。
forEach
forEach()和map()類(lèi)似香伴,它也把每個(gè)元素依次作用于傳入的函數(shù)慰枕,但不會(huì)返回新的數(shù)組。forEach()常用于遍歷數(shù)組即纲,因此具帮,傳入的函數(shù)不需要返回值。
2.5 閉包
函數(shù)作為返回值
此處參考廖雪峰老師的講解低斋,不再贅述 https://www.liaoxuefeng.com/wiki/1022910821149312/1023021250770016
2.6 generator
generator(生成器)是ES6標(biāo)準(zhǔn)引入的新的數(shù)據(jù)類(lèi)型蜂厅。一個(gè)generator看上去像一個(gè)函數(shù),但可以返回多次膊畴。generator和函數(shù)不同的是掘猿,generator由function定義(注意多出的號(hào)),并且唇跨,除了return語(yǔ)句稠通,還可以用yield返回多次衬衬。
調(diào)用generator對(duì)象有兩個(gè)方法,一是不斷地調(diào)用generator對(duì)象的next()方法改橘,next()方法會(huì)執(zhí)行g(shù)enerator的代碼滋尉,然后,每次遇到y(tǒng)ield x;就返回一個(gè)對(duì)象{value: x, done: true/false}飞主,然后“暫褪ㄏВ”。返回的value就是yield的返回值碌识,done表示這個(gè)generator是否已經(jīng)執(zhí)行結(jié)束了碾篡。如果done為true,則value就是return的返回值丸冕。
當(dāng)執(zhí)行到done為true時(shí)耽梅,這個(gè)generator對(duì)象就已經(jīng)全部執(zhí)行完畢,不要再繼續(xù)調(diào)用next()了胖烛。
第二個(gè)方法是直接用for ... of循環(huán)迭代generator對(duì)象眼姐,這種方式不需要我們自己判斷done。