JavaScript原型與原型鏈,原型的實際應用

原型鏈是js面向對象的基礎,很是重要。
一,建立對象的幾種方法:
1,字面量
var o1 = {
    name:'o1'
};
2,構造函數
var M = function(name){
    this.name = name;
};
var o2 = new M('o2');
var a = {} 實際上是 var a = new Object()的語法糖,推薦使用前者
var a = [] 實際上是 var a = new Array()的語法糖,推薦使用前者
function Foo(){} 實際上是 var Foo = new Function()的語法糖,推薦使用前者
 
3,Object.create( Object.create()方法建立一個新對象,使用現有的對象來提供新建立的對象的__proto__。 )
var P = {name:'o3'};
var o3 = Object.create(P);

 二,原型鏈css

JavaScript 規定,全部對象都有本身的原型對象(prototype)。一方面,任何一個對象,均可以充當其餘對象的原型;另外一方面,因爲原型對象也是對象,因此它也有本身的原型。所以,就會造成一個「原型鏈」(prototype chain):對象到原型,再到原型的原型……
那麼,Object.prototype對象有沒有它的原型呢?回答是Object.prototype的原型是null。null沒有任何屬性和方法,也沒有本身的原型。所以,原型鏈的盡頭就是null。
 Object.getPrototypeOf(Object.prototype) //null

Object.prototype === Object.getPrototypeOf( {} ); //true
5條原型規則
1,全部的引用類型(數組,對象,函數),都具備對象特性,便可自由擴展屬性
2,全部的引用類型(數組,對象,函數),都有一個__proto__屬性(隱式原型),屬性值是一個普通的對象
3,全部的函數,都有一個prototype屬性(顯式原型),屬性值也是一個普通的對象
4,全部的引用類型(數組,對象,函數)__proto__屬性值指向它的構造函數的prototype屬性值
5,當試圖獲得一個引用類型(數組,對象,函數)的某個屬性時,若是這個引用類型自己沒有這個屬性,那麼會去它的__proto__(即它的構造函數的prototype)中尋找

看下圖html

 

三,instanceof原理node

判斷一個函數是不是一個變量的構造函數

工做原理:判斷實例對象的__proto__屬性和構造函數的prototype是否同一個地址,只要在原型鏈上的構造函數,都會被instanceof認爲是實例的構造函數。如圖:

    // 判斷實例對象的proto屬性和構造函數的prototype是否同一個地址
    // 只要在原型鏈上的構造函數,都會被instanceof認爲是實例的構造函數
    var M = function(name) { this.name = name; };
    var o2 = new M('o2');
    o2.__proto__ === M.prototype //true
    M.prototype.__proto__ === Object.prototype //true
    o2.__proto__.__proto__ === Object.prototype //true

    o2 instanceof M //true
    o2 instanceof Object //true

    // 用constructor屬性判斷更嚴謹
    o2.__proto__.constructor === M //true
    o2.__proto__.constructor === Object //false

四,new運算符工做原理jquery

    // new運算符及背後工做原理 new後跟構造函數
    // 1,一個新對象被建立,它繼承自func.prototype(構造函數的原型對象)
    // 2,構造函數func被執行,執行的時候,相應的傳參會被傳入,同時上下文(this)會被指定爲這個新實例
    // 3,若是構造函數返回了一個「對象」,那麼這個對象會取代整個new出來的結果,若是構造函數沒有返回對象,那麼new出來的結果爲步驟1建立的對象
    var new2 = function(func) {
        var o = Object.create(func.prototype);
        var k = func.call(o);
        if (typeof k === 'object' && k != null) {
            return k;
        } else {
            return o;
        }
    };

 五,原型鏈繼承的例子數組

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>proto</title>
</head>

<body>
    <div id="div1">123</div>
    <script>
    // 封裝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, fn) {
        var elem = this.elem;
        elem.addEventListener(type, fn);
        return this;//方便鏈式操做
    }

    var div1 = new Elem('div1');
    console.log(div1.html());
    div1.html('<p>234p</p>').on('click', function() {
        alert('1');
    }).html('<p>js</p>');
    </script>
</body>

</html>

六:原型實際應用dom

1,看一個平時使用jquery或者zepto的例子:
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>jquery</title>
</head>

