js 概念,閉包,call,apply,prototype等

1,類型

javascript 簡單類型null,undefined,boolean,string,number,複雜類型爲object。js是區分大小寫的,不要Number, String, Object, Function等JavaScript內置函數混淆了。

 undefined:   代表一切未知的事物,啥都沒有,無法想象,代碼也就更無法去處理了。
                      注意:typeof(undefined) 返回也是 undefined。
                              可以將undefined賦值給任何變量或屬性,但並不意味了清除了該變量,反而會因此多了一個屬性

    null:            有那麼一個概念,但沒有東西。無中似有,有中還無。雖難以想象,但已經可以用代碼來處理了。
                      注意:typeof(null)返回object,但null並非object,具有null值的變量也並非object。

    boolean:      是就是,非就非,沒有疑義。對就對,錯就錯,絕對明確。既能被代碼處理,也可以控制代碼的流程。

    number:      線性的事物,大小和次序分明,多而不亂。便於代碼進行批量處理,也控制代碼的迭代和循環等。
                      注意:typeof(NaN)和typeof(Infinity)都返回number 。
                              NaN參與任何數值計算的結構都是NaN,而且 NaN != NaN 。
                              Infinity / Infinity = NaN 。

    string:         面向人類的理性事物,而不是機器信號。人機信息溝通,代碼據此理解人的意圖等等,都靠它了。

簡單類型都不是對象,JavaScript沒有將對象化的能力賦予這些簡單類型。直接被賦予簡單類型常量值的標識符、變量和參數都不是一個對象。

所謂“對象化”,就是可以將數據和代碼組織成複雜結構的能力。JavaScript中只有object類型和function類型提供了對象化的能力。

(以上來自

悟透JavaScripthttp://www.cnblogs.com/leadzen/archive/2008/02/25/1073404.html

)

2,函數

 

   function myfunc()
    {
        alert(
"hello");
    };
    
    alert(
typeof(myfunc));


   運行之後可以看到typeof(myfunc)返回的是function。以上的函數寫法我們稱之爲“定義式”的,如果我們將其改寫成下面的“變量式”的,就更容易理解了:

    var myfunc = function ()
        {
            alert(
"hello");
        };
    
    alert(
typeof(myfunc));


    這裏明確定義了一個變量myfunc,它的初始值被賦予了一個function的實體。因此,typeof(myfunc)返回的也是function。其實,這兩種函數的寫法是等價的,除了一點細微差別,其內部實現完全相同。也就是說,我們寫的這些JavaScript函數只是一個命了名的變量而已,其變量類型即爲function,變量的值就是我們編寫的函數代碼體。

第二種變量方式:

  var myfunc = function ()
        {
            alert(
"hello");
        };
    myfunc(); 
//第一次調用myfunc,輸出hello
    
    myfunc 
= function ()
        {
            alert(
"yeah");
        };    
    myfunc(); 
//第二次調用myfunc,將輸出yeah

第一種定義方式:

function myfunc ()
    {
        alert(
"hello");
    };
    myfunc(); 
//這裏調用myfunc,輸出yeah而不是hello
    
    
function myfunc ()
    {
        alert(
"yeah");
    };    
    myfunc(); 
//這裏調用myfunc,當然輸出yeah

兩次調用都只是最後那個函數裏輸出的值!顯然第一個函數沒有起到任何作用。JavaScript執行引擎並非一行一行地分析和執行程序,而是一段一段地分析執行的。而且,在同一段程序的分析執行中,定義式的函數語句會被提取出來優先執行。函數定義執行完之後,纔會按順序執行其他語句代碼。也就是說,在第一次調用myfunc之前,第一個函數語句定義的代碼邏輯,已被第二個函數定義語句覆蓋了。所以,兩次都調用都是執行最後一個函數邏輯了。

一段代碼中的定義式函數語句會優先執行,這似乎有點象靜態語言的編譯概念。所以,這一特徵也被有些人稱爲:JavaScript的“預編譯”。

 大多數情況下,我們也沒有必要去糾纏這些細節問題。只要你記住一點:JavaScript裏的代碼也是一種數據,同樣可以被任意賦值和修改的,而它的值就是代碼的邏輯。只是,與一般數據不同的是,函數是可以被調用執行的。

3,對象

 

