《javascript語言精粹》學習筆記2

來到第四章。javascript

函數:前面說過,函數就是對象,特色就是還可以被調用。前端

1.函數對象

由於函數是對象,因此它們能夠像任何其餘的值同樣被使用。函數能夠保存在變量,對象和數組中。函數能夠被看成參數傳遞給其餘函數,函數也能夠返回函數,也能夠擁有方法。
2.函數字面量

<!-- lang: js -->
var add = function (a,b) {
    return a + b;
}

包括保留字function,函數名(可省略,無名稱爲匿名函數),包圍在括號中的參數和包圍在花括號中的函數主體。
3.調用

除了接受形式參數外,每一個函數默認接受兩個附加的參數,this和arguments。js有四種調用模式:方法調用模式,函數調用模式,構造器調用模式和apply調用模式。這些模式在如何初始化關鍵參數this上存在差別。 實際參數個數超過形式參數個數時,超出的參數值會省略,少時缺失的值會替換爲undefined。這個方面js的容忍度很高,就是不會提示。 4.方法調用模式

當一個函數爲對象的一個屬性時,咱們稱之爲方法。當方法被調用時,this綁定到該對象。java

<!-- lang: js -->
var myObject = {
    value : 0,
    increment : function (inc) {
        this.value += typeof inc === 'number' ? inc : 1;
    }
};

myObject.increment();  // 1
document.writeln(myObject.value);

myObject.increment(2);  // 3
document.writeln(myObject.value);

5.函數調用模式

當一個函數並不是一個對象的屬性時,那麼它就是被看成一個函數來調用。它僅僅是一個函數而已。node

<!-- lang: js -->
var sum = add(3,4);

注意:這樣調用函數,this 被綁定到全局對象。一個解決方案是:若是該方法定義一個變量並把它賦值爲this,那麼內部函數就能夠經過那個變量訪問到this,按照約定,那個變量命名爲that。面試

<!-- lang: js -->
myObject.double = function () {
    var that = this;
    var helper = function () {
        that.value = add(that.value. that.value);
    };

    helper();// 以函數形式調用helper
}

//以方法的形式調用double
myObject.double();
document.writeln(myObject.value);

6.構造器調用模式

若是在一個函數前面加上new來調用,那麼背地裏會建立一個連接到該函數的prototype成員的新對象,同時this會綁定到那個新對象上。ajax

<!-- lang: js -->
//建立一個名爲Quo的構造器函數。它構造一個帶有status屬性的對象
var Quo = function (string) {
    this.status = string;
}
//給Quo的全部實例提供一個名爲get_status的公共方法
Quo.prototype.get_status = function () {
    return this.status;
}
//構建一個Quo實例
var myQuo = new Quo("confused");
document.writeln(myQuo.get_status());

原文不推薦,後面有更好的替代方式。 7.Apply調用模式

apply方法讓咱們構建一個參數數組傳遞給調用函數。接收兩個參數,第一個是要綁定給this的值,第二個就是一個參數數組。
實際就是把一個本來不是屬於本身的方法或函數用在本身身上,這就是apply的意思。正則表達式

<!-- lang: js -->
var array = [3,4];
var sum = add.apply(null,array);//7,用了上面定義的add函數,用數組傳遞參數
//構造一個包含status成員的對象
var statusObject = {
    status : "A-OK"
};
//statusObject 並無繼承自Quo.prototype,但咱們能夠在statusObject上調用get_status方法,儘管statusObject並無
var status = Quo.prototype.get_status.apply(statusObject);//A-OK

8.參數

當函數被調用時,會獲得一個"免費"配送的參數,那就是arguments數組。數組包括傳遞給形參的參數,也包括沒有分配形式參數的多餘參數。也就是說傳遞給形參的參數能夠經過兩種途徑在函數中取得。算法

<!-- lang: js -->
var sum = function (){
    var sum = 0;
    for(var i = 0; i < arguments.length; i += 1){
        sum += arguments[i];
        
    }
    return sum;
}
document.writeln(sum(1,2,3,4));//10

注意:arguments只是一個相似數組的對象,不是一個真正的數組,只是擁有一個length的屬性,但它沒有任何數組的方法。書說後面會有它的後果,咱們拭目以待。
9.返回

一個函數總會返回一個值,若是沒有指定則爲undefined。
**若是函數調用時在前面加上了new前綴,且返回值不是一個對象,則返回this(該新對象)。**這個對應構造器調用模式。
10.異常

(感受本身很沒注意差錯控制檢驗這方面的事情TT) 這一部分跟C++差很少,有throw就要有對應的catch。catch到的error要對應throw出來的東西。例子跑出的是一個對象,則catch的參數對應一個對象,而後把對應的信息輸出來。
書上例子先理解下。數組

<!-- lang: js -->
var add = function (a,b) {
    if (typeof a !== 'number' || typeof b !== 'number') {
        throw {
            name : 'TypeError',
            message : 'add needs number'
        };
    }
    return a + b;
}

var try_it = function () {
    try {
        add("seven");
    } catch (e) {
        document.writeln(e.name + ': ' + e.message);
    }
}

try_it();

11.擴充類型的功能

js可以給語言的基本類型擴充功能。第三章對象的時候,咱們能夠經過給Object.prototype添加方法,可讓該方法對全部對象均可用。這樣的方式對函數,數組,字符串,數字,正則表達式和布爾值都適用。
下面這個擴充在後面會常常用到的喔!閉包

<!-- lang: js -->
Function.prototype.method = function (name, func) {
    this.prototype[name] = func;
    return this;
}

經過給Function.prototype增長方法來是的該方法對全部函數可用。咱們下次給對象增長方法時就不用鍵入prototype這幾個字符,省掉一點麻煩(不是很懂。。實際上是不是看起來帥點,優雅一點呢)

