午夜国产狂喷潮在线观看|国产AⅤ精品一区二区久久|中文字幕AV中文字幕|国产看片高清在线

    JavaScript 面向?qū)ο笈c原型
    來源:易賢網(wǎng) 閱讀:1241 次 日期:2015-04-13 13:55:46
    溫馨提示:易賢網(wǎng)小編為您整理了“JavaScript 面向?qū)ο笈c原型”,方便廣大網(wǎng)友查閱!

    ECMAScript有兩種開發(fā)模式:1.函數(shù)式(過程化);2.面向?qū)ο?OOP);

    一 創(chuàng)建對象

    1.普通的創(chuàng)建對象

    // 創(chuàng)建一個對象,然后給這個對象新的屬性和方法;

    var box = new Object(); // 創(chuàng)建一個Object對象;

    box.name = 'lee'; // 創(chuàng)建一個name屬性并賦值;

    box.age = 100;

    box.run = function(){ // 創(chuàng)建一個run()方法并返回值;

    return this.name+this.age+'運行中...';

    }

    console.log(box.run()); // 輸入屬性和方法的值;

    // 缺點:想創(chuàng)建類似的對象,就會產(chǎn)生大量的代碼;

    2. 工廠模式創(chuàng)建對象

    // 這種方法就是為了解決實例化對象產(chǎn)生大量代碼重復的問題;

    function createObject(name,age){ // 集中創(chuàng)建函數(shù)體;

    var obj = new Object;         // 函數(shù)體內(nèi)創(chuàng)建Object;

    obj.name = name;

    obj.age = age;

    obj.run = function(){

    return this.name+this.age+"運行中...";

    };

    return obj;

    }

    var box1 = createObject("lee",100); // 實例化;調(diào)用函數(shù)并傳參;

    var box2 = createObject("jack",200); // 實例二;

    console.log(box1.run()+box2.run()); // 實例保持相對獨立;

    // 缺點:對象與實例的識別問題;無法搞清楚它們到底是那個對象的實例;

    console.log(typeof box1); // Object;

    3.構造函數(shù)創(chuàng)建對象

    // ECMAScript采用構造函數(shù)(構造方法)可用來創(chuàng)建特定的對象;

    function Box(name,age){          // 構造函數(shù)模式;

    this.name = name;           // this代表對象Box;

    this.age = age;

    this.run = function(){

    return this.name+this.age+"運行中...";

    };

    }

    var box1 = new Box("lee",100); // 要創(chuàng)建對象的實例必須用new操作符;

    var box2 = new Box("jack",200); // box1和box2都是Box對象的實例;

    console.log(box1 instanceof Box); // true;很清晰的識別box1從屬于Box;

    // 使用構造函數(shù),即解決了重復實例化的問題,有解決了對象識別的問題;

    使用構造函數(shù)與工廠模式不同之處:

    (1).構造函數(shù)方法沒有顯示的創(chuàng)建對象(new Object);

    (2).直接將屬性和方法賦值給this對象;

    (3).沒有return語句;1 // 構造函數(shù)規(guī)范:

    (1).函數(shù)名(function Box)和實例化構造名(new Box)相同且大寫;

    (2).通過構造函數(shù)創(chuàng)建實例對象,必須使用new運算符;

    // 構造函數(shù)和普通函數(shù)的區(qū)別:

    var box = new Box('lee',100); // 構造模式調(diào)用;

    Box('lee',200); // 普通模式調(diào)用,無效;

    var o = new Object();

    Box.call(o,'jack',200); // 對象冒充調(diào)用;

    // 將Box對象作用域擴充到對象o;Box()方法的運行環(huán)境已經(jīng)變成了對象o里;

    構造函數(shù)的問題:

    使用構造函數(shù)創(chuàng)建每個實例的時候,構造函數(shù)里的方法都要在每個實例上重新創(chuàng)建一遍;

    因為ECMAScript中的函數(shù)是對象,因此每定義一個函數(shù),也就是實例化了一個對象;

    以這種方式創(chuàng)建函數(shù),會導致不同的作用域鏈和標識符解析;

    二 原型

    // 我們創(chuàng)建的每個函數(shù)都有一個prototype(原型)屬性,這個屬性是一個對象;

    // 用途:包含可以由特定類型的所有實例共享的屬性和方法;

    // 理解:prototype是通過調(diào)用構造函數(shù)創(chuàng)建的那個對象的原型對象;

    // 使用原型的好處是可以讓所有對象實例共享它所包含的屬性和方法;

    // 也就是說,不必在構造函數(shù)中定義對象信息(屬性/方法),而是可以直接將這些信息添加到原型中;

    1.原型模式(prototype添加屬性和方法)

    1.原型模式

    function Box(){} // 聲明構造函數(shù);

    Box.prototype.name = 'Lee'; // 在原型里添加屬性和方法;

    Box.prototype.age = 100;

    Box.prototype.run = function() {

    return this.name+this.age+'運行中...';

    };

    var box1 = new Box();

    var box2 = new Box();

    console.log(box1.run==box2.run); // =>true;方法引用的地址保持一致;

    // 在原型中多了兩個屬性,這兩個原型屬性都是創(chuàng)建對象時自動生成的;

    // 1.__proto__:構造函數(shù)指向原型對象的一個指針;它的作用:指向構造函數(shù)的原型的屬性constructor;

    14// IE瀏覽器在腳本訪問__proto__會不能識別; 15

    // 判斷一個實例對象是否指向了該構造函數(shù)的原型對象,可以使用isPrototypeOf()方法來測試;

    console.log(Box.prototype.isPrototypeOf(box)); // =>true; 只要實例化對象,即都會指向;

    // 原型模式的執(zhí)行流程:

    // 1.先查找構造函數(shù)對象的實例里的屬性或方法,若有,立刻返回;

    // 2.若構造函數(shù)對象的實例里沒有,則去它的原型對象里找,若有,就返回;

    // 雖然我們可以通過對象實例訪問保存在原型中的值,但卻不能訪問通過對象實例重寫原型中的值;

    var box1 = new Box();

    console.log(box1.name); // Lee; 原型里的值;

    bo1.name = 'jack';

    console.log(box1.name); // Jack;實例自己賦的值;

    var box2 = new Box();

    console.log(box2.name); // Lee;原型里的值;沒有被box1修改;

    // 如果想要box1繼續(xù)訪問原型里的值,可以把構造函數(shù)里的屬性刪除即可;

    delete box1.name; // 刪除實例自己的屬性;

    console.log(box1.name); // Lee; 原型里原來的值;

    JavaScript 面向?qū)ο笈c原型 三聯(lián)

    名單

    2.原型與in操作符

    如何判斷屬性是在構造函數(shù)的實例里,還是在原型里? 可以用hasOwnProperty()函數(shù)來驗證;

    console.log(box.hasOwnProperty('name')); // 實例里若有返回true,否則返回false;

    in操作符會在通過對象能夠訪問給定屬性時返回true,無論該屬性存在與實例中還是原型中;

    console.log('name' in box); // =>true,存在實例中或原型中;3.更簡單的原型語法(原型+字面量模式)

    3.更簡單的原型語法(原型+字面量模式)

    function Box(){};

    Box.prototype = { // 以字面量形式創(chuàng)建包含屬性和方法的新對象;

    name:'Lee',

    age:100,

    run:function(){

    return this.name+this.age+'運行中...';

    }

    };

    // 使用構造函數(shù)創(chuàng)建原型對象和使用字面量創(chuàng)建原型對象在使用上基本相同;

    // 但是,使用字面量創(chuàng)建的原型對象使用constructor屬性不會指向?qū)嵗?而是指向原型對象Object;構造函數(shù)的方式則相反;

    var box = new Box();

    console.log(box instanceof Box);

    console.log(box instanceof Object);

    console.log(box.constructor == Box); // 字面量方式,返回false;

    console.log(box.constructor == Object); // 字面量方式,返回true;

    // 如果想讓字面量方式的constructor指向?qū)嵗龑ο?

    Box.prototype = {

    constructor:Box, // 直接強制指向即可;

    }

    // PS:字面量方式為什么constructor會指向Object?

    // 因為Box.prototype={}這種字面量寫法就是創(chuàng)建一個新對象;

    // 而每創(chuàng)建一個函數(shù),就會同時創(chuàng)建它的prototype,這個對象也會自動獲取constructor屬性;

    // 所以,新對象的constructor重寫了Box原來的constructor,因此指向了新對象,

    // 那個新對象沒有指定構造函數(shù),那么就默認為是Object;

    4.原型的動態(tài)性(重寫會覆蓋之前的內(nèi)容)

    // 原型的聲明是有先后順序的,所以,重寫的原型會切斷之前的原型;

    function Box(){};

    Box.prototype = {

    constructor:Box,

    name:'Lee',

    age:100,

    run:function(){

    return this.age+'運行中...';

    }

    };

    Box.prototype = { // 原型重寫了,覆蓋了之前的原型;

    age:200,

    run:function(){

    return this.age+'運行中...';

    }

    }

    var box = new Box();

    console.log(box.run()); // =>200運行中...;

    // 重寫原型對象切斷了現(xiàn)有原型與任何之前已經(jīng)存在的對象實例之間的聯(lián)系;對象實例引用的仍然是最初的原型;

    5.原生對象的原型

    // 原型對象不僅僅可以在自定義對象的情況下使用,而是ECMAScript內(nèi)置的引用類型都可以使用這種方式,

    // 并且內(nèi)置的引用類型本身也是用了原型;

    console.log(Array.prototype.sort); // =>function sort() { [native code] };

    console.log(String.prototype.substring); // =>function substring() { [native code] };

    6.原型對象的問題

    // 原型模式創(chuàng)建對象缺點:省略了構造函數(shù)傳參初始化這一過程,帶來的缺點就是初始化的值都是一致的;

    // 而原型最大的有點就是共享,屬性共享;

    // 但是,如果原型中的屬性包含引用類型(對象),共享就會存在一定問題;

    function Box(){};

    Box.prototype = {

    constructor:Box,

    name:'Lee',

    age:100,

    family:['father','mother'],

    run:function(){

    return this.name+this.age+this.family;

    }

    };

    var box1 = new Box();

    box1.family.push('sister'); // 為box1的family屬性添加了sister;而這個屬性被共享到原型了;

    console.log(box1.run()); // =>Lee100father,mother,sister;

    var box2 = new Box();

    console.log(box2.run()); // =>Lee100father,mother,sister;

    // 數(shù)據(jù)共享導致實例化出的數(shù)據(jù)不能保存自己的特性;

    7.組合使用構造函數(shù)模式(對象不共享的數(shù)據(jù))和原型模式(對象共享的數(shù)據(jù))

    // 為了解決構造傳參和共享問題,組合構造函數(shù)+原型模式:

    function Box(name,age){ // 不共享的使用構造函數(shù);

    this.name = name;

    this.age = age;

    this.family = ['father','moter'];

    };

    Box.prototype = { // 共享的使用原型模式;

    constructor:Box,

    run:function(){

    return this.name+this.age+this.family;

    }

    };

    // PS:這種混合模式很好的解決了傳參和引用共享的大難題;是創(chuàng)建對象比較好的方法;

    8.動態(tài)原型模式(將原型封裝到構造函數(shù)里)

    // 原型模式,不管是否調(diào)用了原型中的共享方法,它都會初始化原型中的方法;

    // 并且在聲明一個對象時,構造函數(shù)+原型讓人感覺怪異;最好把構造函數(shù)和原型封裝到一起;

    function Box(name,age){ // 將所有信息封裝到構造函數(shù)體內(nèi);

    this.name = name;

    this.age = age;

    // 當?shù)谝淮握{(diào)用構造函數(shù)時,run()方法不存在,然后執(zhí)行初始化原型;

    // 當?shù)诙握{(diào)用,就不會初始化,并且第二次創(chuàng)建新對象,原型也不會載初始化;

    // 這樣既得到了封裝,又實現(xiàn)了原型方法共享,并且屬性都保持獨立;

    if(typeof this.run != 'function'){ // 僅在第一次調(diào)用時初始化;

    Box.prototype.run = function (){

    return this.name+this.age+'運行中...';

    };

    }

    };

    var box = new Box('lee',10);

    console.log(box.run());

    // PS:使用動態(tài)原型模式,要注意一點,不可以再使用字面量的方式重寫原型,因為會切斷實例和新原型之間的聯(lián)系;

    9.寄生構造函數(shù)

    // 寄生構造函數(shù),其實就是工廠模式+構造模式;這種模式比較通用,但不能確定對象關系;

    function Box(name,age){

    var obj = new Object();

    obj.name = name;

    obj.age = age;

    obj.run = function (){

    return this.name+this.age+'運行中...';

    };

    return obj;

    }

    三 繼承

    1.原型鏈

    // 繼承是面向?qū)ο笾幸粋€比較核心的概念;

    // 其他正統(tǒng)面向?qū)ο笳Z言都會用兩種方式實現(xiàn)繼承:一個是接口實現(xiàn),一個是繼承;

    // 而ECMAScript只支持繼承,不支持接口實現(xiàn),而實現(xiàn)繼承的方式依靠原型鏈完成;

    // 實質(zhì):利用原型讓一個引用類型繼承另一個引用類型的屬性和方法;

    // 原型繼承鏈:Box ==>> Desk ==>> Table;

    function Box(){ // Box構造;

    this.name = 'Lee';

    }

    function Desk(){ // Desk構造;

    this.age = 100;

    }

    Desk.prototype = new Box(); // 通過創(chuàng)建Box實例,并賦值給Desk.prototype實現(xiàn)的;通過原型,形成鏈條;

    // 實質(zhì)是:重寫了Desk的原型對象,取而代之的是一個新類型Box的實例;

    // 也就是說原來存在于Box實例中的屬性和方法,現(xiàn)在也存在與Desk.prototype中了;

    var desk = new Desk();

    console.log(desk.age); // 100;

    console.log(desk.name); // =>Lee;

    function Table(){

    this.level = 'AAA';

    }

    Table.prototype = new Desk(); // 繼續(xù)原型鏈繼承;Table繼承了Desk;

    var table = new Table();

    console.log(table.name); // Lee;

    2.原型與實例的關系;

    // PS:以上原型鏈繼承缺少一環(huán),那就是Object,所有的構造函數(shù)都繼承自Object;

    // 而繼承Object是自動完成的,并不需要手動繼承;

    console.log(table instanceof Object); // =>true;

    console.log(desk instanceof Table); // =>false;Desk是Table的超類;

    console.log(table instanceof Desk); // =>true;

    console.log(table instanceof Box); // =>true;

    // 在JS中,被繼承的函數(shù)稱為超類型(父類,基類);

    // 繼承的函數(shù)稱為子類型(子類,派生類);

    // 繼承問題:

    // 字面量重寫原型會中斷關系;

    // 子類型無法給超類型傳遞參數(shù);

    3.借用構造函數(shù)(對象冒充)

    // 為了解決引用共享和給超類型無法傳參問題;

    // 在子類型構造函數(shù)的內(nèi)部調(diào)用超類型構造函數(shù);

    function Box(age){

    this.name = ['Lee','Jack','Hello'];

    this.age = age;

    }

    function Desk(age){

    // 繼承了Box;同時還傳遞了參數(shù);

    // 這樣一來,就會在新Desk對象上執(zhí)行Box()函數(shù)中定義的所有對象初始化代碼;

    Box.call(this,age); // 對象冒充,Desk繼承Box,并可以給超類型傳參;

    // 為了確保Box構造函數(shù)不會重寫子類型的屬性,可以在超類型構造函數(shù)后,再添加應該在子類型中定義的屬性;

    this.height = 175;

    }

    var desk = new Desk(200);               // 向Desk()函數(shù)傳參,再通過函數(shù)冒用向Box()函數(shù)傳參;

    console.log(desk.age); // =>200;

    console.log(desk.name); // =>['Lee','Jack','Hello'];

    desk.name.push('AAA'); // =>添加的新數(shù)據(jù),只添加給desk;

    console.log(desk.name); // =>['Lee','Jack','Hello','AAA'];

    4.組合繼承(原型鏈+借用構造函數(shù))

    // 借用構造函數(shù)雖然解決了引用共享和給超類型無法傳參問題,但是沒有使用原型,復用則無從談起;所以需要組合繼承模式;

    // 使用原型鏈實現(xiàn)對原型屬性和方法的繼承;

    // 通過借用構造函數(shù)來實現(xiàn)對實例屬性的繼承;

    // 這樣,既通過在原型上定義方法實現(xiàn)了函數(shù)復用,又能保證每個實例都有他自己的屬性;

    function Box(age){                   // 構造函數(shù);

    this.name = ['Lee','Jack','Hello'];

    this.age = age;

    }

    Box.prototype.run = function(){            // 原型;

    return this.name+this.age;

    }

    function Desk(age){

    Box.call(this,age); // 繼承屬性; 對象冒充; 將Box對象的作用域擴充到Desk中,Desk就會繼承Box里的屬性和方法;

    }                             

    Desk.prototype = new Box(); // 繼承方法; 原型鏈繼承;

    var desk = new Desk(100);

    console.log(desk.run()); // =>Lee,Jack,Hello100

    // 最常用的繼承模式;

    5.原型式繼承?

    // 這種繼承借助原型并基于已有的對象創(chuàng)建對象,同時還不必因此創(chuàng)建自定義類型;

    function obj(o){ // 傳遞一個字面量函數(shù);

    function F(){}; // 創(chuàng)建一個構造函數(shù);

    F.prototype = o; // 把字面量函數(shù)賦值給構造函數(shù)的原型;

    return new F(); // 返回實例化的構造函數(shù);

    }

    var box = { // 字面量對象;

    name:'Lee',

    arr:['brother','sisiter']

    };

    var box1 = obj(box);

    console.log(box1.name); // =>Lee;

    box1.name = 'Jack';

    console.log(box1.name); // =>Jack;

    console.log(box1.arr); // =>brother,sister;

    box1.arr.push('father'); //

    console.log(box1.arr); // =>brother,sister,father;

    var box2 = obj(box);

    console.log(box2.name); // =>Lee;

    console.log(box2.arr); // =>brother,sister,father;引用類型共享了;

    6.寄生式繼承?

    // 把原型式+工廠模式結合而來,目的是為了封裝創(chuàng)建對象的過程;

    // 創(chuàng)建一個僅用于封裝繼承過程的函數(shù),

    function create(o){ // 封裝創(chuàng)建過程;

    var f = obj(o);

    f.run = function(){

    return this.arr; // 同樣會共享引用;

    };

    return f;

    }

    7.寄生組合式繼承?

    // 之前說過,組合式繼承是JS最常用的繼承模式;

    // 但是,組合式繼承也有問題:

    // 超類型在使用過程中會被調(diào)用兩次:一次是創(chuàng)建子類型的時候,另一次是在子類型構造函數(shù)的內(nèi)部;

    function Box(name){

    this.name = name;

    this.arr = ['brother','sister'];

    }

    Box.prototype.run = function(){

    return this.name;

    }

    function Desk(name,age){

    Box.call(this,name); // 第二次調(diào)用Box;

    this.age = age;

    }

    Desk.prototype = new Box(); // 第一次調(diào)用Box;

    // 寄生組合式繼承:

    // 通過借用構造函數(shù)來繼承屬性,

    // 通過原型鏈的混成形式來繼承方法;

    // 解決了兩次調(diào)用的問題;

    function obj(o){

    function F(){};

    F.prototype = o;

    return new F();

    }

    function create(box,desk){

    var f = obj(box.prototype);

    f.constructor = desk;

    desk.prototype = f;

    }

    function Box(name){

    this.name = name;

    this.arr = ['brother','sister'];

    }

    Box.prototype.run = function(){

    return this.name;

    }

    function Desk(name,age){

    Box.call(this,name);

    this.age = age;

    }

    inheritPrototype(Box,Desk); // 通過這里實現(xiàn)繼承;

    var desk = new Desk('Lee',100);

    desk.arr.push('father');

    console.log(desk.arr);

    console.log(desk.run());

    var desk2 = new Desk('Jack',200);

    console.log(desk2.arr); // 兩次引用問題解決;

    四 小結

    1.創(chuàng)建對象

    對象可以在代碼執(zhí)行過程中創(chuàng)建和增強,因此具有動態(tài)性而非嚴格定義的實體;

    在沒有類的情況下,可以采用下列模式創(chuàng)建對象;

    (1).工廠模式:使用簡單的函數(shù)創(chuàng)建對象,為對象添加屬性和方法,然后返回對象;

    這個模式后來被構造函數(shù)模式所取代;

    (2).構造函數(shù)模式:可以自定義引用類型,可以像創(chuàng)建內(nèi)置對象實例一眼使用new操作符;

    缺點:它的每個成員都無法得到復用,包括函數(shù);由于函數(shù)可以不局限于任何對象,因此沒有理由不在多個對象間共享函數(shù);

    (3).原型模式:使用函數(shù)的prototype屬性來指定那些應該共享的屬性和方法;

    組合使用構造函數(shù)模式和原型模式時,使用構造函數(shù)定義實例屬性,使用原型定義共享的屬性和方法;

    2.原型鏈

    原型鏈的構建是通過將一個類型的實例賦值給另一個構造函數(shù)的原型實現(xiàn)的;

    子類型可以訪問到超類型的所有屬性和方法;

    原型鏈的問題是對象實例共享所有繼承的屬性和方法,因此不適宜單獨使用;

    解決方案:借用構造函數(shù),即在子類型構造函數(shù)的內(nèi)部調(diào)用超類型構造函數(shù);

    這樣就可以做到每個實例都具有自己的屬性,同時還能保證只使用構造函數(shù)來定義類型;

    使用最多的繼承模式是組合繼承;它使用原型鏈繼承共享的屬性和方法,而通過借用構造函數(shù)繼承實例屬性;

    3.繼承模式

    (1).原型式繼承:可以在不必預先定義構造函數(shù)的情況下實現(xiàn)繼承;其本質(zhì)是執(zhí)行對給定對象的淺復制;

    而復制得到的副本開可以得到進一步改造;

    (2).寄生式繼承:基于某個對象或某些信息創(chuàng)建一個對象,然后增強對象,最后返回對象;

    為了解決組合繼承模式由于多次調(diào)用超類型構造函數(shù)而導致的低效率問題,可以將這個模式與組合繼承一起使用;

    (3).寄生組合式繼承:集寄生式繼承和組合式繼承的有點于一身,是實現(xiàn)基于類型繼承的最有效方式;

    更多信息請查看IT技術專欄

    更多信息請查看腳本欄目
    易賢網(wǎng)手機網(wǎng)站地址:JavaScript 面向?qū)ο笈c原型

    2025國考·省考課程試聽報名

    • 報班類型
    • 姓名
    • 手機號
    • 驗證碼
    關于我們 | 聯(lián)系我們 | 人才招聘 | 網(wǎng)站聲明 | 網(wǎng)站幫助 | 非正式的簡要咨詢 | 簡要咨詢須知 | 新媒體/短視頻平臺 | 手機站點 | 投訴建議
    工業(yè)和信息化部備案號:滇ICP備2023014141號-1 云南省教育廳備案號:云教ICP備0901021 滇公網(wǎng)安備53010202001879號 人力資源服務許可證:(云)人服證字(2023)第0102001523號
    聯(lián)系電話:0871-65099533/13759567129 獲取招聘考試信息及咨詢關注公眾號:hfpxwx
    咨詢QQ:1093837350(9:00—18:00)版權所有:易賢網(wǎng)