JavaScript 面向?qū)ο笕腴T

JavaScript是一個(gè)動(dòng)態(tài)的通用面向?qū)ο缶幊陶Z(yǔ)言狂塘,所有的現(xiàn)代Web瀏覽器均包含了JavaScript解釋器爆捞,這使得JavaScript能夠稱得上史上使用最廣泛的編程語(yǔ)言。

特別是自2009年后,隨著Node.js 、ES5的誕生充择,使得JavaScript的功能能夠負(fù)責(zé)“全棧”匪蟀。Node.js是一個(gè)服務(wù)器端框架椎麦,基于Google的V8 JavaScript引擎創(chuàng)建。用Node.js去實(shí)現(xiàn)一層完全配置化的適配HTTP各種協(xié)議材彪,具有緩存策略的接口路由观挎,再通過(guò)配置或少量代碼實(shí)現(xiàn)接口調(diào)用聚合即可完成功能,這些工作前端工程師就能干了段化,使用javascript來(lái)提高團(tuán)隊(duì)整體工作效率嘁捷,完全不需要后端參與。況且在各種評(píng)測(cè)中显熏,看到JavaScript虛擬機(jī)比Java虛擬機(jī)快個(gè)一兩倍雄嚣,甚至幾倍已經(jīng)不是什么新鮮事了。

1996年11月喘蟆,網(wǎng)景公司將JavaScript提交給歐洲計(jì)算機(jī)制造商協(xié)會(huì)進(jìn)行標(biāo)準(zhǔn)化缓升。ECMAScript是由ECMA-262標(biāo)準(zhǔn)化的腳本語(yǔ)言的名稱。到現(xiàn)在已經(jīng)有近20個(gè)年頭了蕴轨,從ECMAScript 1版發(fā)展到了現(xiàn)在的ECMAScript 6(ES6)港谊,經(jīng)歷了5個(gè)版本的更迭(ES4被叫停);近年來(lái)橙弱,基于JavaScript各種架構(gòu)橫空出世歧寺,在后端和移動(dòng)端都有出色的表現(xiàn)。仿佛這個(gè)古老的語(yǔ)言一夜之間咸魚(yú)翻身膘螟。遙想當(dāng)年成福,取名為JavaScript無(wú)非想蹭JAVA的光,誰(shuí)曾想有今日的風(fēng)光荆残。

面向?qū)ο缶幊淌怯贸橄蠓绞絼?chuàng)建基于現(xiàn)實(shí)世界模型的一種編程模式奴艾。它使用先前建立的范例,包括模塊化内斯,多態(tài)和封裝幾種技術(shù)蕴潦。 Javascript并不是一種真正的面向?qū)ο缶幊蹋∣OP)語(yǔ)言像啼,ES6正在朝這方面努力。Javascript是一種基于對(duì)象(object-based)的語(yǔ)言潭苞,你遇到的所有東西幾乎都是對(duì)象忽冻。下面,我們來(lái)看看如何將"屬性"(property)和"方法"(method)此疹,封裝成一個(gè)對(duì)象僧诚,甚至要從原型對(duì)象生成一個(gè)實(shí)例對(duì)象。

一 . 封裝

假設(shè)我們將“人”看成一個(gè)對(duì)象蝗碎,他有名字湖笨、年齡兩個(gè)屬性。

var person={
  name:'',
  age:0
}

根據(jù)這個(gè)原型對(duì)象蹦骑,我們需要來(lái)生成一個(gè)實(shí)例對(duì)象慈省。

var person1={};
person1.name="jack";
person1.age=18;

以上就是最簡(jiǎn)單的封裝了,但這樣的寫法有一下兩個(gè)缺點(diǎn):

一是如果多生成幾個(gè)實(shí)例眠菇,這樣寫起來(lái)就非常累贅边败;

二是實(shí)例與原型之間,沒(méi)有任何辦法捎废,可以看出有什么聯(lián)系笑窜。

