JavaScript 對象(三)

一:原型

1、原型的定義: 原型是function對象的一個屬性,它定義了構造函數製造出的對象的公共祖先。通過改構造函數產生的對象,可以繼承該原型的屬性和方法。原型也是對象。

  2、利用原型特點和概念,可以提取共有屬性。將一類對象的共有屬性提取出來,放到該類對象的原型中,從而不需要每次用new操作符時都重新定義一遍該共有屬性。

  如下,定義一個Person構造函數,而屬於Person多構造對象共有的屬性方法,則定義到Person的原型中

Person.prototype = {
        eat: function (food) {
           console.log('I have eated ' + food);
        },
        sleep: function () {
          console.log("I am sleeping");
        }
      }
      // 人的構造函數
      function Person (name, age) {
        this.name = name;
        this.age = age;
      }
      var person1 = new Person('lyl', 18);
      console.log(person1.name); //lyl
      person1.eat('apple'); //I have eated apple

3、如何查看原型:
之前是不允許我們查看構造函數的原型的,但後來提供了一個可查看構造函數原型的接口:隱士屬性proto(其實我們能夠訪問原型的屬性,或者說繼承原型,靠的就是proto屬性連接着構造函數和原型,可以說沒有proto屬性的存在,就無法實現原型的繼承)

(1)、首先我們先說明一下proto這個接口是存放到哪裏的

 看過以上對象創建過程的都應該知道在用new創建一個對象時,內部會隱士自動創建一個this的對象,進過一系列處理後再隱士將this對象返回。而proto就對於隱士創建的this對象中,如下代碼:

// 原型
    Person.prototype = {
      say: function () {
        console.log("I am saying ");
      },
      play: function () {
        console.log("I am playing");
      }
    }
    // 構造函數
    function Person (name) {

      // var this = Object.create(Person.prototype);
      // p.s.在隱士創建的this對象中存在一個屬性,即__proto__,該屬性存儲了Person.prototype

      this.name = name;

      // return this;
    }
    // 對象的創建
    var person1 = new Person('lyl');
    // 打印原型
    console.log(person1.__proto__);

(2)、如何查看原型:直接通過new操作符創建的對象訪問proto屬性即可,如上代碼演示

4、如何查看對象的構造函數,我們通過屬性constructor來查看:

contructor屬性位於構造函數的原型中,其中存儲的是構造函數信息,所以在不知道原型的情況下,由原型繼承原理,我們可以用實例對象來直接訪問constructor,即獲取創建該實例的構造函數

 function Person () {
           this.name = 'myName';
           this.age = 18;
       }
       var person = new Person();
       console.log(person.constructor); // function Perso(){...}

二:原型鏈:

1、定義:顧名思義,原型鏈就是將一個個原型串連起來,形成一條原型繼承的鏈子。

2、原型鏈的構成:

 如下代碼例子, Child繼承Parent, Parent繼承GrandParent, 而GrandParent沒有自定義原型,所以默認爲原型鏈的最頂端new Object();

(爲什麼Object爲最頂端,因爲Object.prototype爲null,爲null是沒有原型的)

//    原型鏈: Child -> new Parent() -> new GrandParent() -> new Object();
       function GrandParent() {
           this.name = 'GrandParent';
           this.a = 3;
       }
       Parent.prototype = new GrandParent();
       function Parent() {
           this.name = 'parent';
           this.b = 2;
       }
       Child.prototype = new Parent();
       function Child() {
           this.name = 'child';
           this.c = 1;
       }

       var child = new Child();
       console.log(child); // Child {name: "child", c: 1}

       console.log(child.a); // 3
       console.log(child.b); //2
       console.log(child.c); //1

3、原型鏈的增刪改查:

 使用如上的原型鏈說明, Child -> new Parent() -> new GrandParent() -> new Object(), 實例對象爲child

(1)、增:

爲child實例對象添加屬性,總是添加爲其自己本身的屬性,爲對原型和原型鏈是沒有影響的。(再具體說即對和其相同構造函數構造的實例對象無法造成影響,以下說法同此)

(2)、刪:

 使用delete操作符只能刪除child實例對象自己本身的屬性,而無法刪除由原型繼承而來的屬性

(3)、改:

分兩種情況:

若修改的屬性爲繼承自原型的,且值類型爲原始值,則僅僅修改的是該實例對象的屬性,對原型無法造成影響。

若修改的屬性爲繼承自原型的,屬性值類型爲引用值,則對引用值的修改又分兩種情況:

第一種是直接對該修改的屬性賦值 => 此情況僅僅修改的是實例對象的該屬性,無法對原型造成影響。

第二種是對該修改的屬性添加內容或去除內容,而不是對其重新賦值 => 此情況會對原型造成影響。如下例子:

Person.prototype = {
            has: [1, 2, 3]
        }
        function Person () {
            this.name = 'lyl';
        }
        var person1 = new Person();
        var person2 = new Person();
        person1.has.push(4);
        // person1 和 person2都改變了,因爲person1的修改影響到了原型,進而影響到了另一個實例對象
        console.log(person1.has); //[1, 2, 3, 4]
        console.log(person2.has); // [1, 2, 3, 4]

4)、查:

查詢過程如下,首先看構造函數中是否有要查詢的屬性,若有,則直接返回,若沒有,則看其原型有沒有要查詢的屬性,若沒有,則再看原型的原型上是否有要查詢的屬性,以此順序在原型鏈上查詢,若一直到原型鏈頂端後仍沒有要查詢的屬性,則返回undefined

p.s.理解了在原型鏈上的增刪改查後,自然就能理解在原型上的增刪改查了,只要把在原型上的增刪改查當成只有一個原型的很短的原型鏈即可。

4、絕大多數對象最終都會繼承自Object.prototype

爲什麼事絕大多數呢?因爲null,和undefined是沒有原型的,上文有詳細提到
十一、this基本介紹:

  1、函數預編譯過程 this —> window
  2、全局作用域裏 this —> window

  3、obj.func(); func()裏面的this指向obj), 可以這樣理解,誰調用func,則this就指向誰

  4、call/apply 可以改變函數運行時this指向,

    (1)、call用法:

      func.call(要改變後的this, arg1, arg2, … );

    (2)、apply用法:

      func.apply(要改變後的this, [arg1, arg2, arg2]);

    (3)、apply和call共同點:都是改變this指向

        apply和call不同點:傳參形式不同,call是將參數一個個傳進來,而apply是將所有參數存進一個數組中,然後將該數組傳

複製代碼
// demo1
    function demo1() {
        console.log(this);
    }
    // demo1() <==> this.demo1(); <==> window.demo1()
    demo1(); // window
// demo2
    var demo2 = {
        retThis: function () {
            console.log(this);
        }
    }
    demo2.retThis(); // demo2 = {...}
// call / apply改變this
    demo1.call(demo2);  // demo2 = {}
    demo2.retThis.call(window); // window
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章