var anObject = {};  //一個對象
    anObject.aProperty = "Property of object";  //對象的一個屬性
    anObject.aMethod = function(){alert("Method of object")}; //對象的一個方法
    //主要看下面:
    alert(anObject["aProperty"]);   //可以將對象當數組以屬性名作爲下標來訪問屬性
    anObject["aMethod"]();          //可以將對象當數組以方法名作爲下標來調用方法
    forvar s in anObject)           //遍歷對象的所有屬性和方法進行迭代化處理
        alert(s + " is a " + typeof(anObject[s]));


    同樣對於function類型的對象也是一樣:

    var aFunction = function() {};  //一個函數
    aFunction.aProperty = "Property of function";  //函數的一個屬性
    aFunction.aMethod = function(){alert("Method of function")}; //函數的一個方法
    //主要看下面:
    alert(aFunction["aProperty"]);   //可以將函數當數組以屬性名作爲下標來訪問屬性
    aFunction["aMethod"]();          //可以將函數當數組以方法名作爲下標來調用方法
    forvar s in aFunction)           //遍歷函數的所有屬性和方法進行迭代化處理
        alert(s + " is a " + typeof(aFunction[s]));


    是的,對象和函數可以象數組一樣,用屬性名或方法名作爲下標來訪問並處理。

   function類型的東西都是和object類型一樣的東西,這種東西被我們稱爲“對象”。我們的確可以這樣去看待這些“對象”,因爲它們既有“屬性”也有“方法”。

  JavaScript中也有this,但這個this卻與C++、C#或Java等語言的this不同。一般編程語言的this就是對象自己,而 JavaScript的this卻並不一定!this可能是我,也可能是你,可能是他,反正是我中有你,你中有我,這就不能用原來的那個“自我”來理解 JavaScript這個this的含義了。爲此,我們必須首先放下原來對象的那個“自我”。

  在JavaScript函數中,你只能把this看成當前要服務的“這個”對象。this是一個特殊的內置參數,根據this參數,您可以訪問到“這個”對象的屬性和方法,但卻不能給this參數賦值。在一般對象語言中,方法體代碼中的this可以省略的,成員默認都首先是“自己”的。但JavaScript卻不同,由於不存在“自我”,當訪問“這個”對象時,this不可省略!

JSON對象:

  JSON的形式就是用大括“{}”號包括起來的項目列表,每一個項目間並用逗號“,”分隔,而項目就是用冒號“:”分隔的屬性名和屬性值。這是典型的字典表示形式,也再次表明了 JavaScript裏的對象就是字典結構。不管多麼複雜的對象,都可以被一句JSON代碼來創建並賦值。

    其實,JSON就是JavaScript對象最好的序列化形式,它比XML更簡潔也更省空間。對象可以作爲一個JSON形式的字符串,在網絡間自由傳遞和交換信息。而當需要將這個JSON字符串變成一個JavaScript對象時,只需要使用eval函數這個強大的數碼轉換引擎,就立即能得到一個JavaScript內存對象。

new對象:

  除JSON外,在JavaScript中我們可以使用new操作符結合一個函數的形式來創建對象。例如:

    function MyFunc() {};         //定義一個空函數
    var anObj = new MyFunc();  //使用new操作符,藉助MyFun函數,就創建了一個對象


    JavaScript的這種創建對象的方式可真有意思,如何去理解這種寫法呢?
 
   其實,可以把上面的代碼改寫成這種等價形式:

    function MyFunc(){};
    
var anObj = {};     //創建一個對象
    MyFunc.call(anObj); //將anObj對象作爲this指針調用MyFunc函數

我們就可以這樣理解,JavaScript先用new操作符創建了一個對象,緊接着就將這個對象作爲this參數調用了後面的函數。其實,JavaScript內部就是這麼做的,而且任何函數都可以被這樣調用!
 

4,作用域:函數內部聲明變量的時候,一定要使用var命令。如果不用的話,你實際上聲明瞭一個全局變量!

    function f1(){
    n=1;
  }

  f1();

  alert(n); // 1
    function f1(){
    var n=1;
  }

  f1();

  alert(n); // error

5,閉包:閉包就是能夠讀取其他函數內部變量的函數。

由於在Javascript語言中,只有函數內部的子函數才能讀取局部變量,因此可以把閉包簡單理解成“定義在一個函數內部的函數”。

所以,在本質上,閉包就是將函數內部和函數外部連接起來的一座橋樑。