為了解決從原型對(duì)象生成實(shí)例的問(wèn)題,Javascript提供了一個(gè)構(gòu)造函數(shù)(Constructor)模式登疗。

對(duì)構(gòu)造函數(shù)使用new運(yùn)算符怖侦,就能生成實(shí)例,并且this變量會(huì)綁定在實(shí)例對(duì)象上谜叹。

  function person(name, age) {
        this.name = name;
        this.age = age;
    }
    //生成實(shí)例
    var person1 = new person("jack", 18);
    var person2 = new person("baby", 17);
    console.log(person1.name);//jack

這樣,person1搬葬、person2就同時(shí)擁有constructor 屬性荷腊,指向它們的構(gòu)造函數(shù)。

console.log(person1.constructor == person); //true
console.log(person2.constructor == person); //true

現(xiàn)在我們還需要為person類添加多個(gè)不變的屬性:legs_num(幾條腿),arms_num(幾只手)急凰,以及一個(gè)方法:sayHi()女仰。

  function person(name, age) {
        this.name = name;
        this.age = age;
        this.legs_num=2;
        this.arms_num=2;
        this.sayHi= function(){
          console.log("Hi,My name's "+this.name+",I'm"+this.age+"years old now.");
        };
    }

如果這樣直接加上去,有一個(gè)很大的弊端:那就是對(duì)于每一個(gè)實(shí)例對(duì)象抡锈,屬性和方法都是一樣的內(nèi)容疾忍,每一次生成一個(gè)實(shí)例,都必須為重復(fù)的內(nèi)容床三,多占用一些內(nèi)存一罩,顯得缺乏效率。

Javascript提供了一個(gè)prototype屬性撇簿,每一個(gè)構(gòu)造函數(shù)都有一個(gè)prototype屬性聂渊,指向另一個(gè)對(duì)象差购。這個(gè)對(duì)象的所有屬性和方法,都會(huì)被構(gòu)造函數(shù)的實(shí)例繼承汉嗽。我們可以把那些不變的屬性和方法欲逃,直接定義在prototype對(duì)象上。

   function person(name, age) {
        this.name = name;
        this.age = age;
    }
    person.prototype.legs_num = 2;
    person.prototype.arms_num = 2;
    person.prototype.sayHi = function() {
        console.log("Hi,My name's " + this.name + ",I'm " + this.age + " years old now.");
    };
    //生成實(shí)例
    var person1 = new person("jack", 18);
    var person2 = new person("baby", 17);
    person1.sayHi();//Hi,My name's jack,I'm 18 years old now.
    person2.sayHi();//Hi,My name's baby,I'm 17 years old now.

為了配合prototype屬性饼暑,Javascript定義了一些輔助方法:isPrototypeOf()稳析、hasOwnProperty()。

isPrototypeOf()方法用來(lái)判斷弓叛,某個(gè)proptotype對(duì)象和某個(gè)實(shí)例之間的關(guān)系彰居。alert(person.prototype.isPrototypeOf(person1)); //true

每個(gè)實(shí)例對(duì)象都有一個(gè)hasOwnProperty()方法邪码,用來(lái)判斷是否本地屬性裕菠,false值就表示繼承自prototype對(duì)象的屬性。alert(person1.hasOwnProperty("name")); //true

二 . 繼承

 function person(name, age) {
        this.name = name;
        this.age = age;
    }
    person.prototype.legs_num = 2;
    person.prototype.arms_num = 2;
    person.prototype.sayHi = function() {
        console.log("Hi,My name's " + this.name + ",I'm " + this.age + " years old now.");
    };

function Student(name, age, className){
        this.name = name;
        this.age = age;
        this.className=className;
};

