js高級程序設計讀書筆記

1. js介紹

js是為了實現網絡交互而是設計的腳本語言,有以下三部分組成

  • ECMAScript,由ECMA-262定義,提供核心功能,其實web瀏覽器是其宿主環境之一,組成部分:語法,類型,語句,關鍵字,保留字,操作符,對象
  • 文檔對象模型(DOM),提供訪問和操作頁面的接口和方法,DOM把整個頁面映射為一個多層節點結構,根據其提供的API,開發人員可自如增刪改換節點。
    DOM1=DOM Core(映射文檔結構) + DOM html(對html的對象和方法)
    DOM2新模塊:
  1. DOM視圖(DOM views),跟蹤不同文檔的視圖接口
    2 .DOM事件(DOM Events),定義事件和時間處理的接口
  2. DOM樣式(DOM style),基于CSS為元素應用樣式接口
  3. DOM遍歷和范圍(DOM Traversal and Range),遍歷和操作文檔樹的接口
    DOM3新模塊:
    .DOM驗證(DOM Validation)文檔驗證方法并支持XML1.0規范
  • 瀏覽器對象模型(BOM)
    根本來說,處理瀏覽器的窗口和框架,但人們習慣將一下擴展也算成BOM的一部分
    1.彈出新瀏覽器窗口
    2.移動 關閉 縮放瀏覽器
  1. 提供瀏覽器詳細信息的navigator對象
    4.瀏覽器所加載頁面的詳細信息location對象
    5.用戶顯示器分辨率信息screen對象
    6.對cookie的支持
    7.自定義對象

2.在html中使用js

通過<script>引入,常用屬性
1.async,表示應該立即下載腳本,但不妨礙頁面的其他操作,會在頁面load之前進行
2.defer,表示文檔可以延遲到文檔全部被解析和顯示后再執行
3.src,執行代碼的外部文件
4.type,表示編寫代碼使用的腳本語言的內容類型(也成為MIME類型),一般是text/javascript(這個是默認的)
使用方式
1.直接放入內部元素,內部/script應替換成</script>,以免造成誤解
2.嵌入式
一般放在body后面,這樣在解析JS語言之前,頁面內容已經完全展示在瀏覽器上,不要出現瀏覽器明顯延遲的問題
<noscript>在不支持js的瀏覽器顯示替代的內容

3.基本概念

  • 標識符
    駝峰大小寫格式,如firstStudent
  • 注釋
    //單行
    /*
    *多行
    */
  • 嚴格模式
    "use strict"
  • 變量
    松散類型
var message; //沒初始化,因此是undefined

不使用var標識符的話就是全局變量

  • 類型typeof
    undefined,boolean, string,object,number,function
  • number:
    Infinity(正無窮)
    -Infinity(負無窮)
    isFinite()判斷是否有窮
    NaN非數值isNaN()判斷是否為數值
    將非數值轉化為數值
    Number parseInt parseFloat
  • object對象
    初始化 var o = new object();
    屬性方法
    Constructor創建當前對象的函數
    hasOwnProperty(propertyName)給定的屬性在當前實例中是否出現
    ** isPrototypeOf(object) **別傳入的對象是否為另一個對象的原型
    PropertyIsEnumerable判斷給定的屬性是否可以用 for...in 語句進行枚舉。
    toLocaleString 方法返回執行環境對應的字符串。
    toString()對象字符串表示
    valueOf()返回對象的字符串數值或布爾表示
  • 語句
    if do-while while for switch
    for-in枚舉
    lable-代碼中添加標簽,以便后來使用???
    break 跳出整個循環 continue跳出當前循環
    with 作用域設定到一個對象中

4.變量,作用域,內存問題

執行環境類型——全局,局部
其中內部環境可通過作用域鏈訪問所有的外部環境,但外部環境不能內部環境的任何變量和函數

4.1延長作用域鏈

執行下列語句時,作用域延長

try-catch
with

  • 栗子1
function buildUrl(){  
     var qs="?debug=true";  
     with(location){  
         var url=href+qs;  
     }  
     return url;  
}  
var result=buildUrl();  
alert(result);  

輸出: 靜態頁地址+qs的值。
原因:
1.由于with語句塊中作用域的‘變量對象’是只讀的,所以在他本層定義的標識符,不能存儲到本層,而是存儲到它的上一層作用域
2.with代碼塊中,javascript引擎對變量的處理方式是:先查找是不是該對象的屬性,如果是,則停止。如果不是繼續查找是不是局部變量。