閉包可以用在許多地方。它的最大用處有兩個,一個是前面提到的可以讀取函數內部的變量,另一個就是讓這些變量的值始終保持在內存中。

前兩年,微軟在設計AJAX類庫的初期,用了一種被稱爲“閉包”(closure)的技術來模擬“類”。其大致模型如下:

    function Person(firstName, lastName, age)
    {
        
//私有變量:
        var _firstName = firstName;
        
var _lastName = lastName;

        
//公共變量:
        this.age = age;

        
//方法:
        this.getName = function()
        {
            
return(firstName + " " + lastName);
        };
        
this.SayHello = function()
        {
            alert(
"Hello, I'm " + firstName + " " + lastName);
        };
    };
    
    
var BillGates = new Person("Bill""Gates"53);
    
var SteveJobs = new Person("Steve""Jobs"53);
    
    BillGates.SayHello();
    SteveJobs.SayHello();
    alert(BillGates.getName() 
+ " " + BillGates.age);
    alert(BillGates.firstName);     
//這裏不能訪問到私有變量

 很顯然,這種模型的類描述特別象C#語言的描述形式,在一個構造函數裏依次定義了私有成員、公共屬性和可用的方法。特別是“閉包”機制可以模擬對私有成員的保護機制,做得非常漂亮。

    所謂的“閉包”,就是在構造函數體內定義另外的函數作爲目標對象的方法函數,而這個對象的方法函數反過來引用外層外層函數體中的臨時變量。這使得只要目標對象在生存期內始終能保持其方法,就能間接保持原構造函數體當時用到的臨時變量值。儘管最開始的構造函數調用已經結束,臨時變量的名稱也都消失了,但在目標對象的方法內卻始終能引用到該變量的值,而且該值只能通這種方法來訪問。即使再次調用相同的構造函數,但只會生成新對象和方法,新的臨時變量只是對應新的值,和上次那次調用的是各自獨立的。的確很巧妙!

    但是前面我們說過,給每一個對象設置一份方法是一種很大的浪費。還有,“閉包”這種間接保持變量值的機制,往往會給JavaSript的垃圾回收器製造難題。特別是遇到對象間複雜的循環引用時,垃圾回收的判斷邏輯非常複雜。無獨有偶,IE瀏覽器早期版本確實存在JavaSript垃圾回收方面的內存泄漏問題。再加上“閉包”模型在性能測試方面的表現不佳,微軟最終放棄了“閉包”模型,而改用“原型”模型。
 

 6,prototype

JavaScript的所有function類型的對象都有一個prototype屬性。這個prototype屬性本身又是一個object類型的對象,因此我們也可以給這個prototype對象添加任意的屬性和方法。既然prototype是對象的“原型”,那麼由該函數構造出來的對象應該都會具有這個“原型”的特性。事實上,在構造函數的prototype上定義的所有屬性和方法,都是可以通過其構造的對象直接訪問和調用的。也可以這麼說,prototype提供了一羣同類對象共享屬性和方法的機制。

 

   function Person(name)
    {
        
this.name = name;   //設置對象屬性,每個對象各自一份屬性數據
    };
    
    Person.prototype.SayHello 
= function()  //給Person函數的prototype添加SayHello方法。
    {
        alert(
"Hello, I'm " + this.name);
    }

    
var BillGates = new Person("Bill Gates");   //創建BillGates對象
    var SteveJobs = new Person("Steve Jobs");   //創建SteveJobs對象

    BillGates.SayHello();   
