web前端練習22----js中的原型對象prototype,原型鏈(重要)

原型對象 原理解析:

我們創建的每一個函數,解析器都會向函數中添加一個屬性 prototype
這個屬性對應着一個對象,就是原型對象。

如果函數作爲普通函數,調用 prototype 沒有任何作用

當函數以構造函數的形式調用時,它所創建的對象都會有一個隱含的屬性 __proto__,
我們可以通過 __proto__ 來訪問原型對象

原型對象就相當於一個公共區域,同一個構造函數的實例都能訪問到這個原型對象
我們可以將對象共有的內容,統一設置到原型對象中

創建構造函數時,可以將對象共有的屬性和方法,統一添加到構造函數的原型對象中,
這樣不用分別爲每一個對象添加,也不會影響到全局作用域,就可以使每個對象都具有這些屬性和方法了

當我們訪問對象的一個屬性或者方法時,他會先在對象自身中尋找,如果有則直接使用,
如果沒有則會去原型對象中尋找,如果有則直接使用。
如果原型對象中也沒有,則會在原型對象的原型對象中尋找,一層一層往上找,直到找到Object的原型對象,
如果沒有,就沒有這個屬性或方法

圖解:

 

下面寫一些具體例子:

拿到原型對象:

        // 拿到原型對象:
        function method1() {
            // 構造函數
            function Person() {

            }
            // 原型對象
            let yuanxing = Person.prototype;

            // 實例化對象
            let person1 = new Person();
            // 通過對象找到原型對象 person1.__proto__
            console.log(yuanxing == person1.__proto__);

            // 任何對象都可以找到原型對象 person2.__proto__
            let person2 = new Person();
            console.log(yuanxing == person2.__proto__);

        }

調用對象屬性和方法,先在自身找,自身沒有在原型對象中找

        //調用對象屬性和方法,先在自身找,自身沒有在原型對象中找
        function method2() {
            // 構造函數
            function Person() {

            }

            // 向原型對象中添加屬性和方法
            Person.prototype.name = 'zhh';
            Person.prototype.method = function (args) {
                console.log('原型對象的方法>>>' + args);
            }

            // 調用對象屬性和方法,先在自身找,自身沒有在原型對象中找
            let person1 = new Person();
            let person2 = new Person();

            console.log(person1.name = 'peson1');
            person1.method('person1調用');

            console.log(person2.name = 'perso2');
            person2.method('person2調用');

        }

原型對象也是對象,所以它也有原型(就是原型對象的原型對象)
當我們使用一個對象的屬性或方法時,會先在自身找,
自身有,則直接使用
自身沒有,則去原型對象中尋找,如果原型對象中有,則使用
如果原型對象中也沒有,則去 原型對象的原型對象 中去尋找。
以此類推向上尋找,直到找到Object的原型對象,Object 的原型對象,沒有原型對象,
如果依然沒找到則返回 unedfined

        function method3() {
            // 構造函數
            function Person() {
               
            }
            let person = new Person();
            // person 對象中沒有 toLocaleString() ; 調用的是原型對象中的方法
            person.toLocaleString();

            // 下面查找 toLocaleString() 在哪一級的原型對象裏面

            // hasOwnProperty() 對象自身是否含有某個屬性或方法
            // person 中是否有 toLocaleString 方法
            console.log(person.hasOwnProperty('toLocaleString'));

            // person的原型對象中是否有 toLocaleString 方法
            console.log(person.__proto__.hasOwnProperty('toLocaleString'));

            // person的原型對象的原型對象中是否有 toLocaleString 方法
            // 打印結果是true
            // 說明person.toLocaleString()調用的是person.__proto__.__proto__中的toLocaleString方法
            console.log(person.__proto__.__proto__.hasOwnProperty('toLocaleString'));
            }

OK,講完了

下面是一個綜合的例子:

    <script>
        // 構造函數
        function Person() {

        }
        // 拿到原型對象,原型對象是一個公共區域
        // 構造函數的實例,都能訪問到裏面的屬性和方法
        let yuanxing = Person.prototype;

        // 原型裏面添加屬性和方法
        yuanxing.name = 'zhh';
        yuanxing.say = function () {
            console.log('原型對象中的方法');
        }

        // 實例化對象
        let person1 = new Person();
        // 調屬性,先在自身找,自身沒有在原型對象裏找
        console.log(person1.name);
        // 調方法,先在自身找,自身沒有在原型對象裏找
        person1.say();

        // 調屬性或方法,自身沒有,原型對象中也沒有,一層一層往上找
        console.log(person1.toLocaleString());

    </script>

 

下面對原型對象進行深層次的解析