var o={href:"sssss"};  
var href="1111";  
function buildUrl(){  
     var qs="?debug=true";       
     with(o){  
          href="2222";  
          var url=href+qs;  
     }      
     return url;  
}  
var result=buildUrl();  
alert(result);  
alert(href);  

輸出: 結果:2222?debug=true + 1111

4.2 JavaScript作用域之沒有塊級作用域

<script>  
function test(o){  
    var i=0;  
    if(typeof o == "object"){  
        var j=0;  
        for(var k=0; k<10; k++){  
            document.writeln("k="+k);  
        }  
        document.writeln("k2="+k);  
    }     
    document.writeln("j="+j);     
}  
  
test(new String("pppp"));  
</script>  

輸出結果為:k=0 k=1 k=2 k=3 k=4 k=5 k=6 k=7 k=8 k=9 +++k=10 j=0
這是因為,由于JavaScript中不存在塊級作用域,因此函數中聲明的所有變量,無論是在哪里聲明的,在整個函數中它們都有定義。

<script>  
var scope="global";  
function f(){  
    alert(scope);  
    var scope="local";  
    alert(scope);     
}  
  
f();  
</script> 

結果:第一個alert輸出:underfined而不是global,第二個alert輸出local
與下列函數等價

function f(){  
    var scope;  
    alert(scope);  
    var scope="local";  
    alert(scope);         
}  

5.引用類型

5.1Object類型

創建有兩種方式:
1.new操作符后跟Object構造函數

var person = new Object();
person.name = "Nicholas";
person.age = 29;

2.字面量表示法

var person = {
    name: "Nicholas",
    age: 20,
    5:true
};

這里所有的數值屬性名都會被轉換為字符串
訪問方法

alert(person.name);
alert(person[name])

5.2Arrary類型

構造
var colors = ["red","green"];
var arrays = new Array(3);//new可省略
var names = new Array["Mia"];
數組尾部添加新項
colors[colors.length] = "pink"

js判斷數組類型的方法
  • instanceof
var a=[];
console.log(a instanceof Array) //返回true 
  • constructor
    constructor 屬性返回對創建此對象的數組函數的引用
console.log([].constructor == Array);
console.log({}.constructor == Object);
console.log("string".constructor == String);
console.log((123).constructor == Number);
console.log(true.constructor == Boolean);

較為嚴謹并且通用的方法:

function isArray(object){
    return object && typeof object==='object' &&
            Array == object.constructor;
}

!!注意:

使用instaceof和construcor,被判斷的array必須是在當前頁面聲明的!比如,一個頁面(父頁面)有一個框架,框架中引用了一個頁面(子頁面),在子頁面中聲明了一個array,并將其賦值給父頁面的一個變量,這時判斷該變量,Array == object.constructor;會返回false;
原因:
1、array屬于引用型數據,在傳遞過程中,僅僅是引用地址的傳遞。
2、每個頁面的Array原生對象所引用的地址是不一樣的,在子頁面聲明的array,所對應的構造函數,是子頁面的Array對象;父頁面來進行判斷,使用的Array并不等于子頁面的Array;切記,不然很難跟蹤問題!

  • 特性判斷法
function isArray(object){
    return  object && typeof object==='object' &&    
            typeof object.length==='number' &&  
            typeof object.splice==='function' &&    
             //判斷length屬性是否是可枚舉的 對于數組 將得到false  
            !(object.propertyIsEnumerable('length'));
}

有length和splice并不一定是數組,因為可以為對象添加屬性,而不能枚舉length屬性,才是最重要的判斷因子。備注:如果 proName 存在于 object 中且可以使用一個 For…In 循環窮舉出來,那么 propertyIsEnumerable 屬性返回 true

  • 最簡單的方法
function isArray(o) {
    return Object.prototype.toString.call(o) === ‘[object Array]‘;
}

轉化方法

  • arrayObject.toLocaleString()
    返回值:
    arrayObject 的本地字符串表示。
  • toString()
    把數組轉換為字符串,并返回結果。返回值與沒有參數的 join() 方法返回的字符串相同。
    alert(colors.join(","))
  • valueOf
    valueOf()方法返回 Array 對象的原始值。實際上,為了創建這個字符串會調用數組的每一項的toString()方法。