//通過BillGates對象直接調用到SayHello方法
    SteveJobs.SayHello();   //通過SteveJobs對象直接調用到SayHello方法

    alert(BillGates.SayHello 
== SteveJobs.SayHello); //因爲兩個對象是共享prototype的SayHello,所以顯示:true


    程序運行的結果表明,構造函數的prototype上定義的方法確實可以通過對象直接調用到,而且代碼是共享的。顯然,把方法設置到prototype的寫法顯得優雅多了,儘管調用形式沒有變,但邏輯上卻體現了方法與類的關係。

    在JavaScript中,prototype不但能讓對象共享自己財富,而且prototype還有尋根問祖的天性,從而使得先輩們的遺產可以代代相傳。當從一個對象那裏讀取屬性或調用方法時,如果該對象自身不存在這樣的屬性或方法,就會去自己關聯的prototype對象那裏尋找;如果prototype沒有,又會去prototype自己關聯的前輩prototype那裏尋找,直到找到或追溯過程結束爲止。

    在JavaScript內部,對象的屬性和方法追溯機制是通過所謂的prototype鏈來實現的。當用new操作符構造對象時,也會同時將構造函數的prototype對象指派給新創建的對象,成爲該對象內置的原型對象。對象內置的原型對象應該是對外不可見的,儘管有些瀏覽器(如Firefox)可以讓我們訪問這個內置原型對象,但並不建議這樣做。內置的原型對象本身也是對象,也有自己關聯的原型對象,這樣就形成了所謂的原型鏈。

    在原型鏈的最末端,就是Object構造函數prototype屬性指向的那一個原型對象。這個原型對象是所有對象的最老祖先,這個老祖宗實現了諸如toString等所有對象天生就該具有的方法。其他內置構造函數,如Function, Boolean, String, Date和RegExp等的prototype都是從這個老祖宗傳承下來的,但他們各自又定義了自身的屬性和方法,從而他們的子孫就表現出各自宗族的那些特徵。

    這不就是“繼承”嗎?是的,這就是“繼承”,是JavaScript特有的“原型繼承”。

 function Person(name)   //基類構造函數
 2     {
 3         this.name = name;
 4     };
 5     
 6     Person.prototype.SayHello = function()  //給基類構造函數的prototype添加方法
 7     {
 8         alert("Hello, I'm " + this.name);
 9     };
10     
11     function Employee(name, salary) //子類構造函數
12     {
13         Person.call(this, name);    //調用基類構造函數
14         this.salary = salary;
15     };
16     
17     Employee.prototype = new Person();  //建一個基類的對象作爲子類原型的原型,這裏很有意思
18     
19     Employee.prototype.ShowMeTheMoney = function()  //給子類添構造函數的prototype添加方法
20     {
21         alert(this.name + " $" + this.salary);
22     };
23 
24     var BillGates = new Person("Bill Gates");   //創建基類Person的BillGates對象
25     var SteveJobs = new Employee("Steve Jobs"1234);   //創建子類Employee的SteveJobs對象
26 
27     BillGates.SayHello();       //通過對象直接調用到prototype的方法
28     SteveJobs.SayHello();       //通過子類對象直接調用基類prototype的方法,關注!
29     SteveJobs.ShowMeTheMoney(); //通過子類對象直接調用子類prototype的方法
30 
31     alert(BillGates.SayHello == SteveJobs.SayHello); //顯示:true,表明prototype的方法是共享的

7,call,apply

call和apply,它們的作用都是將函數綁定到另外一個對象上去運行

兩者的格式和參數定義:

call( thisArg [,arg1,arg2,… ] ); // 參數列表,arg1,arg2,...

apply(thisArg [,argArray] ); // 參數數組,argArray

上面兩個函數內部的this指針,都會被賦值爲thisArg,這可實現將函數作爲另外一個對象的方法運行的目的

區分apply,call就一句話,

  foo.call(this, arg1,arg2,arg3) == foo.apply(this, arguments)==this.foo(arg1, arg2, arg3)

 

call, apply都屬於Function.prototype的一個方法,它是JavaScript引擎內在實現的,因爲屬於Function.prototype,所以每個Function對象實例,也就是每個方法都有call, apply屬性.既然作爲方法的屬性,那它們的使用就當然是針對方法的了.這兩個方法是容易混淆的,因爲它們的作用一樣,只是使用方式不同.

相同點:兩個方法產生的作用是完全一樣的

不同點:方法傳遞的參數不同

我們就上面的foo.call(this, arg1, arg2, arg3)展開分析.

foo是一個方法,this是方法執行時上下文相關對象,arg1, arg2, arg3是傳給foo方法的參數.這裏所謂的方法執行時上下文相關對象, 如果有面向對象的編程基礎,那很好理解,就是在類實例化後對象中的this.

在JavaScript中,代碼總是有一個上下文對象,代碼處理該對象之內. 上下文對象是通過this變量來體現的, 這個this變量永遠指向當前代碼所處的對象中.

call, apply作用就是借用別人的方法來調用,就像調用自己的一樣.

call, apply方法區別是,從第二個參數起, call方法參數將依次傳遞給借用的方法作參數, 而apply直接將這些參數放到一個數組中再傳遞, 最後借用方法的參數列表是一樣的.

當參數明確時可用call, 當參數不明確時可用apply給合arguments

 

 

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