現(xiàn)在闭专,我們用一個(gè)學(xué)生(Student)的構(gòu)造函數(shù)奴潘,如何讓它繼承自人(person)這個(gè)構(gòu)造函數(shù)呢?

  function person(name, age) {
        this.name = name;
        this.age = age;
    }
    person.prototype.legs_num = 2;
    person.prototype.arms_num = 2;
    person.prototype.sayHi = function() {
        console.log("Hi,My name's " + this.name + ",I'm " + this.age + " years old now.");
    };

    function Student(name, age, className) {
        person.apply(this, arguments);
        this.className = className;
    };
    var Student1 = new Student('jack', 18, 'Class 3,Grade 2');
    console.log(Student1.name); //jack
    console.log(Student1.age); //18
    console.log(Student1.className); //Class 3,Grade 2
    Student1.sayHi(); //Student1.sayHi is not a function

使用apply影钉、call簡(jiǎn)單繼承一下画髓,發(fā)現(xiàn)可以繼承到person,而person.prototype.sayHi這顯示沒(méi)有這個(gè)函數(shù)平委,這個(gè)錯(cuò)誤暫時(shí)不用理他奈虾。下面我們使用使用prototype屬性進(jìn)行繼承。

   function person(name, age) {
        this.name = name;
        this.age = age;
    }
    person.prototype.legs_num = 2;
    person.prototype.arms_num = 2;
    person.prototype.sayHi = function() {
        console.log("Hi,My name's " + this.name + ",I'm " + this.age + " years old now.");
    };


    function Student(name, age, className) {
       this.name=name;
        this.age=age;
        this.className=className;
    };
    Student.prototype = new person();
  Student.prototype.constructor = Student;

    var Student1 = new Student('jack', 18, 'Class 3,Grade 2');
    console.log(Student1.name); //jack
    console.log(Student1.age); //18
    console.log(Student1.className); //Class 3,Grade 2
    Student1.sayHi(); //Hi,My name's jack,I'm 18 years old now.

然后再修改一下代碼廉赔,看看:

  function person(name, age) {
        this.name = name;
        this.age = age;
    }
    person.prototype.legs_num = 2;
    person.prototype.arms_num = 2;
    person.prototype.sayHi = function() {
        console.log("Hi,My name's " + this.name + ",I'm " + this.age + " years old now.");
    };


    function Student(name, age, className) {
       this.name=name;
        this.age=age;
        this.className=className;
    };
    Student.prototype =person.prototype;
  Student.prototype.constructor = Student;

    var Student1 = new Student('jack', 18, 'Class 3,Grade 2');
    console.log(Student1.name); //jack
    console.log(Student1.age); //18
    console.log(Student1.className); //Class 3,Grade 2
    Student1.sayHi(); //Hi,My name's jack,I'm 18 years old now.
 alert(person.prototype.constructor); //  function Student(name, age, className)

有沒(méi)有發(fā)現(xiàn)哪里不同了肉微?Student.prototype = new person(); 改為 Student.prototype =person.prototype; 這樣做好像是少用了一個(gè)new節(jié)省了,但實(shí)際上把Animal.prototype對(duì)象的constructor屬性也改掉了蜡塌!在做繼承的時(shí)候千萬(wàn)要注意碉纳,要保護(hù)好父級(jí)的代碼不受影響。

alert(person.prototype.constructor);//function Student(name, age, className)

那么馏艾,我們將它改為:

Student.prototype = Object.create(person.prototype);

輸出來(lái)是不是又好了呢劳曹?好了,現(xiàn)在回到上面繼承第一個(gè)例子琅摩,我們可以來(lái)修補(bǔ)整段代碼了

  function person(name, age) {
        this.name = name;
        this.age = age;
    }
    person.prototype.legs_num = 2;
    person.prototype.arms_num = 2;
    person.prototype.sayHi = function() {
        console.log("Hi,My name's " + this.name + ",I'm " + this.age + " years old now.");
    };

    function Student(name, age, className) {
        person.apply(this, arguments);
        this.className = className;
    };

    Student.prototype = Object.create(person.prototype);  
    Student.prototype.constructor = Student;

    var Student1 = new Student('jack', 18, 'Class 3,Grade 2');
    console.log(Student1.name); //jack
    console.log(Student1.age); //18
    console.log(Student1.className); //Class 3,Grade 2
    Student1.sayHi(); //Student1.sayHi is not a function
    alert(person.prototype.constructor); // function person(name, age)