棧方法

棧是一種LIFO(Last-In-First-Out,后進先出)的數據結構ECMAScript為數組提供了push()和pop()方法,可以實現類似棧的行為。分別添加到數組末尾和從數組末尾移除最后一項。

隊列方法

shift:從數組中把第一個元素刪除,并返回這個元素的值。
unshift: 在數組的開頭添加一個或更多元素,并返回新的長度
push:在數組的中末尾添加元素,并返回新的長度
pop:從數組中把最后一個元素刪除,并返回這個元素的值。

unshift比push要慢差不多100倍!因此,平時還是要慎用unshift,特別是對大數組。那如果一定要達到unshift的效果,可以借助于Array的reverse方法,Array的reverse的方法能夠把一個數組反轉。先把要放進數組的元素用push添加,再執行一次reverse

重排序方法

reverse()——反轉
sort()——升序排列數組

操作方法

  • concat()方法
    基于當前數組中所有項創建新數組。
var colors = ["red","green","blue"];
var colors2 = colors.concat("yellow",["black","brown"]);

alert(colors);
alert(colors2);

結果為colors為數組[“red”,”green”,”blue”];
colors2為數組[“red”,”green”,”blue”,”yellow”,”black”,”brown”];
concat()方法只是用當前數組重新創建一個新的數組,因此當前數組保持不變(colors數組不變

  • slice()方法
    slice()方法:基于當前數組中的一個或多個項創建一個新數組。
    slice()方法中可以有一個或者兩個參數(代表數組的索引值,0,1,2……)。
    接收一個參數時:返回當前數組中從此參數位置開始到當前數組末尾間所有項。
    接收兩個參數時:返回當前數組中兩個參數位置間的所有項,但不返回第二個參數位置的項。
    參數也可以為負數,表示從末尾算起,-1代表最后一個,使用方法和正數一樣。
var colors = ["red","green","blue","yellow","black","brown"];
var colors2 = colors.slice(2);
var colors3 = colors.slice(1,4);
var colors4 = colors.slice(2,-2);
var colors5 = colors.slice(-3,-1);

console.log(colors2);
console.log(colors3);
console.log(colors4);
console.log(colors5);

結果為:
[“blue”, “yellow”, “black”, “brown”]
[“green”, “blue”, “yellow”]
[“blue”, “yellow”]
[“yellow”, “black”]

  • splice()
    splice()主要用途是向當前數組的中間插入項,可以進行刪除、插入、替換操作。會返回一個數組,包含從原始項中刪除的項(若果沒有刪除,返回一個空數組)

刪除:兩個參數,刪除起始項的位置和刪除的項數。

var colors = ["red","green","blue"];
var removed = colors.splice(1,2);
alert(colors);      //red
alert(removed);     //green,blue

插入:在指定位置插入任意數量項,包括兩個基本參數(即刪除操作中的兩個參數類型)和要插入項的參數,兩個基本參數為起始位置和0(要刪除的項數應為0項),要插入的項參數可以是任意個(”red”,”green”,”blue”)。

var colors = ["red","green","blue"];
var removed = colors.splice(1,0,"yellow","orange");
alert(colors);      //"red","yellow","orange","green","blue"
alert(removed);     //空數組

替換:向指定位置插入任意數量的項同時刪除任意數量的項,插入項數和刪除項數可以不同。參數包括兩個基本參數(即刪除操作中的兩個參數類型)和要插入項的參數。

var colors = ["red","green","blue"];
var removed = colors.splice(1,1,"purple","black");
alert(colors);    //"red","purple","black","blue"
alert(removed);   //"green"                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  

位置方法

  • indexOf() 方法
    返回某個指定的字符串值在字符串中首次出現的位置。

  • lastIndexOf() 方法
    返回一個指定的字符串值最后出現的位置,在一個字符串中的指定位置從后向前搜索。

  • substr
    substr(start,length)表示從start位置開始,截取length長度的字符串。
    var src="images/off_1.png";
    alert(src.substr(7,3));
    彈出值為:off

  • substring
    substring(start,end)表示從start到end之間的字符串,包括start位置的字符但是不包括end位置的字符。
    var src="images/off_1.png";
    alert(src.substring(7,10));
    彈出值為:off

迭代方法

var arr = [3,4,5,6,7,"a"];
var isNum = function(elem,index,AAA){
return !isNaN(elem);
}
var toUpperCase = function(elem){
return String.prototype.toUpperCase.apply(elem);
}
var print = function(elem,index){
console.log(index+"."+elem);
}
  • every
    對數組中的每一項執行測試函數,直到獲得對指定的函數返回 false 的項。使用此方法 可確定數組中的所有項是否滿足某一條件,類似于&&的含義
var res = arr.every(isNum);
console.log(res);//false;
  • some
    對數組中的每一項執行測試函數,直到獲得返回 true 的項。 使用此方法確定數組中的所有項是否滿足條件.類似于||的含義
res = arr.some(isNum);
console.log(res);//true
  • filter
    對數組中的每一項執行測試函數,并構造一個新數組,返回 true的項被添加進新數組。 如果某項返回 false,則新數組中將不包含此項
res = arr.filter(isNum);
console.log(res);//[3, 4, 5, 6, 7]
  • map
    對數組中的每一項執行函數并構造一個新數組,并將原始數組中的每一項的函數結添加進新數組。
res = arr.map(toUpperCase);
console.log(res);//["3", "4", "5", "6", "7", "A"]
  • forEach
    對數組中的每一項執行函數,不返回值
res = arr.forEach(print);
console.log(res);

縮小方法

  • reduce
array.reduce(callbackfn,[initialValue])
function callbackfn(preValue,curValue,index,array){}

**preValue
**: 上一次調用回調返回的值,或者是提供的初始值(initialValue)
**curValue
**: 數組中當前被處理的數組項
**index
**: 當前數組項在數組中的索引值
**array
**: 調用 reduce()

Date類型

Date.parse接受一個字符串參數,如果可以轉化,將轉換為對應的毫秒數,否則返回 NaN;
Date.UTC最少接受兩個參數,分別表示年份和月份(0·11),其他的日期,小時(0-24)、分鐘、秒,可以指定也可以不指定,不指定時默認為 0;

dateObject.getTime() 0~... 從GTM1970年1月1日0:00:00開始計算的毫秒數。
dateObject.getYear() 70~... 指定的年份減去1900,2000年后為4位數表示的年份。
dateObject.getFullYear() 1970~... 4位數年份,適用于版本4以上的瀏覽器。
dateObject.getMonth() 0~11 一年中的月份(1月為0)。
dateObject.getDate() 1~31 一月中的日期。
dateObject.getDay() 0~6 星期(星期日為0)。
dateObject.getHours() 0~23 一天中指定的小時數,采用24小時制。
dateObject.getMinutes() 0~59 指定小時內的分鐘數。
dateObject.getSeconds() 0~59 指定分鐘內的秒數。
dateObject.setTime(val) 0~... 從GTM1970年1月1日0:00:00開始計算的毫秒數。
dateObject.setYear(val) 70~... 指定的年份減去1900,2000年后為4位數表示的年份。
dateObject.setMonth(val) 0~11 一年中的月份(1月為0)。
dateObject.setDate(val) 1~31 一月中的日期。
dateObject.setDay(val) 0~6 星期(星期日為0)。
dateObject.setHours(val) 0~23 一天中的小時數,采用24小時值。
dateObject.setMinutes(val) 0~59 指定小時內的分鐘數。
dateObject.setSecond(val) 0~59 指定小時內的秒數。

當前時間

var start = Date.now()

格式化

var d = new Date();
console.log(d); // 輸出:Mon Nov 04 2013 21:50:33 GMT+0800 (中國標準時間)
console.log(d.toDateString()); // 日期字符串,輸出:Mon Nov 04 2013
console.log(d.toGMTString()); // 格林威治時間,輸出:Mon, 04 Nov 2013 14:03:05 GMT
console.log(d.toISOString()); // 國際標準組織(ISO)格式,輸出:2013-11-04T14:03:05.420Z
console.log(d.toJSON()); // 輸出:2013-11-04T14:03:05.420Z
console.log(d.toLocaleDateString()); // 轉換為本地日期格式,視環境而定,輸出:2013年11月4日
console.log(d.toLocaleString()); // 轉換為本地日期和時間格式,視環境而定,輸出:2013年11月4日 下午10:03:05
console.log(d.toLocaleTimeString()); // 轉換為本地時間格式,視環境而定,輸出:下午10:03:05
console.log(d.toString()); // 轉換為字符串,輸出:Mon Nov 04 2013 22:03:05 GMT+0800 (中國標準時間)
console.log(d.toTimeString()); // 轉換為時間字符串,輸出:22:03:05 GMT+0800 (中國標準時間)
console.log(d.toUTCString()); // 轉換為世界時間,輸出:Mon, 04 Nov 2013 14:03:05 GMT

RegExp

RegExp詳談

function

函數內部屬性只要包括兩個特殊的對象:arguments和this。
函數屬性包括:length和prototype
函數方法(非繼承)包括:apply()和call()
繼承而來的函數方法:bind()、toString()、toLocaleString()、valueOf()

  • 沒有重載
    js不存在重載的概念,后面的方法會覆蓋先前的同名的方法。
  • 函數聲明和函數表達式
    1)函數聲明(Function Declaration);
    // 函數聲明
    function funDeclaration(type){
        return type==="Declaration";
    }

