JavaScript學習筆記次日_函數

廖雪峯的JavaScript教程學習筆記

1. 變量做用域

var 不能聲明塊級的變量,js的函數內變量聲明會被提高至函數體開頭
let 則用來解決這個塊級變量聲明,於ES6引入。
const用於聲明常量,也是ES6引入。javascript

2. 命名空間

全局變量會被默認綁定到window,不一樣JS文件若是定義了相同的名稱的全局變量或者頂級函數,那麼就會致使衝突。所以,解決方法就是把本身的全局變量綁定到一個全局變量中,相似於Java中的class,我的感受。java

3.this 關鍵字

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。算法

4. apply 和 call

每一個函數都自帶applycall方法,其中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]);

5. 裝飾器

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哭暈在廁所。函數

6. 高階函數

定義:高階函數英文叫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

7. 高階函數map/reduce

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。

測試題

  1. 不要使用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;
    });

    }

  2. 請把用戶輸入的不規範的英文名字,變爲首字母大寫,其餘小寫的規範名字。輸入:['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);
    });
  3. 小明的疑惑在與沒搞懂parseInt的接受的參數,由於map的callback默認是會傳入三個參數,而parseInt的參數有兩個parseInt(string, radix);那麼此時parseInt自動忽略傳入的第三個參數array,而傳入了(element, index)。索引改變了進制,於是獲得錯誤的結果。
    這個感受是個坑,還不清楚IDE是否會提示函數或者方法的參數爲啥。

8.高階函數filter

語法: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;
       });
}

9.高階函數sort

[1, 2,3,10,24].sort();
輸出結果是[1, 10, 2, 24, 3]。javascript或默認將數組裏數字轉換成string去比較,那麼根據ASCII嘛,天然是1要比2小,因此10就在2的前面。
l

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。

10.閉包

高階函數除了能夠接受函數做爲參數外,還能夠把函數做爲結果值返回。
好比返回求和的函數。

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表達式加法沒能理解呀。

11.箭頭函數

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。

12. generator

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同樣。

相關文章
相關標籤/搜索