《JavaScript高級程序設計》學習筆記(三)

第六章  面向對象的程序設計

1、原型模式:我們創建的每個函數都有一個 prototype (原型)屬性,這個屬性是一個指針,指向一個對象,而這個對象的用途是包含可以由特定類型的所有實例共享的屬性和方法。

function Person(){}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
    alert(this.name);
};
var person1 = new Person();
person1.sayName(); //"Nicholas"
var person2 = new Person();
person2.sayName(); //"Nicholas"
alert(person1.sayName == person2.sayName); //true

       每當代碼讀取某個對象的某個屬性時,都會執行一次搜索,目標是具有給定名字的屬性。搜索首先從對象實例本身開始。如果在實例中找到了具有給定名字的屬性,則返回該屬性的值;如果沒有找到,則繼續搜索指針指向的原型對象,在原型對象中查找具有給定名字的屬性。如果在原型對象中找到了這個屬性,則返回該屬性的值。也就是說,在我們調用 person1.sayName() 的時候,會先後執行兩次搜索。首先,解析器會問:“實例 person1 有 sayName 屬性嗎?”答:“沒有。”然後,它繼續搜索,再問:“ person1 的原型有 sayName 屬性嗎?”答:“有。”於是,它就讀取那個保存在原型對象中的函數。當我們調用 person2.sayName() 時,將會重現相同的搜索過程,得到相同的結果。而這正是多個對象實例共享原型所保存的屬性和方法的基本原理。

2、原型鏈:其基本思想是利用原型讓一個引用類型繼承另一個引用類型的屬性和方法。

 

第七章  函數表達式

1、定義函數的方法有兩種:一是函數聲明,二是函數表達式。

  • 函數聲明:它的一個重要特徵就是函數聲明提升(function declaration hoisting),意思是在執行代碼之前會先讀取函數聲明。這就意味着可以把函數聲明放在調用它的語句後面。
function functionName(arg0, arg1, arg2) {
    //函數體
}
  • 函數表達式:函數表達式與其他表達式一樣,在使用前必須先賦值。
var functionName = function(arg0, arg1, arg2){
    //函數體
};

注意:不要這樣做!

//不要用條件語句聲明一個函數
if(condition){
    function sayHi(){
        alert("Hi!");
    }
} else {
    function sayHi(){
        alert("Yo!");
    }
}

    表面上看,以上代碼表示在 condition 爲 true 時,使用一個 sayHi() 的定義;否則,就使用另一個定義。實際上,這在 ECMAScript 中屬於無效語法,JavaScript 引擎會嘗試修正錯誤,將其轉換爲合理的狀態。但問題是瀏覽器嘗試修正錯誤的做法並不一致。大多數瀏覽器會返回第二個聲明,忽略condition ;Firefox 會在 condition 爲 true 時返回第一個聲明。因此這種使用方式很危險,不應該出現在代碼中。

//可以這樣做,通過變量來改變函數
var sayHi;
if(condition){
    sayHi = function(){
        alert("Hi!");
    };
} else {
    sayHi = function(){
        alert("Yo!");
    };
}

2、閉包:閉包是指有權訪問另一個函數作用域中的變量的函數。(一般這種最外層函數的返回值都是另一個函數)創建閉包的常見方式,就是在一個函數內部創建另一個函數。

(1)閉包與變量:

作用域鏈的這種配置機制引出了一個值得注意的副作用,即閉包只能取得包含函數中任何變量的最後一個值。閉包所保存的是整個變量對象,而不是某個特殊的變量。

function createFunctions(){
    var result = new Array();
    for (var i=0; i < 10; i++){
        result[i] = function(){
            return i;
        };
    }
    return result;
}

        這個函數會返回一個函數數組。表面上看,似乎每個函數都應該返回自己的索引值,即位置 0 的函數返回 0,位置 1 的函數返回 1,以此類推。但實際上,每個函數都返回 10。因爲每個函數的作用域鏈中都保存着 createFunctions() 函數的活動對象,所以它們引用的都是同一個變量 i 。(閉包所保存的是整個變量對象。而不是某個特殊變量)當createFunctions() 函數返回後,變量 i 的值是 10,此時每個函數都引用着保存變量 i 的同一個變量對象,所以在每個函數內部 i 的值都是 10。但是,我們可以通過創建另一個匿名函數強制讓閉包的行爲符合預期,如下所示。

function createFunctions(){
    var result = new Array();
    for (var i=0; i < 10; i++){
        result[i] = function(num){
            return function(){
                return num;
            };
        }(i);
    }
    return result;
}

      在這個版本中,我們沒有直接把閉包賦值給數組,而是定義了一個匿名函數,並將立即執行該匿名函數的結果賦給數組。這裏的匿名函數有一個參數 num ,也就是最終的函數要返回的值。在調用每個匿名函數時,我們傳入了變量 i 。由於函數參數是按值傳遞的,所以就會將變量 i 的當前值複製給參數 num 。而在這個匿名函數內部,又創建並返回了一個訪問 num 的閉包。這樣一來, result 數組中的每個函數都有自己num 變量的一個副本,因此就可以返回各自不同的數值了。

(2)關於this對象:

        在閉包中使用 this 對象也可能會導致一些問題。我們知道, this 對象是在運行時基於函數的執行環境綁定的:在全局函數中, this 等於 window ,而當函數被作爲某個對象的方法調用時, this 等於那個對象。不過,匿名函數的執行環境具有全局性,因此其 this 對象通常指向 window 。

var name = "The Window";
var object = {
    name : "My Object",
    getNameFunc : function(){
        return function(){
            return this.name;
        };
    }
};
alert(object.getNameFunc()()); //"The Window"(在非嚴格模式下)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章