2)函數表達式(Function Expression)。

    // 函數表達式
    var funExpression = function(type){
        return type==="Expression";
    }

上面的代碼看起來很類似,感覺也沒什么太大差別。但實際上,Javascript函數上的一個“陷阱”就體現在Javascript兩種類型的函數定義上。下面看兩段代碼:

     funDeclaration("Declaration");//=> true
     function funDeclaration(type){
         return type==="Declaration";
     }
     funExpression("Expression");//=>error
     var funExpression = function(type){
         return type==="Expression";
     }

用函數聲明創建的函數funDeclaration可以在funDeclaration定義之前就進行調用;而用函數表達式創建的funExpression函數不能在funExpression被賦值之前進行調用。
代碼1段JS函數等同于:

    function funDeclaration(type){
        return type==="Declaration";
    }
    funDeclaration("Declaration");//=> true

代碼2段JS函數等同于:

    var funExpression;
    funExpression("Expression");//==>error
    funExpression = function(type){
        return type==="Expression";
    }

再來個例子

var sayHello;
    console.log(typeof (sayHey));//=>function    
    console.log(typeof (sayHo));//=>undefined
    if (true) {
        function sayHey() {
            console.log("sayHey");
        }
        sayHello = function sayHo() {
            console.log("sayHello");
    }
    } else {
        function sayHey() {
            console.log("sayHey2");
        }
        sayHello = function sayHo() {
            console.log("sayHello2");
        }
    }    
    sayHey();// => sayHey2    
    sayHello();// => sayHello