1、原型對象在堆棧存儲

看下面一段代碼:

    <script>
        // 創建函數時內部語句
        // this.prototype = {};
        function Dog() {
            this.test1 = function() {
                console.log('test1方法');
            }
        }
        // 拿到原型對象,並給原型對象添加方法
        Dog.prototype.test2 = function() {
            console.log('test2方法');
        }

        // 實例化對象時內部語句
        // this.__proto__=Dog.prototype
        var dog = new Dog();
        dog.test1();
        dog.test2();
        console.log(dog.toString());
        dog.test3();
    </script>

上面代碼,堆棧是示意圖,(很重要,這個圖比上面Person示意圖要詳細)

2、構造函數和原型對象的相互指向

構造函數對象的 prototype 屬性,指向原型對象
原型對象中的 constructor 指向構造函數對象

看下面一段代碼:

    <script>
        // 構造函數和原型對象的相互指向
        // 構造函數
        function Type() {

        }
        // 拿到原型對象,
        // 構造函數對象的 prototype 屬性,指向原型對象
        // 原型對象就是一個 Object 的實例
        let yuanxing = Type.prototype;
        yuanxing.name = function() {};
        // 原型對象中的 constructor 指向構造函數對象
        console.log(yuanxing.constructor == Type);

        
    </script>

圖解:

3、幾個重要概念和結論
I、兩個概念:
1.顯式原型對象  函數對象.prototype
2.隱式原型對象  實例對象.__proto__


II、Function 和Object 的三個重要結論
1、函數的原型對象都是Object的實例(但Object除外)
2、所有的函數對象都是 Function 的實例 (Function 是他自己的實例)
3、Object 的原型對象是原型鏈的盡頭

上面3個結論的驗證:

1、函數的原型對象都是Object的實例(但Object除外) 

        /* 
         * 1、函數的原型對象都是Object的實例(但Object除外)   
         */
        // Person的原型對象,是Object 實例
        console.log(Person.prototype instanceof Object);
        // Object的原型對象不是Object 實例
        console.log(Object.prototype instanceof Object);
        // Function的原型對象,是 Object 實例
        console.log(Function.prototype instanceof Object);

2所有的函數對象都是 Function 的實例 (Function 是他自己的實例)

/* 
         * 2所有的函數對象都是 Function 的實例 (Function 是他自己的實例)
         */

        {
            //  例如:Dog函數對象,內部就是 new Function()得到的
            //        所以 Dog 就是 Function 的實例
            function Dog() {

            }
            //  實例化對象
            let dog = new Dog();
            //  對象中添加方法
            dog.method = function() {
                console.log('對象中添加方法');
            }
            dog.method();
        }


        {
            // 上面的代碼可以寫成這樣
            //  Dog是構造函數對象
            let Dog = new Function();
            //  實例化對象
            let dog = new Dog();
            //  對象中添加方法
            dog.method = function() {
                console.log('對象中添加的方法2');
            }
            dog.method();
        }

        // Function是自己的實例
        // Function.prototype 的Function是函數對象
        // Function.__proto__ 的Function是實例對象
        console.log(Function.prototype == Function.__proto__);

3.Object 的原型對象是原型鏈的盡頭

        /* 
         * 3.Object 的原型對象是原型鏈的盡頭
         */
        console.log(Object.prototype.__proto__);

根據上面的三條結論驗證 instanceof  判斷時原型鏈的指向

例子1:

    // 案例1
    function Foo() {

    }
    let foo = new Foo();
    console.log(foo instanceof Foo);
    console.log(foo instanceof Object);

console.log(foo instanceof Foo); 結果是true,示意圖如下:

console.log(foo instanceof Object);結果true,示意圖如下:

例子2

    // 案例2
    console.log(Object instanceof Function);
    console.log(Object instanceof Object);
    console.log(Function instanceof Function);
    console.log(Function instanceof Object);

    function Fooo(){}
    console.log(Object instanceof Fooo);

console.log(Object instanceof Function); 結果true,示意圖如下:

console.log(Object instanceof Object);結果是true,示意圖如下:

console.log(Function instanceof Function);結果是true,示意圖如下:

console.log(Function instanceof Object);結果是true,示意圖如下:

    function Fooo(){}

    console.log(Object instanceof Fooo);結果是 false 結果如下:

OK,原型對象全部講完了

參考視頻

http://www.gulixueyuan.com/my/course/58

http://www.gulixueyuan.com/course/194

js繼承機制的設計思想

http://www.ruanyifeng.com/blog/2011/06/designing_ideas_of_inheritance_mechanism_in_javascript.html

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章