由於基本類型的原型是公用結構,因此在類庫混用時務必當心。一個保險的作法是指在肯定沒有該方法時才添加它。

<!-- lang: js -->
Function.prototype.method = function (name, func) {
    if (!this.prototype[name]) {
        this.prototype[name] = func;
    }
    return this;
}

12.遞歸

遞歸就是那意思,調用函數自身去解決它的子問題。 很少說,以爲裏面一個遍歷DOM樹的函數寫的不錯。

<!-- lang: js -->
var walk_the_DOM = function walk(node, func) {
    func(node);
    node = node.firstChild;
    while (node) {
        walk(node,func);
        node = node.nextSibling;
    }
}

書裏還提到了一個概念叫尾遞歸優化,意思是若是一個函數返回自身遞歸調用的結果,那麼調用的過程會被替換爲一個循環,它能夠顯著提升速度。而後又說js沒有提供尾遞歸優化(坑爹呢~~)。
13.做用域

這一方面好像以前看過的幾道前端面試題目就有這個的內容,基礎要掌握啊!!
從新找回之前的一篇博文來看,才發現之前都沒怎麼理解,此次看了幾個鍾,有些收穫。牆裂建議看看:http://blog.csdn.net/rommel1/article/details/7804973

總結一下就是,像javascript這樣的解釋型語言,基本分爲兩個階段,編譯期與運行期。在預編譯階段,它是用函數來劃分做用域,而後逐層爲其以 var 聲明的變量與函數定義開闢內存空間,再而後對var變量進行特殊處理,通通賦初始值爲undefined。 在研究上文的過程當中,又看到類屬性和實例屬性的概念,又找了一篇文章,感受說的還比較全面。最重要的一點:動態共有屬性優於原型屬性。即:若是兩者都定義了同一屬性,則最終的屬性值以動態公有屬性爲準。附連接:http://evanwukong.blog.163.com/blog/static/134836495201141752910780/

看了這些,書裏的例子已經是浮雲。 其餘語言是塊級做用域,但js不支持,js有的是函數做用域,因此要在頂部聲明函數中可能用到的全部變量,並且產生了下面的閉包。 14.閉包

閉包的好處是內部函數能夠訪問定義他們的外部函數的參數和變量(除了this和arguments) 以前的Quo構造器沒什麼意義,能夠直接訪問到的屬性爲何要用一個getter方法去訪問呢? 因此下面的方式比較推薦。

<!-- lang: js -->
var quo = function (status) {
    return {
        get_status : function () {
            return status;
        }
    };
};
//構造一個quo實例
var myQuo = quo("amazed");
document.writeln(myQuo.get_status());

這個quo無需在前面加new,即便quo已經返回,但get_status仍能夠訪問到quo對象的status屬性。
下面是一個設置DOM節點爲黃色,而後把它漸變爲白色的函數。

<!-- lang: js -->
var fade = function (node) {
    var level = 1;
    var step = function () {
        var hex = level.toString(16);
        node.style.backgroundColor = '#FFFF' + hex + hex;
        if (level < 15) {
            level += 1;
            setTimeout(step, 100);
        }
    };
    //書中是setTimeout(step, 100);我以爲下面也能夠
    step();
};
fade(document.body);

一個糟糕的例子,

<!-- lang: js -->
var add_the_handleers = function (nodes) {
    var i;
    for (i = 0; i < nodes.length; i += 1) {
        alert(i);
    }
};

這個例子的本意是想每個節點alert出不一樣的值,,可是因爲綁定的數是變量i自己,而不是函數在構造時變量i的值。 改良一下,

<!-- lang: js -->
var add_the_handlers = function (nodes) {
    var helper = function (i) {
        return function (e) {
            alert(i);
        };
    };
    var i;
    for (var i = 0; i < nodes.length; i += 1) {
        nodes[i].onclick = helper(i);
    };
}

上面的程序把i經過形參i傳遞進去,用到的不是外面的變量i自己,因此解決了以上那個問題。
15.回調

說的是概念吧。你們知道ajax的概念就差很少我以爲。 16.模塊

利用函數包裝成模塊,提供接口卻隱藏狀態與實現的函數或對象。 咱們想給String增長deentityify方法,用來尋找字符竄中的HTML字符實體並把他們替換爲對應的字符。這就須要保存字符實體及其對應字符。咱們不能在全局變量中保存,保存在函數內部又會帶來運行時的內耗,覺得每次執行該函數時該字面量都會被求值一次。理想方法就是放進閉包裏。

<!-- lang: js -->
String.method('deentityify', function () {
    var entity = {
        quot : '"',
        lt : '<',
        gt : '>'
    };
    return function () {
        return this.replace(/&([^&;]+);/g,
            function (a,b) {
                var r = entity[b];
                return typeof r === 'string' ? r : a;
            }
        );
    };
}());
document.writeln('&lt;&quot;&gt;'.deentityify());//<">

注意最後一行的括號,用()運算法馬上調用咱們剛剛構造出來的函數。由於返回的是函數,要讓調用deentityfy函數執行代碼,因此要加括號。 模塊模式的通常形式是:一個定義了私有變量和函數的函數,利用閉包建立能夠訪問私有變量和函數的特權函數,最後返回這個特權函數,或者把他們保存到一個可訪問到的地方。

17.級聯

讓函數返回this就能夠實現優雅的級聯。
18.柯里化

就是說一個本來的函數加上一個傳遞給它的參數,變成一個新的函數,不是很懂有什麼用? 19.記憶

這個要根據具體狀況來作,好比把一些後面須要用到的結果保存在一個數組,後面用到直接取值,就不用再去計算了。

相關文章
相關標籤/搜索