不要被繞暈了铁孵,跟著代碼做一遍就明白了。

最后用拷貝繼承的方式來(lái)實(shí)現(xiàn)繼承房资。這倒不是孔乙己所說(shuō)的茴字到底有幾種寫法蜕劝,有時(shí)候就需要考慮內(nèi)存的資源分配、兼容性等等,實(shí)現(xiàn)同一目標(biāo)有多種方式熙宇,可以找到最適合的一種鳖擒。

   function person(name, age) {
        this.name = name;
        this.age = age;
    }
    person.prototype.legs_num = 2;
    person.prototype.arms_num = 2;
    person.prototype.sayHi = function() {
        console.log("Hi,My name's " + this.name + ",I'm " + this.age + " years old now.");
    };

    function Student(name, age, className) {
        this.name = name;
        this.age = age;
        this.className = className;
    };

    function extend(Child, Parent) {
        var p = Parent.prototype;
        var c = Child.prototype;
        for (var i in p) {
            c[i] = p[i];
        }
        c.uber = p;
    }

    extend(Student, person);

    var Student1 = new Student('jack', 18, 'Class 3,Grade 2');
    console.log(Student1.name); //jack
    console.log(Student1.age); //18
    console.log(Student1.className); //Class 3,Grade 2
    Student1.sayHi(); //Hi,My name's jack,I'm 18 years old now.
    alert(person.prototype.constructor); // function person(name, age)

這是純粹采用"拷貝"方法實(shí)現(xiàn)繼承:把父對(duì)象的所有屬性和方法,拷貝進(jìn)子對(duì)象烫止,就能夠?qū)崿F(xiàn)繼承蒋荚。c.uber = p; 意思是為子對(duì)象設(shè)一個(gè)uber屬性,這個(gè)屬性直接指向父對(duì)象的prototype屬性馆蠕。這等于在子對(duì)象上打開(kāi)一條通道杭棵,可以直接調(diào)用父對(duì)象的方法舶赔。

最后,我們來(lái)看看普通對(duì)象是如何進(jìn)行繼承操作的。

var area{
  nation:'中國(guó)'
}

var person{
  name:'jack'
}

現(xiàn)在我們想用person去繼承area礁阁,但這兩個(gè)對(duì)象都是普通對(duì)象语稠,不是構(gòu)造函數(shù)蛤肌,無(wú)法使用構(gòu)造函數(shù)方法實(shí)現(xiàn)"繼承"恩溅。json格式的創(chuàng)始人提出了一個(gè)object()函數(shù),下面看看是如何做到這一點(diǎn)的寺酪。

var area={nation:'中國(guó)'}; 
function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
  }
 var person = object(area);
person.career = 'jack';
console.log(person.nation);//中國(guó)

下面我再給添加一個(gè)"出生地"屬性坎背,它的值是一個(gè)數(shù)組。

area.birthPlaces = ['北京','上海','香港'];
    var area = {
        nation: '中國(guó)',
      birthPlaces:['北京', '上海', '香港']
    }; 
    function object(o) {    
        function F() {}    
        F.prototype = o;    
        return new F();  
    } 
    var person = object(area);
    person.career = 'jack';
    person.birthPlaces.push('廣州');
    console.log(person.nation); //中國(guó)

    console.log(area.birthPlaces);//["北京", "上海", "香港", "廣州"]
    console.log(person.birthPlaces);//["北京", "上海", "香港", "廣州"]

但是寄雀,這樣的拷貝有一個(gè)問(wèn)題得滤。那就是,如果父對(duì)象的屬性等于數(shù)組或另一個(gè)對(duì)象盒犹,因此存在父對(duì)象被篡改的可能懂更。上面提醒過(guò),繼承要保護(hù)好父級(jí)的代碼不受影響急膀。

