溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

JavaScript原型和原型鏈的案例分析

發(fā)布時間:2020-12-05 11:23:20 來源:億速云 閱讀:166 作者:小新 欄目:web開發(fā)

這篇文章將為大家詳細講解有關JavaScript原型和原型鏈的案例分析,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。

1、題目

如何準確判斷一個變量是數(shù)組

寫一個原型鏈繼承的例子

繼承實現(xiàn)的其他方式

es6 實現(xiàn)繼承的底層原理是什么

描述new一個對象的過程

zepto及其他源碼中如何使用原型鏈

2.知識點

2.1 構造函數(shù)

特點:以大寫字母開頭

function Foo(name,age){
    //var obj = {}
    //this = {}
    this.name = name;
    this.age = age;
    this.class = 'class1'
    // return this
}

var f1 = new Foo('liming',19);

擴展

var o = {} 是 var o = new Object() 的語法糖

var a = [] 是 var a = new Array() 的語法糖

function Foo(){}  相當于 var Foo = new Function(){}

2.2 原型規(guī)則

五條規(guī)則:

1.所有引用類型(對象,數(shù)組,函數(shù))都具有對象特性,即可以自由擴展屬性

2.所有引用類型(對象,數(shù)組,函數(shù))都具有一個__proto__(隱式原型)屬性,是一個普通對象

3.所有的函數(shù)都具有prototype(顯式原型)屬性,也是一個普通對象

4.所有引用類型(對象,數(shù)組,函數(shù))__proto__值指向它構造函數(shù)的prototype

5.當試圖得到一個對象的屬性時,如果變量本身沒有這個屬性,則會去他的__proto__中去找

for (var key in object) {
    //高級瀏覽器中已經(jīng)屏蔽了來自原型的屬性
    //建議加上判斷保證程序的健壯性
    if (object.hasOwnProperty(key)) {
        console.log(object[key]);
    }
}

2.3 原型鏈

obj.__ proto . proto . proto __ ...

Object.prototype === null

instanceof 用于判斷引用類型屬于哪個構造函數(shù)

obj instanceob  Foo

實際意義:判斷 Foo.prototype 在不在 obj的原型鏈上

3.題目解答

3.1 如何準確判斷一個變量是數(shù)組

arr instanceof Array

3.2 寫一個原型鏈繼承的例子

封裝dom查詢

function Elem(id){
    this.elem = document.getElementById(id);
};

Elem.prototype.html = function(val){
    var elem = this.elem;
    if (val) {
        elem.innerHTML = val;
        return this;
    }else{
        return elem.innerHTML;
    }
}

Elem.prototype.on = function(type,fun){
    var elem = this.elem;
    elem.addEventListener(type,fun);
    return this;
}

var p1 = new Elem('id1');
p1.html("test").on('click',function(){
    console.log('點擊');
})

3.3 繼承實現(xiàn)的其他方式

3.3.1 原型繼承

        var obj = {
            0:'a',
            1:'b',
            arr:[1]
        }
        
        function Foo(arr2){
            this.arr2 = [1]
        }

        Foo.prototype = obj;

        var foo1 = new Foo();
        var foo2 = new Foo();

        foo1.arr.push(2);
        foo1.arr2.push(2);

        console.log(foo2.arr);  //[1,2]
        console.log(foo2.arr2); //[1]

優(yōu)點:實現(xiàn)簡單

缺點:

1.無法向父類構造函數(shù)傳參

2.同時new兩個對象時改變一個對象的原型中的引用類型的屬性時,另一個對象的該屬性也會修改。因為來自原型對象的引用屬性是所有實例共享的。

3.3.2 構造繼承

        function Super(b){
            this.b = b;
            this.fun = function(){}
        }
        function Foo(a,b){
            this.a = a;
            Super.call(this,b);
        }

        var foo1 = new Foo(1,2);
        console.log(foo1.b);

優(yōu)點:可以向父類傳參,子類不會共享父類的引用屬性

缺點:無法實現(xiàn)函數(shù)復用,每個子類都有新的fun,太多了就會影響性能,不能繼承父類的原型對象。

3.3.3 組合繼承

function Super(){
    // 只在此處聲明基本屬性和引用屬性
    this.val = 1;
    this.arr = [1];
}
//  在此處聲明函數(shù)
Super.prototype.fun1 = function(){};
Super.prototype.fun2 = function(){};
//Super.prototype.fun3...
function Sub(){
    Super.call(this);   // 核心
    // ...
}
Sub.prototype = new Super();

優(yōu)點:不存在引用屬性共享問題,可傳參,函數(shù)可復用

缺點:父類的屬性會被實例化兩次,獲取不到真正實例父類(無法區(qū)分實例是父類創(chuàng)建還是父類創(chuàng)建的)

優(yōu)化:

         function Super(b){
            this.b = b;
            this.fun = function(){}
        }

        Super.prototype.c = function(){console.log(1111)}

        function Foo(a,b){
            this.a = a;
            Super.call(this,b);
        }


        Foo.prototype = Super.prototype;
        //修復構造函數(shù):
        var foo1 = new Foo(1,2);

