首頁  >  文章  >  web前端  >  JavaScript原型和原型鏈的方法介紹(程式碼範例)

JavaScript原型和原型鏈的方法介紹(程式碼範例)

不言
不言轉載
2019-01-10 10:59:442723瀏覽

這篇文章帶給大家的內容是關於JavaScript原型和原型鏈的方法介紹(程式碼範例),有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。

1、題目

如何準確判斷一個變數是數組

寫一個原型鏈繼承的範例

繼承實作的其他方式

es6 實作繼承的底層原理是什麼

描述new一個物件的過程

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

#2.知識點

2.1 建構子

特點:以大寫字母開頭

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 原型規則

五條規則:

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

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

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

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

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

for (var key in object) {
    //高级浏览器中已经屏蔽了来自原型的属性
    //建议加上判断保证程序的健壮性
    if (object.hasOwnProperty(key)) {
        console.log(object[key]);
    }
}

2.3 原型鏈

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

Object.prototype === null

instanceof 用來判斷引用型別屬於​​哪個建構子

obj instanceob  Foo

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

3.題目解答

3.1 如何精確判斷一個變數是陣列

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 繼承實作的其他方式

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]

優點:實作簡單

##缺點:

1.無法向父類別建構子傳參

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);

優點:可以傳參父類別給父類別的參考屬性

缺點:無法實作函式重複使用,每個子類別都有新的fun,太多了就會影響效能,不能繼承父類別的原型物件。

3.3.3 組合繼承

function Super(){
    // 只在此处声明基本属性和引用属性
    this.val = 1;
    this.arr = [1];
}
//  在此处声明函数
Super.prototype.fun1 = function(){};
Super.prototype.fun2 = function(){};
//Super.prototype.fun3...
function Sub(){
    Super.call(this);   // 核心
    // ...
}
Sub.prototype = new Super();

優點:不存在引用屬性共享問題,可傳參,函數可重複使用

缺點:父類別的屬性會被實例化兩次,取得不到真正實例父類別(無法區分實例是父類別建立還是父類別建立的)

最佳化:

         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;
        //修复构造函数:
        var foo1 = new Foo(1,2);
缺點:無法區分實例是父類別建立還是子類別所建立的

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。

缺點:無法區分實例是父類別建立還是子類別建立的

可以新增以下程式碼:

Foo.prototype.constructor = Foo
這種解決方法不能用於上面的組合最佳化方法,因為子類父類引用的是同一個原型對象,修改會同時修改。

總結:

繼承主要是實作子類別對父類別方法,屬性的複用。

來自原型物件的參考屬性是所有實例共享的,所以我們要避免從原型繼承屬性。

在建構子中透過call函數可以繼承父類別建構子的屬性和方法,但是透過這種方式實例化出來的實例會將父類別方法多次存儲,影響效能。

透過組合繼承我們使用call繼承屬性,使用原型繼承方法,可以解決以上兩個問題,但是透過這種方式實例化出來的物件會儲存兩份父類別建構子中的屬性。

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

3.3.6 node原始碼中的繼承實作

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建構子中透過call繼承Stream建構中的屬性。

2.呼叫inherits方法繼承Stream原型中的屬性。

3.擴充OutgoingMessage自身原型的函數。

inherits方法中使用了Object.create方法,該方法的作用是透過指定的原型物件和屬性來建立新的物件。

ctor.prototype=Object.create(superCtor.prototype,{.....});
該方法實際上就做了我們上面寄生組合繼承中的工作

var f = new Function();
f.prototype =superCtor.prototype;
return new f();
後面的參數是給原型對象添加屬性,可選屬性(非必填),即把自身作為新建立物件的建構子。

value: 表示constructor 的属性值;
writable: 表示constructor 的属性值是否可写;[默认为: false]
enumerable: 表示属性constructor 是否可以被枚举;[默认为: false]
configurable: 表示属性constructor 是否可以被配置,例如 对obj.a做 delete操作是否允许;[默认为: false]
3.4 es6繼承的實作方式

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

3.5 描述new一个对象的过程

  1. 创建一个对象

  2. {}._proto_ = 构造函数.prototype

  3. this指向这个对象

  4. 执行代码即对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
        
        // 针对参数情况,分别对dom赋值
        
        // 最终调用 zepto.Z 返回的数据
        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 = {
        // 里面有若干个工具函数
    }
      
  
    zepto.Z.prototype = Z.prototype = $.fn
  
    
    // ...省略N行代码...
    
    return $
})()

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

以上是JavaScript原型和原型鏈的方法介紹(程式碼範例)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:segmentfault.com。如有侵權,請聯絡admin@php.cn刪除