等同于

var sayHello;
    function sayHey() {
            console.log("sayHey");
        }
    function sayHey() {
            console.log("sayHey2");
    }
    console.log(typeof (sayHey));//=>function    
    console.log(typeof (sayHo));//=>undefined
    if (true) {
        //hoisting...
        sayHello = function sayHo() {
            console.log("sayHello");
    }
    } else {
        //hoisting...
        sayHello = function sayHo() {
            console.log("sayHello2");
        }
    }    
    sayHey();// => sayHey2    
    sayHello();// => sayHello

Javascript 中函數聲明和函數表達式是存在區別的,函數聲明在JS解析時進行函數提升,因此在同一個作用域內,不管函數聲明在哪里定義,該函數都可以進行調用。而函數表達式的值是在JS運行時確定,并且在表達式賦值完成后,該函數才能調用。這個微小的區別,可能會導致JS代碼出現意想不到的bug,讓你陷入莫名的陷阱中。

  • 函數的內部屬性
  • arguments
    有兩個特殊的對象:arguments和this。其中,arguments它是一個類數組對象,包含著傳入函數中的所有參數。雖然arguments的主要用途是保存函數參數,但這個對象含所有一個名叫callee的屬性,該屬性是一個指針,指向擁有這個arguments對象的函數。
    下面這個非常經典的階乘函數。
function factorial (num){
  if(num <= 1){
    return 1;
  } else{
  return num * factorial(num-1); 
  }
}

定義階乘函數一般都會用到遞歸算法,如上面代碼所示,在有函數名字,并且函數名字以后也不會改變的情況下,這種定義沒問題。但是這個函數的執行與函數名factorial緊緊耦合在了一起,為了消除這種緊密耦合現象(函數名字改變等情況),可以使用arguments.callee。

function factorial(num){
if(num<=1){
  return 1;
  } else{
    return num * arguments.callee(num-1);
  }
}

