var
不能聲明塊級的變量,js的函數內變量聲明會被提高至函數體開頭let
則用來解決這個塊級變量聲明,於ES6引入。const
用於聲明常量,也是ES6引入。javascript
全局變量會被默認綁定到window
,不一樣JS文件若是定義了相同的名稱的全局變量或者頂級函數,那麼就會致使衝突。所以,解決方法就是把本身的全局變量綁定到一個全局變量中,相似於Java中的class,我的感受。java
var xiaoming = { name: '小明', birth: 1990, age: function () { var y = new Date().getFullYear(); return y - this.birth; } }; xiaoming.age; // function xiaoming.age() xiaoming.age(); // 今年調用是25,明年調用就變成26了
由於xiaoming是個對象,因此方法內部的this就是指當前xiaoming這個對象,和java對象內部使用的this是一個意思。可是若是在方法內不又定義了方法,並在這個內部方法裏引用this,那麼這個this將報錯,undefined或者window。若是從java角度看的話,有點像是內部類裏使用this於是出錯。 但也能夠在內部方法的外層,捕獲this對象,好比賦值給that。算法
每一個函數都自帶apply
和call
方法,其中apply
接受兩個參數(object, arguments[])
, 即傳入對象自己和參數數組,這樣就方法體內的this
就會自動指向object
,從而避免this亂指。數組
call
方法與apply
具備一樣的功能,第一個也是object
本身,以後是參數列表,非Array
。
普通函數通常將obejct賦值爲null
。
好比閉包
Math.max.call(null, 3,4,4,5,6,6,67,8,9); Math.max.apply(null, [3,4,4,5,6,6,67,8,9]);
javascript全部的對象都是動態,即便是內置的函數,也能夠從新指向新的函數。
如window上有個全局函數parseInt()函數,須要統計parseInt被調用次數。app
var count = 0; var oldParseInt = parseInt; // 保存原函數 window.parseInt = function () { count += 1; // arguments是函數的參數數組 return oldParseInt.apply(null, arguments); // 調用原函數 }; // 測試: parseInt('10'); parseInt('20'); parseInt('30'); count; // 3
能夠看出來JavaScript如此輕易就實現了裝飾器,java哭暈在廁所。函數
定義:高階函數英文叫Higher-order function。
JavaScript的函數其實都指向某個變量。既然變量能夠指向函數,函數的參數能接收變量,那麼一個函數就能夠接收另外一個函數做爲參數,這種函數就稱之爲高階函數。這個厲害了,WOW。
裏面也涉及到一個javascript的函數的參數是能夠傳入多個或不傳的原理,因此方法體內才能夠直接對傳入變量輸入參數,而且個數不限制。直接show me the fucking source code。學習
function add(x, y, f){ return f(x) + f(y); } var f = Math.abs; add(6, -3, f); // 輸出9
666啊,這個map/reduce不是大數據用的麼?竟然是js的高階函數啊,看起來好吊啊,真是吊打java了。
網上找了一段解釋:測試
array.map(callback[, thisArg]);
map 方法會給原數組中的每一個元素都按順序調用一次 callback 函數。callback 每次執行後的返回值組合起來造成一個新數組。 callback 函數只會在有值的索引上被調用;那些歷來沒被賦過值或者使用 delete 刪除的索引則不會被調用。大數據
callback 函數會被自動傳入三個參數:數組元素,元素索引,原數組自己。
若是 thisArg 參數有值,則每次 callback 函數被調用的時候,this 都會指向 thisArg 參數上的這個對象。若是省略了 thisArg 參數,或者賦值爲 null 或 undefined,則 this 指向全局對象 。
如對每一個元素進行平方運算。
var pow = function(x){ return x*x; } var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]; arr.map(pow);
輸出結果是[1, 4, 9, 16, 25, 36, 49, 64, 81]
。
因此結合上面的解釋就是map對數組arr的每一個元素調用一次callback即pow()函數。因爲map會給callback傳入三個參數,而pow只取了第一個參數,即數組元素,因此結果是對每一個元素平方,而後組成成新的數組。
咱們能夠試着修改pow,去把傳入到callback的第二個參數也使用上,那麼咱們來試試給給每一個元素平方後加上他的索引值。
var pow = function(x){ return x*x+arguments[1]; } var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]; arr.map(pow);
輸出結果爲[1, 5, 11, 19, 29, 41, 55, 71, 89]
。
更爲具體的map定義能夠看MSDN的解釋。
接下來看看reduce函數。一樣做爲array的方法,reduce是對數組的每個元素與其下一個元素調用callback一次方法,並將callback的結果做爲下一次的參數與下一個元素再次調用callback方法,進行累積運算。有點繞,看數學公式可能更好理解
[x1, x2, x3, x4].reduce(f) = f(f(f(x1, x2), x3), x4)
因此咱們來進行一個求和,計算1到100和運算。
var arr = []; for(var i = 1; i <= 100; i++){ arr[i-1] = i; } arr.reduce(function (x, y){ return x+y; });
輸出結果爲5050
。
再來看看具體reduce定義
arr.reduce(callback[,initialValue])
initialValue做爲reduce方法的可選參數,callback接收4個參數, accumulator 是累積值,若是reduce方法設定了initialValue,那麼accumulator的初始值就是initialValue。
accumulator
currentValue
currentIndex
array
因此咱們能夠繼續嘗試設定初始的方式
var arr = []; for(var i = 1; i <= 100; i++){ arr[i-1] = i; } arr.reduce(function (x, y){ return x+y; }, 100);
咱們將初始值設爲100,那麼輸出結果是5150。
測試題
不要使用JavaScript內置的parseInt()函數,利用map和reduce操做實現一個string2int()函數:
function string2ints(s){
return s.split('').map(function(x){ return x*1}).reduce(function(x, y){ return x*10+y*1; });
}
請把用戶輸入的不規範的英文名字,變爲首字母大寫,其餘小寫的規範名字。輸入:['adam', 'LISA', 'barT']
,輸出:['Adam', 'Lisa', 'Bart']
。
// comment on this var arr = ["LINda", "adam", "barT"]; arr.map(function(x){ return x.toLowerCase();}).map(function(x){ return x[0].toUpperCase()+x.slice(1); });
小明的疑惑在與沒搞懂parseInt的接受的參數,由於map的callback默認是會傳入三個參數,而parseInt的參數有兩個parseInt(string, radix);那麼此時parseInt自動忽略傳入的第三個參數array,而傳入了(element, index)。索引改變了進制,於是獲得錯誤的結果。
這個感受是個坑,還不清楚IDE是否會提示函數或者方法的參數爲啥。
語法:var new_array = arr.filter(callback[, thisArg])
filter也是一個經常使用的操做,它用於把Array的某些元素過濾掉,而後返回剩下的元素。與map相同的是,它也接受一個函數callback,而後對每一個元素調用函數進行處理,不一樣的地方在於,函數須要返回true或false,用戶決定是保留該元素仍是刪除該元素,最後生成一個新的數組,如其名字所決定的同樣。thisAgrs不用解釋。
其中callback一樣接收3個參數element,index,array。
簡單的作個去重操做和過濾掉奇數。
var arr = [1,2,3,4,4,4,5,5,5,6,6,7,7,8,8,9,9,9]; arr.filter(function(x){return x % 2 === 0;}) .filter(function(element, index, self){ return self.indexOf(element) === index;} );
self.index(element)
只會返回找到的第一個元素的index,所以後續的index就再也不相等了。
在作一個找出1-100之內全部的質數或者說素數。
質數或素數定義
質數大於等於2 不能被它自己和1之外的數整除
作法:在通常領域,對正整數n,若是用2到sqrt(n)
之間的全部整數去除,均沒法整除,則n爲質數。
代碼:
function getPrimes(arr){ return arr.filter(function(x){ if(x <= 3) return x>1; // 大於等於2才能夠爲質數,因此1除外 // 2到sqrt(n)之間均沒法整除,即只要有一個能夠整除即非質數。 var end = Math.round(Math.sqrt(x)); for(let i=2; i <= end; i++){ if(x % i === 0) return false; } return true; }); }
[1, 2,3,10,24].sort();
輸出結果是[1, 10, 2, 24, 3]。javascript或默認將數組裏數字轉換成string去比較,那麼根據ASCII嘛,天然是1要比2小,因此10就在2的前面。
But, 高階函數嘛,那麼天然sort也是能夠接受函數來進行修改比較算法的,相似於要實現Java裏的comparable接口,規則也是相似的,若x < y, 則返回-1,表示小於;若x === y, 返回0;若x > y,則返回1。理論上,並不要定義爲1或者-1,只要用正數表示大於,負數表示小於就行了。因此你能夠直接使用x-y;
那就數字排個序好了。
arr = [12343,5435,6,7,2,3,77,88,322]; arr.sort(function(x, y){ return x-y; });
Caution:這個sort方法會將排序結果直接做用在數組自己上,即會修改arr。
高階函數除了能夠接受函數做爲參數外,還能夠把函數做爲結果值返回。
好比返回求和的函數。
function lazy_sum(arr){ var sum = function(){ return arr.reduce(function(x, y){ return x+y; }); return sum; } // 使用時 var f = lazy_sum([1,3,2,5]); f(); var f1 = lazy_sum([1,3,2,5]); f === f1; // result is false.
因爲返回的是個函數,即便傳入的參數相等,也至關因而返回了不一樣的函數對象。
返回閉包時牢記的一點就是:返回函數不要引用任何循環變量,或者後續會發生變化的變量。
這個例子返回的函數引用了局部變量, 若是局部變量後續會變化,那麼最好是再建立一個函數,綁定局部變量做爲參數。
建立匿名函數並馬上執行.
(function (x) { return x * x }) (3);
其中function (x) { return x * x }是匿名函數,須要使用括號括起來才能夠馬上執行,否則會報語法錯誤。
使用閉關封裝一個私有變量private。
function create_counter(initial){ var x = initial || 0; return { inc:function(){ x += 1; return x; } } }
create_counter()返回了一個匿名對象,這個對象有個屬性inc,而這個inc的值又是一個函數。那麼就能夠獲取這個匿名對象,而後調用函數inc()便可實現對private的x加1.
縮減參數列表的用法
function make_pow(n){ return function(x){ return Math.pow(x,n); } } var pow2 = make_pow(2);// 返回一個Math.pow(x, 2)的函數。 var pow3 = make_pow(3);// 返回一個Math.pow(x, 3)的函數。
pow2 和 pow3就只須要接收一個參數,便可實現power的操做。
底下的Lambda表達式加法沒能理解呀。
ES6 新引入的
x => x * x
至關於以下的匿名函數,其中x爲參數。
function (x){ return x*x; }
若是有多個參數須要使用括號括起來,如(x,y) => x+y;
。
用法呢和匿名函數也差很少,好比:
var pow2 = x=> x*x; pow2(2); // 4 // 建立匿名函數並執行 (x => x*x)(2); // 4
還有一種包含多條語句的,須要使用{}
將其包起來。如:
(x, y) => { if(x>y){ return x-y;} else{ return y-x;} }
還有相似於()=>3.14
的寫法,雖然看起來沒啥卵用。
可變參數寫法,就是參數變了一下
(x, y, ...rest) =>{ var sum= x =y; for(var x of arguments){ sum += x; } return sum; }
廖老師說x => { foo: x }
這樣寫會語義錯誤,然而並無。可是確實沒可以被正確解析,因此當返回匿名對象的時候,使用()
將其包起來,如x => ({ foo: x })。
須要注意的是箭頭函數內部的this是詞法做用域,由上下文肯定。箭頭函數徹底修復了this的指向,this老是指向詞法做用域,也就是外層調用者obj。那麼在第4節裏說的每一個函數都自帶apply和call方法,他們的第一個參數object就都不在有用,由於箭頭函數會自動綁定外層調用者obj。
ES6新引入的數據類型,看上去像是一個函數,可是能夠返回屢次。
與函數定義不一樣的是,使用function*
來定義一個generator,除了使用return退出函數以後,還可使用yield交出控制權,這時並無退出整個函數,使用next時,就會從當前yield處往下執行。
以斐波那契數列爲例:[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377]
普通的使用數組來保存結果的斐波那契數列生成方法
function fib(max){ var t, a = 0, b = 1, arr = [0, 1]; while (arr.length < max){ t = a + b; a = b; b = t; arr.push(t); } return arr; }
以generator生成器生成斐波那契數列的方法。
function* fib2(max){ var t, a = 0, b = 1, n = 1; while (n < max){ yield a; t = a + b; a = b; b = t; n++; } return a; } > var f = fib2(5); undefined > f.next(); // 輸出0返回。 Object {value: 0, done: false} > f.next(); Object {value: 1, done: false} > f.next(); Object {value: 1, done: false} > f.next(); Object {value: 2, done: false} > f.next(); Object {value: 3, done: true} > f.next(); Object {value: undefined, done: true}
能夠看到每次調用next,都會促使整段代碼執行到下一個yield,而後輸出一個匿名對象{value:xx, done:true/false}
其中xx是yield的返回值,done是代表此時是否生成結束,便是否執行到return。
還有一種使用方式,就是for...of
,無需判斷是否執行完畢。
for (var x of fib(5)) { console.log(x); // 依次輸出0, 1, 1, 2, 3 }
But這個generator有啥卵用呢?我暫時想起來一個狀態轉換的,即每次調用都會切換到下一個狀態,直到切換完成,像Android中的StateMachine同樣。