請(qǐng)看沮协,現(xiàn)在給area添加一個(gè)"出生地"屬性,它的值是一個(gè)數(shù)組卓嫂。

下面使用深拷貝進(jìn)行繼承:

var area = {
        nation: '中國(guó)',
        birthPlaces: ['北京', '上海', '香港']
    };  
    function deepCopy(p, c) {    
        var c = c || {};    
        for (var i in p) {      
            if (typeof p[i] === 'object') {        
                c[i] = (p[i].constructor === Array) ? [] : {};        
                deepCopy(p[i], c[i]);      
            } else {         
                c[i] = p[i];      
            }    
        }    
        return c;  
    }

    var person = deepCopy(area);
    person.career = 'jack';
    person.birthPlaces.push('廣州');
    console.log(person.nation); //中國(guó)

    console.log(area.birthPlaces); //["北京", "上海", "香港"]
    console.log(person.birthPlaces); //["北京", "上海", "香港", "廣州"]

把父對(duì)象的屬性皂股,全部拷貝給子對(duì)象,也能實(shí)現(xiàn)繼承命黔。同時(shí)又不會(huì)影響到父對(duì)象的數(shù)據(jù)。jQuery庫(kù)使用的就是這種繼承方法就斤。

畫了一個(gè)簡(jiǎn)單的圖悍募,希望能對(duì)你了解這篇文章有幫助:

oop.png

參考資料:

維基百科

MDN JavaScript

阮一峰的網(wǎng)絡(luò)日志:Javascript 面向?qū)ο缶幊?/p>

《JavaScript 權(quán)威指南》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市洋机,隨后出現(xiàn)的幾起案子坠宴,更是在濱河造成了極大的恐慌,老刑警劉巖绷旗,帶你破解...
    沈念sama閱讀 210,914評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件喜鼓,死亡現(xiàn)場(chǎng)離奇詭異副砍,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)庄岖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門豁翎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人隅忿,你說(shuō)我怎么就攤上這事心剥。” “怎么了背桐?”我有些...
    開(kāi)封第一講書人閱讀 156,531評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵优烧,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我链峭,道長(zhǎng)畦娄,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 56,309評(píng)論 1 282
  • 正文 為了忘掉前任弊仪,我火速辦了婚禮熙卡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘撼短。我一直安慰自己再膳,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布曲横。 她就那樣靜靜地躺著喂柒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪禾嫉。 梳的紋絲不亂的頭發(fā)上灾杰,一...
    開(kāi)封第一講書人閱讀 49,730評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音熙参,去河邊找鬼艳吠。 笑死,一個(gè)胖子當(dāng)著我的面吹牛孽椰,可吹牛的內(nèi)容都是我干的昭娩。 我是一名探鬼主播,決...
    沈念sama閱讀 38,882評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼黍匾,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼栏渺!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起锐涯,我...
    開(kāi)封第一講書人閱讀 37,643評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤磕诊,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體霎终,經(jīng)...
    沈念sama閱讀 44,095評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡滞磺,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了莱褒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片击困。...
    茶點(diǎn)故事閱讀 38,566評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖保礼,靈堂內(nèi)的尸體忽然破棺而出沛励,到底是詐尸還是另有隱情,我是刑警寧澤炮障,帶...
    沈念sama閱讀 34,253評(píng)論 4 328
  • 正文 年R本政府宣布目派,位于F島的核電站,受9級(jí)特大地震影響胁赢,放射性物質(zhì)發(fā)生泄漏企蹭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評(píng)論 3 312
  • 文/蒙蒙 一智末、第九天 我趴在偏房一處隱蔽的房頂上張望谅摄。 院中可真熱鬧,春花似錦系馆、人聲如沸送漠。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,715評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)闽寡。三九已至,卻和暖如春尼酿,著一層夾襖步出監(jiān)牢的瞬間爷狈,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,945評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工裳擎, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留涎永,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,248評(píng)論 2 360
  • 正文 我出身青樓鹿响,卻偏偏與公主長(zhǎng)得像羡微,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子惶我,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容