在這個重寫后的factorial()函數的函數體內,沒有再引用函數名factorial。這樣,無論引用函數時使用的是什么名字,都可以保證正常完成遞歸調用。例如:

var trueFactorial=factorial;
factorial=function()
{
    return 0;
};
 
alert(trueFactorial(5));//120
alert(factorial(5));//0

在此,變量trueFactorial獲得了factorial的值,實際上是另一個位置上保存了一個函數的指針。然后,我們又將一個簡單的返回了0的函數賦值給了factorial變量。如果像原來factorial()那樣不用使用arguments.callee,調用trueFactorial()就會返回0。可是,在解除了函數體內的代碼與函數名的耦合狀態之后,trueFactorial()仍然能夠正常的計算階乘;至于factorial(),它現在只是一個返回0的函數。

  • this
     函數內部的另一個特殊對象是this,其行為與java和c#中的this大致類似。換句話說,this引用的是函數據以執行的環境對象,或則也可以說是this值(當在網頁的全局作用中調用函數時,this對象引用的就是window)。如下例子:
window.color="red";
var o={color:"blue"};
 
function sayColor()
{
    alert(this.color);
}
sayColor();//red
 
o.sayColor=sayColor;
o.sayColor();//blue
  • apply()方法
    接收兩個參數:一個是在其中運行函數的作用域,另一個是參數數組。其中,第二個參數可以使Array的實例,也可以是arguments對象
unction sum(num1,num2)
{
    return num1+num2;
}
 
function callSum1(num1,num2)
{
    return sum.apply(this,arguments);//傳入arguments對象
}
 
function callSum2(num1,num2)
{
    return sum.apply(this,[num1,num2]);//傳入數組
}
 
alert(callSum1(10,10));//20
alert(callSum2(10,10));//20
  • call()方法
    對于call()方法而言,第一個參數是this值沒有變化,變化的是其余參數都直接傳遞給函數。換句話講,在使用call()方法時,傳遞給函數的參數必須逐個列舉出來,如下例子:
function sum(num1,num2)
{
    return num1+num2;
}
 
function callSum(num1,num2)
{
    return sum.call(this,num1,num2);
}
 
alert(callSum(10,10));//20

事實上,傳遞參數并非apply()和call()正真的用武之地;它們正真強大的地方是能夠擴充函數賴以運行的作用域。如下例子:

window.color="red";
var o={color:"blue"};
 
function sayColor()
{
    alert(this.color);
}
 
sayColor();//red
 
sayColor.call(this);//red
sayColor.call(window);//red
sayColor.call(o);//blue

這一次,sayColor()也是作為全局函數定義的,而且當在全局作用域中調用它時,它確實會顯示“red”,因為對this.color的求值會轉換成對window.color的求值。而sayColor.call(this)和sayColor.call(window),則是兩種顯示的在全局作用域中調用函數的方式,結果當然都會顯示“red”。但是,當運行sayColor.call(o)時,函數的執行環境就不一樣了,因為此時函數體內的this對象指向了o,于是結果顯示的是“blue”。
使用call()或apply()來擴充作用域的最大好處,就是對象不需要與方法有任何耦合關系。在前面例子的第一個版本中,我們是先將sayColor()函數放到了對象o中,然后再通過o來調用它;而在這里重寫的例子中,就不需要先前那個多余的步驟了。

  • bind()
window.color="red";
var o={color:"blue"};
 
function sayColor()
{
    alert(this.color);
}
 
var objectSayColor=sayColor.bind(o);
objectSayColor();//blue

在這里,sayColor()調用bind()并傳入對象o,創建了objectSayColor()函數。objectSayColor()函數的this值等于o,因此即使是在全局作用域中調用這個函數,也會看到“blue”。

函數表達式

理解作用域鏈是理解閉包的關鍵;

(1)什么是執行環境

執行環境有二種: 全局執行環境;局部執行環境---function里面;

執行流進入函數執行時,創建執行環境;函數執行結束,回收!

(2)變量對象

理解變量對象非常重要,變量對象在函數中也稱為 活動對象,我還是喜歡說成變量對象。

每一個執行環境都有一個變量對象,變量對象會保存這個執行環境的變量和方法;我們不難想到全局的變量對象是誰? window對象

我們在全局中添加變量和方法都是添加到window里。函數執行時 執行環境中就會創建變量對象;一般來說:函數調用完畢之后無論是執行環境(出棧),還是變量對象都是回收的。閉包的出現使得變量對象不回收;

