1. 本文是在學(xué)習(xí)廖雪峰先生的JavaScrip教程 后的歸納
一、JavaScript函數(shù)
JavaScript的函數(shù)是"頭等公民",而且可以像變量一樣使用,具有強(qiáng)大的抽象能力
借助抽象,可以不關(guān)心底層具體實(shí)現(xiàn),而直接在最高層次上思考問(wèn)題
函數(shù)是最基本的代碼抽象的方式
二、函數(shù)的定義和調(diào)用
常規(guī)定義
function abs(x){
return x===0;
}
* `function` 關(guān)鍵字,指出函數(shù)的定義
* `abs`是函數(shù)的名稱
* `(x)`括號(hào)列出函數(shù)的參數(shù),多個(gè)參數(shù)以`,`分隔
* `{...}`之間的代碼是函數(shù)體,可以包含若干語(yǔ)句,也可以沒(méi)有任何語(yǔ)句
* 函數(shù)無(wú)論有沒(méi)有`return`都會(huì)返回結(jié)果,無(wú)return只是結(jié)果為`undefined`
* 由于JavaScript的函數(shù)是一個(gè)對(duì)象,函數(shù)名abs為一個(gè)函數(shù)對(duì)象,函數(shù)名abs可以視為指向該函數(shù)的變量
匿名函數(shù)定義
var abs = function(x){
return x===0;
};
此種情況下,function(x){..}為匿名函數(shù),沒(méi)有函數(shù)名,將該函數(shù)賦給變量abs,可以通過(guò)變量abs就可以調(diào)用該函數(shù)
上述定義和常規(guī)完全等價(jià),但需注意要在匿名函數(shù)末尾加上;,表示賦值語(yǔ)句結(jié)束
函數(shù)的調(diào)用
調(diào)用函數(shù)時(shí),按順序傳入?yún)?shù)即可
由于JavaScript允許傳入任意個(gè)參數(shù)而不影響調(diào)用,因此,傳入的參數(shù)比定義的參數(shù)多也沒(méi)問(wèn)題
傳入?yún)?shù)比定義的少也沒(méi)問(wèn)題,不過(guò),此時(shí)函數(shù)的參數(shù)將收到undefined,因此,為了避免收到undefine,需要進(jìn)行類型檢查
function abs(x){
if(typeof x!=='number'){
throw 'Not a number';
}
return x;
}
arguments
Javascript 中還有一個(gè)關(guān)鍵字arguments,僅在函數(shù)內(nèi)部起作用,并且永遠(yuǎn)指向當(dāng)前函數(shù)的調(diào)用傳入的所有參數(shù),類似Array而非Array
利用arguemnts,可以獲取調(diào)用者傳入的所有參數(shù),也就是,即使函數(shù)不定義任何參數(shù),還可以拿到參數(shù)的值
小心return語(yǔ)句,由于JavaScript引擎有一個(gè)在行末自動(dòng)添加分號(hào)的機(jī)制
function foo(){
return //實(shí)際解析這樣return ;
{name:'foo'}; // {name:'foo'};
}
把a(bǔ)rguments對(duì)象轉(zhuǎn)換成一個(gè)真正的數(shù)組
var args = Array.prototype.slice.call(arguments);
三、變量作用域
作用域
var 聲明的變量是有作用域的
變量在函數(shù)體內(nèi)部聲明,則該變量的作用域?yàn)檎麄€(gè)函數(shù)體,在函數(shù)體外不可引用該變量
不同函數(shù)內(nèi)部的同名變量相互獨(dú)立,不受影響
由于JavaScript的函數(shù)可以嵌套,內(nèi)部函數(shù)可以訪問(wèn)外部函數(shù)定義的變量,反過(guò)來(lái),則不行
JavaScript的函數(shù)在查找變量時(shí)從自身函數(shù)定義開(kāi)始,從"內(nèi)"向"外"查找,如果內(nèi)部函數(shù)定義了與外部函數(shù)重名的變量,則內(nèi)部函數(shù)的變量將"屏蔽"外部函數(shù)的變量
變量提升
JavaScript 會(huì)先掃描整個(gè)函數(shù)體的語(yǔ)句,把所有聲明的變量"提升"到函數(shù)頂部
JavaScript 引擎自動(dòng)提升了變量y的聲明,但不會(huì)提升變量y的賦值
要嚴(yán)格遵守在函數(shù)內(nèi)部首先申明所有變量這一規(guī)則,最常見(jiàn)的做法是用一個(gè)var申明函數(shù)內(nèi)部用的變量
function foo(){
var x = 1,
y = x + 1,
z, i;
for(i=0;i<100;i++){
....
}
}
全局作用域
名字空間
全局變量會(huì)綁定到window上,不同的JavaScript文件如果使用了相同的變量,或者定義了相同名字的頂層函數(shù),會(huì)造成命名沖突
減少?zèng)_突的一個(gè)方法是把自己的所有變量和函數(shù)全部綁定到一個(gè)全局變量中
把自己的代碼全部放入唯一的名字中,會(huì)大大減少全局變量沖突的可能,許多著名的JavaScript庫(kù)就是這樣干的:如JQuery
局部作用域
for循環(huán)等語(yǔ)句塊中是無(wú)法定義具有局部作用域的變量
為了解決塊級(jí)作用域,ES6引入了新的關(guān)鍵字let,用let代替var可以申明一個(gè)塊級(jí)作用域的變量
常量
在ES6之前,無(wú)法申明一個(gè)常量,通常采用大寫(xiě)的變量來(lái)表示"這是一個(gè)常量,不要修改它的值"
ES6標(biāo)準(zhǔn)引入了新的關(guān)鍵字const來(lái)定義常量,const與let都具有塊級(jí)作用域
四、方法
this 關(guān)鍵字
在一個(gè)對(duì)象中綁定函數(shù),稱為這個(gè)對(duì)象的方法
示例:
var xiaoming={
name:'小明',
birth:1990,
age:function(){
var y = new Date().getFullYear();
return y - this.birth;
}
};
xiaoming.age;//function xiaoming.age()
xiaoming.age();//返回小明的年紀(jì)
在一個(gè)方法內(nèi)部,this是一個(gè)特殊變量,它始終指向當(dāng)前對(duì)象
要保證this的正確調(diào)用,必須用obj.xxx()的形式調(diào)用
在strict模式下讓直接調(diào)用的函數(shù)中的this指向undefined
在strict模式下,通常在函數(shù)沒(méi)有指向正確的位置,那它將指向全局對(duì)象的window
在方法內(nèi)部定義其他函數(shù)時(shí),可以在方法內(nèi)部加上var that=this;方便函數(shù)調(diào)用對(duì)象的屬性
apply
在獨(dú)立的函數(shù)調(diào)用中,根據(jù)是否是strict模式,this指向undefined或window
函數(shù)本身的apply方法,接受兩個(gè)參數(shù):
需要綁定的this變量
Array,表示函數(shù)本身的參數(shù)
與apply()的類似方法是call(),唯一區(qū)別是:
apply() 把參數(shù)打包成Array在傳入
cal() 把參數(shù)按順序傳入
對(duì)普通函數(shù)調(diào)用,通常把this綁定為null
裝飾器
利用apply(),我們可以動(dòng)態(tài)改變函數(shù)的行為
JavaScript的所有對(duì)象都是動(dòng)態(tài)的,即使內(nèi)置的函數(shù),也可以重新指向新的函數(shù),如統(tǒng)計(jì)某內(nèi)置函數(shù)的調(diào)研次數(shù),可以用自定義函數(shù)替換掉默認(rèn)的內(nèi)置函數(shù)
var count = 0;
var oldParseInt = parseInt; // 保存原函數(shù)
window.parseInt = function () {
count += 1;
return oldParseInt.apply(null, arguments); // 調(diào)用原函數(shù)
};
// 測(cè)試:
parseInt('10');
parseInt('20');
parseInt('30');
count; // 3
五、高階函數(shù)
定義
Higher-order function 高級(jí)函數(shù)
JavaScript的函數(shù)其實(shí)都是指向某個(gè)變量
一個(gè)函數(shù)可以接收另一個(gè)函數(shù)作為參數(shù),這種函數(shù)稱之為高階函數(shù)
編寫(xiě)高級(jí)函數(shù)就是讓函數(shù)能夠接收別的函數(shù)
高級(jí)函數(shù)有強(qiáng)大的抽象能力,可以使核心代碼保持得非常簡(jiǎn)潔
function add(x,y,f){
return f(x)+f(y);
}
map/reduce
示例
function pow(x){
return x*x;
}
var arr = [1,2,3,4,5,6,7,8,9];
arr.map(pow);
map()作為高階函數(shù),事實(shí)上是把運(yùn)算規(guī)則抽象,不但可以計(jì)算簡(jiǎn)單的函數(shù),也可以計(jì)算任意復(fù)雜的函數(shù)
如可以吧Array的所有數(shù)字轉(zhuǎn)化為字符串: var arr=[1,2,]; arr.map(String);
Array的reduce()把一個(gè)函數(shù)作用域Array的[x1,x2,x3,..]上,這個(gè)函數(shù)必須接收兩個(gè)參數(shù),reduce()把結(jié)果繼續(xù)和序列的下一個(gè)元素做累積計(jì)算
[x1,x2,x3,x4].reduce(f)=f(f(f(x1,x2),x3),x4);//reduce類似遞歸
var arr=[1,3,5,7,9];
arr.reduce(function(x,y){
return x+y;
});
filter
也是一個(gè)常用的操作,它用于把Array的某些元素過(guò)濾掉,然后返回剩下的元素
和map()類似,Array的filter()也接收一個(gè)函數(shù),和map()不同的時(shí),filter()把傳入的函數(shù)依次作用于每個(gè)元素,然后根據(jù)返回值是true還是false決定保留還是丟棄該元素
sort
排序算法: 比較的過(guò)程必須通過(guò)函數(shù)抽象出來(lái),通常規(guī)定,對(duì)于兩個(gè)元素x和y,如果認(rèn)為x<y,則返回-1,如果認(rèn)為x===y,則返回0,如果認(rèn)為x>y,返回1
排序算法不用關(guān)心具體的比較過(guò)程,而根據(jù)比較結(jié)果直接排序
JavaScript的Array的sort()方法是用于排序,排序規(guī)則如下:
字符串根據(jù)ASCII碼進(jìn)行排序(小寫(xiě)字母的ASCII碼在大寫(xiě)字母之后)
默認(rèn)將所有的元素先轉(zhuǎn)換成String在排序
可以接收一個(gè)比較函數(shù)來(lái)實(shí)現(xiàn)自定義排序
示例
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]
六、閉包
函數(shù)作為返回值
高級(jí)函數(shù)可以接收函數(shù)做參數(shù),還可以把函數(shù)作為結(jié)果值返回
示例
function lazy_sum(arr){
var sum = function(){
return arr.reduce(function(x,y){
return x+y;
}
}
}
var f= lazy_sum([1,2,3,4,5]);
f();
在上述例子中,函數(shù)layz_sum中定義了函數(shù)sum,并且,內(nèi)部函數(shù)sum可以引用外部函數(shù)lazy_sum的參數(shù)和局部變量,當(dāng)lazy_sum返回函數(shù)sum時(shí),相關(guān)參數(shù)和變量都保存在返回的函數(shù)中,這種稱為"閉包(Closure)"的程序結(jié)構(gòu)擁有巨大威力
當(dāng)調(diào)用`lazy_sum()時(shí),每次調(diào)用都會(huì)返回一個(gè)新的函數(shù),即使傳入相同的參數(shù)
var f1 = lazy_sum([1, 2, 3, 4, 5]);
var f2 = lazy_sum([1, 2, 3, 4, 5]);
f1 === f2; // false
//`f1()和`f2()的調(diào)用結(jié)果互不影響
閉包作用
返回的函數(shù)在其定義內(nèi)部引用了局部變量arr,當(dāng)一個(gè)函數(shù)返回了一個(gè)函數(shù)后,其內(nèi)部的局部變量還被新函數(shù)引用,函數(shù)并沒(méi)有立刻執(zhí)行,而是調(diào)用了f()才行
返回函數(shù)不要引用任何循環(huán)變量,或者后續(xù)會(huì)發(fā)生變化的變
若一定引用循環(huán)變量: 方法是在再創(chuàng)建一個(gè)函數(shù),用該函數(shù)的參數(shù)綁定循環(huán)變量當(dāng)前的值,無(wú)論該循環(huán)變量后續(xù)如何更改,已綁定函數(shù)參數(shù)的值不變
function count() {
var arr = [];
for (var i=1; i<=3; i++) {
arr.push((function (n) {
return function () {
return n * n;
}
})(i));
}
return arr;
}
var results = count();
var f1 = results[0];
var f2 = results[1];
var f3 = results[2];
f1(); // 1
f2(); // 4
f3(); // 9
創(chuàng)建一個(gè)匿名函數(shù)并立刻執(zhí)行可以這么寫(xiě):
(function(x) {return x*x;})(3);
JavaScript 借助閉包,可以封裝一個(gè)私有變量,閉包就是攜帶狀態(tài)的函數(shù),并且它的狀態(tài)可以完全對(duì)外隱藏起來(lái)
'use strict';
function create_counter(initial){
//私有變量
var x = initial||0;
return {
inc: function(){
x +=1;
return x;
}
};
}
var c1 = create_counter();
c1.inc(); // 1
c1.inc(); // 2
c1.inc(); // 3
var c2 = create_counter(10);
c2.inc(); // 11
c2.inc(); // 12
c2.inc(); // 13
閉包就是攜帶狀態(tài)的函數(shù),并且它的狀態(tài)可以完全對(duì)外隱藏起來(lái)
閉包還可以把多參數(shù)的函數(shù)變成單參數(shù)的函數(shù)
function make_pow(n) {
return function (x) {
return Math.pow(x, n);
}
}
// 創(chuàng)建兩個(gè)新函數(shù):
var pow2 = make_pow(2);
var pow3 = make_pow(3);
pow2(5); // 25
pow3(7); // 343
七、generator
定義
generator(生成器)是ES6標(biāo)準(zhǔn)引入的新的數(shù)據(jù)類型。一個(gè)generator看上去像一個(gè)函數(shù),但可以返回多次
generator 有function*定義,除了return語(yǔ)句,還可以使用yield返回多次
使用
function* fib(max) {
var
t,
a = 0,
b = 1,
n = 1;
while (n < max) {
yield a;
t = a + b;
a = b;
b = t;
n ++;
}
return a;
}
直接調(diào)用generator和調(diào)用函數(shù)不一樣,僅僅是創(chuàng)建了一個(gè)generator對(duì)象,還沒(méi)有去執(zhí)行它
調(diào)用generator對(duì)象有兩種方法:
不斷調(diào)用generator對(duì)象的next()方法
var f = fib(5);
f.next(); // {value: 0, done: false}
f.next(); // {value: 1, done: false}
f.next(); // {value: 1, done: false}
f.next(); // {value: 2, done: false}
f.next(); // {value: 3, done: true}
next()方法會(huì)執(zhí)行時(shí),每次遇到yield x;就返回一個(gè)對(duì)象 {value:x,done:true/false},然后暫停,返回value表示yield的返回值,done表示這個(gè)generator對(duì)象是否已經(jīng)執(zhí)行結(jié)束,如果done為true,則value就是return的返回值。
直接用for...of循環(huán)迭達(dá)generator對(duì)象,這種方式不需要自己判斷done
for (var x of fib(5)) {
console.log(x); // 依次輸出0, 1, 1, 2, 3
}
generator可以把異步回調(diào)代碼變成"同步"代碼
最后編輯于 :2017.11.27 03:11:07
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者 【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。 平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。