缺點:無法區(qū)分實例是父類創(chuàng)建還是子類創(chuàng)建的

3.3.4 寄生組合繼承

         function Super(b){
            this.b = b;
        }

        Super.prototype.c = function(){console.log(1111)}

        function Foo(a,b){
            this.a = a;
            Super.call(this,b);
        }

        var f = new Function();
        f.prototype = Super.prototype;
        Foo.prototype = new f();
        //等同于 Foo.prototype = Object.create(Super.prototype);
        
        var foo1 = new Foo(1,2);

對父類的prototype進行一次寄生,即包裝成一個空對象的prototype,再把這個對象實例化出來作為子類的peototype。

缺點:無法區(qū)分實例是父類創(chuàng)建還是子類創(chuàng)建的

可以添加以下代碼:

Foo.prototype.constructor = Foo

這種解決方法不能用于上面的組合優(yōu)化方法,因為子類父類引用的是同一個原型對象,修改會同時修改。

總結:

繼承主要是實現(xiàn)子類對父類方法,屬性的復用。

來自原型對象的引用屬性是所有實例共享的,所以我們要避免從原型中繼承屬性。

在構造函數(shù)中通過call函數(shù)可以繼承父類構造函數(shù)的屬性和方法,但是通過這種方式實例化出來的實例會將父類方法多次存儲,影響性能。

通過組合繼承我們使用call繼承屬性,使用原型繼承方法,可以解決以上兩個問題,但是通過這種方式實例化出來的對象會存儲兩份父類構造函數(shù)中的屬性。

用父類的原型構造一個新對象作為子類的原型,就解決了多次存儲的問題,所以最終的寄生組合繼承就是最佳繼承方式,它的缺點就是書寫起來比較麻煩。

3.3.6 node源碼中的繼承實現(xiàn)

function inherits(ctor, superCtor) {
  ctor.super_ = superCtor;
  ctor.prototype = Object.create(superCtor.prototype, {
    constructor: {
      value: ctor,
      enumerable: false,
      writable: true,
      configurable: true
    }
  });
}; 

function Stream(){
    //...
}

function OutgoingMessage() {
  Stream.call(this);
  //...
}

inherits(OutgoingMessage, Stream);

OutgoingMessage.prototype.setTimeout = ...

以上是寄生組合繼承的一個實例。

1.在OutgoingMessage構造函數(shù)中通過call繼承Stream構造中的屬性。

2.調(diào)用inherits方法繼承Stream原型中的屬性。

3.擴展OutgoingMessage自身原型的函數(shù)。

inherits方法中使用了Object.create方法,該方法的作用是通過指定的原型對象和屬性創(chuàng)建一個新的對象。

ctor.prototype=Object.create(superCtor.prototype,{.....});

該方法實際上就做了我們上面寄生組合繼承中的工作

var f = new Function();
f.prototype =superCtor.prototype;
return new f();

后面的參數(shù)是給原型對象添加屬性,可選屬性(非必填),即把自身作為新創(chuàng)建對象的構造函數(shù)。

value: 表示constructor 的屬性值;
writable: 表示constructor 的屬性值是否可寫;[默認為: false]
enumerable: 表示屬性constructor 是否可以被枚舉;[默認為: false]
configurable: 表示屬性constructor 是否可以被配置,例如 對obj.a做 delete操作是否允許;[默認為: false]

3.4 es6繼承的實現(xiàn)方式

參考我這篇文章:https://segmentfault.com/a/11...

3.5 描述new一個對象的過程

  1. 創(chuàng)建一個對象

  2. {}._proto_ = 構造函數(shù).prototype

  3. this指向這個對象

  4. 執(zhí)行代碼即對this賦值

  5. 返回this

3.6 zepto及其他源碼中如何使用原型鏈

var Zepto = (function(){

    var $,zepto = {}
    
    // ...省略N行代碼...
    
    $ = function(selector, context){
        return zepto.init(selector, context)
    }

    zepto.init = function(selector, context) {
        var dom
        
        // 針對參數(shù)情況,分別對dom賦值
        
        // 最終調(diào)用 zepto.Z 返回的數(shù)據(jù)
        return zepto.Z(dom, selector)
    }    

   fnction Z(dom, selector) {
      var i, len = dom ? dom.length : 0
     for (i = 0; i < len; i++) this[i] = dom[i]
      this.length = len
      this.selector = selector || ''
    }

   zepto.Z = function(dom, selector) {
     return new Z(dom, selector)
   }
  
    $.fn = {
        // 里面有若干個工具函數(shù)
    }
      
  
    zepto.Z.prototype = Z.prototype = $.fn
  
    
    // ...省略N行代碼...
    
    return $
})()

window.Zepto = Zepto
window.$ === undefined && (window.$ = Zepto)

關于JavaScript原型和原型鏈的案例分析就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

AI