<body>
    <p>jquery 1</p>
    <p>jquery 2</p>
    <p>jquery 3</p>
    <div id="div1">
        <p>jquery test in div</p>
    </div>
    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
    <script>
    var $p = $('p');
    $p.css('color', 'red'); //css是原型方法
    console.log($p.html()); //html是原型方法,這裏只打印第一個   jquery 1

    var $div = $('#div1');
    $div.find('p').css('color', 'blue'); //find,css是原型方法
    console.log($div.html()); //html是原型方法  <p style="color: blue;">jquery test in div</p>
    </script>
</body>

</html>
2,zepto如何使用原型(簡化版)-my-zepto.js
(function(window) {
     // 空對象
     var zepto = {};

     // 構造函數
     function 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) {
         // 注意,出現了 new 關鍵字
         return new Z(dom, selector);
     }

     zepto.init = function(selector) {
         // 源碼中,這裏的處理狀況比較複雜,但由於本次只針對原型,所以這裏就弱化了
         var slice = Array.prototype.slice;
         var dom = slice.call(document.querySelectorAll(selector));
         return zepto.Z(dom, selector);
     }


     // 使用zepto的$
     var $ = function(selector) {
         return zepto.init(selector);
     }
     window.$ = $;


     $.fn = {
         constructor: zepto.Z,
         css: function(key, value) {
             console.log('css');

         },
         html: function(value) {
             // console.log('html');
             return '這是一個模擬的html函數';
         }
     }

     zepto.Z.prototype = Z.prototype = $.fn; //!!!

 })(window) 
測試:新建zepto.html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>zepto</title>
</head>

<body>
    <p>zepto 1</p>
    <p>zepto 2</p>
    <p>zepto 3</p>
    <div id="div1">
        <p>zepto test in div</p>
    </div>
    <script src="./my-zepto.js"></script>
    <script>
    var $p = $('p');
    $p.css('color', 'red'); //css是原型方法
    console.log($p.html()); //html是原型方法,這是一個模擬的html函數"
    </script>
</body>

</html>
3,jquery如何使用原型(簡化版)-my-jquery.js
(function(window) {
    var jQuery = function(selector) {
        // 注意new關鍵字,第一步就找到了構造函數
        return new jQuery.fn.init(selector);
    }
    window.$ = jQuery;

    // 初始化 jQuery.fn
    jQuery.fn = jQuery.prototype = { //!!!
        constructor: jQuery,

        // 其餘函數
        css: function(key, value) {

        },
        html: function(value) {
            return 'html';

        }
    }

    // 定義構造函數-簡化版
    var init = jQuery.fn.init = function(selector) {
        var slice = Array.prototype.slice;
        var dom = slice.call(document.querySelectorAll(selector));

        var i, len = dom ? dom.length : 0;
        for (i = 0; i < len; i++) {
            this[i] = dom[i];
        }
        this.length = len;
        this.selector = selector || '';
    }
   
    // 定義原型
    init.prototype = jQuery.fn;
    
})(window)

 測試:新建jquery.html函數

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>jquery</title>
</head>

<body>
    <p>jquery 1</p>
    <p>jquery 2</p>
    <p>jquery 3</p>
    <div id="div1">
        <p>jquery test in div</p>
    </div>
    <script src="./my-jquery.js"></script>
    <script>
    var $p = $('p');
    $p.css('color', 'red'); //css是原型方法
    console.log($p.html()); //html是原型方法,這裏只打印第一個   "jquery 1"
    </script>
</body>

</html>
4,如何體現原型的擴展性-插件機制
思考:爲什麼要把原型方法放在$.fn?:擴展插件。(第一,構造函數的 prototype 確定得指向能擴展的對象;第二,$.fn 確定得指向能擴展的對象)
看一個簡單的插件的例子:
$.fn.getNodeName = function(){
    return this[0].nodeName;
}

 好處:測試

1,只有$會暴露在window全局變量
2,將插件擴展統一到 $.fn.xxx 這一個接口,方便使用ui

實踐:this

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>jquery</title>
</head>

<body>
    <p>jquery 1</p>
    <p>jquery 2</p>
    <p>jquery 3</p>
    <div id="div1">
        <p>jquery test in div</p>
    </div>
    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
    <script>
        // 插件擴展
        $.fn.getNodeName = function(){
            // console.log(this);
            console.log(this[0].nodeName);
        }
    </script>
    <script>
        // 驗證
        var $p = $('p');
        $p.getNodeName()  //P
        $div1 = $('#div1');
        $div1.getNodeName()  //DIV
    </script>
</body>

</html>
相關文章
相關標籤/搜索