面向對象的JavaScript——類

在java裏,咱們定義類的時候用的是class關鍵字,可是JavaScript中class是保留字,另有用途,因此咱們要採用其餘的方法來定義JavaScript中的類。javascript

定義類java

利用JavaScript中的function關鍵字,類名首字母通常採用大寫,如:瀏覽器

function Person(id,name,age){
    this.id = id;
    this.name = name;
    this.age = age;
}

回想java的類中會有一個與該類同名的構造函數,即便開發者本身沒有寫,java虛擬機也會給出一個默認的構造函數。而JavaScript這裏,上面這個類自己其實也是一個構造函數。這裏記住一句話: 「構造函數爲對象的‘類’提供一個名字,並初始化屬性。」咱們能夠用下面的方法來使用這個類:

var per = new Person("001","Xiao Wei","12");

上面那個構造函數裏的this指針(嚴格意義上this不是指針,只是咱們把它想象爲指針會容易理解一些)指向的變量便是Person類的數據成員(或者叫屬性),訪問權限是public。java的類中,除了有屬性以外,還有方法(也就是c裏的函數)。下面咱們爲JavaScript類添加函數。閉包

function Person(id,name,age){
    this.id = id;
    this.name = name;
    this.age = age;
    this.getInformation = function(){
        return "編號:"+this.id+";姓名:"+this.name+";年齡:"+this.age;
    };
}

上面是一種最直接的辦法來爲類添加屬性,然而這不是好的辦法,緣由在於對客觀事物的描述不正確,由於屬性是每一個實例(也就是對象)都有一份,而方法應該不須要,由於方法是對數據進行處理,是公用的,因此,更好的解決辦法採用prototype原型方式。

function Person(id,name,age){
    this.id = id;
    this.name = name;
    this.age = age;
}
Person.prototype.getInformation = function(){
    return "編號:"+this.id+";姓名:"+this.name+";年齡:"+this.age;
};

這裏之因此可以採用原型方式是由於 每一個JavaScript對象都包含着對它的原型對象的內部引用。其實不只方法,屬性也能夠採用prototype原型方式。這裏再記住一句話: 「方法和其餘不變屬性放在原型對象中。」

類已經經過function關鍵字實現了,這是一種實現方式,這種方式實現起來簡單、通俗易懂,其實還有一種實現方式,實現起來很是優雅,咱們看下面代碼:併發

var Class={
    create:function(){
        return function(){
            this.inital.apply(this,arguments);
        };
    }
};
/*定義Person類*/
var Person=Class.create();
Person.prototype={
    inital:function(id,name,age){
        this.id = id;
        this.name = name;
        this.age = age;
    },
    getInformation:function(){
        return "編號:"+this.id+";姓名:"+this.name+";年齡:"+this.age;
    }
};

關鍵代碼在於:this.inital.apply(this,arguments);這樣一行,Class自己是一個對象,有一個方法叫作create,此方法返回一個函數指針,其中函數體內執行this.inital.apply(this,arguments);this指向是的是當前對象,在這裏就是Person,apply方法是更改initial方法的做用域,arguments是參數列表。關於apply更詳細的解釋能夠參見我以前的學習筆記《JavaScript中call()與apply()有什麼區別?》app

this函數

關於this的詳細用法我已在讀書筆記《JavaScript中的this如何使用》中有介紹。這裏再囉嗦幾小句。oop

在JavaScript中,並無嚴格的面向對象概念,天然也沒有類的構造函數這樣的概念。var o=new Obj();這樣的語法,看起來彷佛和Java/C++至關相似,可是它背後的執行過程是不一樣的。首先,解釋器會new一個空的Object對象。而後將這個空的Object,做爲隱藏的參數傳遞給function Obj()。在Obj函數中訪問到的this,其實就是這個傳入的空的Object 對象。這就是所謂:「this關鍵字關聯於執行時的做用域」的含義。學習

若是你想把一個函數做爲「構造函數」,那麼就不要在函數的最後加上return語句。由於若是沒有return語句,new算符返回的就是那個被操做過之後的this。一旦你經過return返回了別的東西,這個this就被廢棄掉了。this

匿名類

var class1 = {p1:value1,p2:value2};

這個也能夠寫成

var class1 = {};
class1.p1 = value1;
class1.p2 = value2;

首先全部的匿名類都是繼承於Object核心對象的,var class1={} 意味着實例化了一個Object對象,它擁有Object對象的原生屬性和原生方法。可是不能爲匿名類添加原生方法,例如這樣寫是錯誤的:

class1.prototype.func1 = function(){};

你也不能嘗試用new() 方法來構造一個新的與class1有相同的屬性的新對象,由於它已經實例化了。如下寫法也是錯的:

var classB = new classA();

這是沒法構造的,

準確的說,匿名類其實是繼承於Object的某一個實例,至關於C#中的靜態類。你能夠爲它添加方法和屬性。例如:

class1.func1 = function(){};

調用的時候就這樣:

class1.func1();  //酷似C#中的靜態類

可是你能夠爲Object添加原生函數,這樣你的匿名類(其實是全部的類)都有這個方法。例如:

var class1 = {};
class1.p1 = value1;
class1.p2 = value2;
Object.prototype.func1 = function(){ alert("1") };
class1.func1();

是沒有問題的,可是這樣一來,全部的實例化對象都有了func1()方法。實際應用中應該避免給Object類添加原生方法。

匿名函數

先說說關於Javascript的函數:能夠這樣說,JavaScript中一切皆是對象,function天然不例外,function能夠做爲函數,類,也能夠當成一個被函數對象返回。

看下面的例子:

function a(){
    alert("Hello Febird!");
    this.aa = "aa";
    this.show = function(){
        alert(this.aa);
    };
    this.sayHello = function(){
        return function(){alert("hello");};
    };
}

var aaa = new a();
aaa.show();
aaa.sayHello();

其中最外面的一個function是定義了一個類 a ,它有屬性aa,方法show(),sayHello();這兩個都是匿名函數,而sayHello中的function即是函數做爲一個返回值的例子。

實際上能夠這樣想,匿名函數就是一塊沒有命名的代碼塊,當把它賦值給別的變量的時候,那麼那個變量就是一個函數,準確的說那是一個函數指針。

在JavaSript中,匿名函數是頗有特色的東西了,也是很是有用,也是有些難以理解的。

好比在寫Ajax引用的時候,若是不依靠別的JSF,本身寫一個通用的Ajax話,通常這樣寫:

var xhr = new XMLHttpRequest(); //已經封裝,能夠適應不一樣的瀏覽器;
function DoAjax(){
    xhr.onreadystatechange=processFunction;
    xhr.open("GET",url,true);
    xhr.send(null);
}
function processFunction(){
    //do something with XMLHttpRequest;
    if(xhr.readState!=4||xhr.status!=200) return false;
    alert(xhr.responseText);
}

在通常的Ajax引用中,也許只要一個XMLHttpRequest對象,並且onreadystatechange的處理函數必須沒有參數,有參數就出錯,因此,通常常常會寫一個全局變量XMLHttpRequest,再在processFunction中用到這個全局變量,可是若是我要創建幾個XMLHttpRequest的併發鏈接怎麼辦呢?這個就不能用全局變量了,可是處理函數又不能有參數,怎麼搞,能夠這樣: 

function DoAjax(){
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = processFunction(xhr);
    xhr.open("GET",url,true);
    xhr.send(null);
}
function processFunction(_xhr){
    return function(){
        //do something with XMLHttpRequest;
        if(_xhr.readState!=4||_xhr.status!=200) return false;
        alert(_xhr.responseText);
    };
}

怎麼理解?雖然processFunction函數有參數,可是它返回的函數沒有參數!而這兩個函數之間是怎麼進行的值傳遞呢?

這裏不妨引用一句話:

「爲了函數可以正確的執行,須要被函數使用的,詞法做用域中的,非全局數據,存在於函數的閉包之中。」

能夠這樣理解:

當咱們把processFunction()返回的函數,在processFunction以外使用的時候,依然要記得本身被定義時的上級做用域中的各類變量的值。這些須要被記住的值,就是「閉包」。

原生對象

原生,即prototype,它提供了擴展、改造原有對象的方法。例如咱們能夠爲已知對象,包括JavaScript的核心對象Array,Number,Math,Object,Boolean等和自定義類添加方法或者屬性。

例如:

Number.prototype.toHexString = function () {
    return this.toString(16);
};
var num = 10;
alert(num.toHexString());

輸出A;

你能夠爲Object對象添加方法,這樣,之後任意一個對象都有這個方法,由於其它對象都是從Object繼承而來的。

你也能夠再造現有函數

Function.prototype.toString = function () {
    return 「Function Locked」;
};

 

參考資料

1. javascript實現類、繼承、多態(原創)

2. JavaScript面向對象---匿名函數和匿名類,以及原生類

相關文章
相關標籤/搜索