(3)作用域鏈?

什么是作用域鏈:作用域鏈的本質是 指向變量對象的一組有序指針;字面上很難理解,我第一次看不明白!

有什么作用:在一個執行環境中對變量和方法進行有序(正確)訪問;

什么時候創建: 一個函數聲明的時候就創建了,作用域鏈會保存在一個函數的內部屬性[[Scope]]中;

注意:執行流進入函數是: 創建執行環境->將作用域鏈(利用[[Scope]]屬性) 復制到執行環境中->創建變量對象(活動對象)->將變量對象的引用(指針)導入作用域鏈的最前端->執行代碼

具體請看下面的代碼

  function compare(value1,value2){
         if(value1<value2){return 1;}
          else if(value1>value2){return -1;}
          else{return 0;}
       }
      var result=compare(5,10)//1

我們可以看到,作用域鏈是什么:有序指針,前面說的作用域最前端在就是0位置。 查找變量或者函數是最前端開始的,0指向的活動對象沒有你要找的變量,再去1找。0-1其實

從代碼的角度上看其實就是從函數內部一直向函數外部找標識符(變量或者函數),找到立即停止,不會再向上查找。這就是作用域鏈!

  • 閉包
    定義: 閉包是有權訪問另外一個函數作用域變量的函數;注意閉包是一個函數!
    創建閉包的主要的方法:在一個函數里面創建一個函數(閉包); 比如
<script type="text/javascript">
    function A(value){
     var num=value;
    return function(){ //閉包
     alert(num);
  }
}
var B = A(0);
alert(B()); //0
</script>

在這里匿名函數是一個閉包,一般情況下: 一個函數執行完畢之后,無論是作用域鏈還是變量對象都會回收,但是這個匿名函數的作用域鏈里有A()變量對象的引用,所以沒有消除。

還可以訪問變量對象的num;這就是閉包的好處!但是閉包的出現加大內存負擔,就這里而已,我們即使后面不再使用B函數了,A()變量對象也不會消失,javascript不知道你什么時候還會再用,當然我們可以這樣 B=null; 這樣的話匿名函數沒有引用,被回收,A()變量對象也一起回收!

《javascript高級程序設計》中尼古拉斯大神建議我們:可以不使用閉包盡量不使用,萬不得已才使用!

  • 塊級作用域

    我們都知道javascript是沒有塊級作用域的,比如{}; if(i=0){} while(){} for(){}這些都不會形成塊級作用域; 那么怎么創建 java c#類似功能的塊級作用域?

    語法:

(function(){

//塊級作用域

})();

注意: 我們創建一個匿名函數,立即調用,里面的代碼都要運行一遍,而在window中看不見,這不就是塊級作用域嗎? 還有一個好處,這個匿名函數沒有指針,

調用后回收,不會產生垃圾(里面的方法,變量都不需要再訪問的)。簡直就是完美的塊級作用域! 注意格式 (匿名函數)其實就是一個指針,再加上()就是調用了。

  • 構造函數中的閉包

    (1) 我們知道怎么為對象添加'私有變量' 這樣

function Person(name,age){
    this.name=name;//共有屬性
    this.age=age;
    
    var school="一中"; //私有屬性
    this.GetSchool=function (){return school;}
}

我們這個school是私有的變量,因為閉包的原因, 對象實例自然可以訪問這個變量; 比如 var p=new Person('nos',20); p.GetSchool(); // 一中;

 (2)現在來一個奇葩: 靜態私有屬性 ;
(function(){

         var school=""; //靜態私有;

        Person=function(name,age,value){ //構造函數

         this.name=name;this.age=age;

          school=value;

     }; 

    Person.prototype.GetSchool=function(){alert(school);}

         })();
      var p=new Person('andy',21,'一中');
      p.GetSchool();//一中
      var pp=new Person('nos',29,'二中');
      pp.GetSchool();//二中
      p.GetSchool();//二中

從結果上看 school是對象共有的,私有的屬性, 即靜態私有屬性;
我們看看構造函數是怎么定義的: 沒有使用var ,前面說過即使在函數里面定義,沒有使用var申明,就是window的,為全局的。自然可以在全局使用!

  這個匿名函數中調用了一次,只產生一個對象變量,school都是同一個,實現共享。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容