js原型詳解


js中的原型毫無疑問一個難點,學習如果不深入很容易就暈了!

任何一個js知識點,比如事件流,閉包,繼承等,都有許許多多的說法,對於這些知識點我們都應該先熟練的使用,然后自己整理一套屬於自己的理解說辭,才不會忘

原型(對象屬性)

Javascript規定,每一個函數都有一個prototype對象屬性,指向另一個對象(原型鏈上面的)。
prototype(對象屬性)的所有屬性和方法,都會被構造函數的實例繼承。這意味着,我們可以把那些不變(公用)的屬性和方法,直接定義在prototype對象屬性上。

prototype就是調用構造函數所創建的那個實例對象的原型(proto)。

prototype可以讓所有對象實例共享它所包含的屬性和方法。也就是說,不必在構造函數中定義對象信息,而是可以直接將這些信息添加到原型中。

原型鏈 (JS原型與原型鏈繼承)

實例對象與原型之間的連接,叫做原型鏈。proto( 隱式連接 )
JS在創建對象的時候,都有一個叫做proto的內置屬性,用於指向創建它的函數對象的原型對象prototype。
內部原型(proto)和構造器的原型(prototype)
1、每個對象都有一個proto屬性,原型鏈上的對象正是依靠這個屬性連結在一起
2、作為一個對象,當你訪問其中的一個屬性或方法的時候,如果這個對象中沒有這個 方法或屬性,那么Javascript引擎將會訪問這個對象的proto屬性所指向上一個對 象,並在那個對象中查找指定的方法或屬性,如果不能找到,那就會繼續通過那個對象 的proto屬性指向的對象進行向上查找,直到這個鏈表結束。

實際開發中,有這樣一種寫法,面向對象,這種寫法就是通過構造函數以及原型來運用的(混合模式開發面向對象)

每一個函數都有一個原型屬性prototype(對象屬性),里面放置的是共有、公有的屬性或者方法。(一般情況屬性是私有的)。注意,只有函數才有prototyoe屬性,

    function Person() {
       
    }
    var p = new Person()
    console.log(Person.prototype); // Object{} 
    console.log(p.prototype); //undefined

這個例子可以發現,函數是存在prototype屬性的

任何對象都是默認存在構造器的,此時我們的Person()只是普通函數,它其實是js內置方法Function()構造出來的,而p此時是Person() new出來的,只有new 過了,才叫構造函數

淺談constructor

在 Javascript 語言中,constructor 屬性是專門為 function 而設計的,它存在於每一個 function 的prototype 屬性中。這個 constructor 保存了指向 function 的一個引用。

上代碼更容易理解

    function Person() {
       
    }
    var p = new Person()
    console.log(Person.prototype); // Object{} 
    console.log(p.prototype); // undifined
    console.log(p.constructor); //function Person(){}    
    此處的p是通過 Person函數構造出來的,所以p的constructor屬性指向Person
    console.log(Person.constructor); //function Function(){}
    之前提過,每個函數其實是通過new Function()構造的
    console.log({}.constructor); // function Object(){}
    每個對象都是通過new Object()構造的
    console.log(Object.constructor); // function Function() {}
    Object也是一個函數,它是Function()構造的
    console.log([].constructor);  //function Array(){}

我想大家此時對於prototype屬性有了一定的了解

   console.log(Object.constructor); // function Function() {}

可能大家對於這個有些不理解,畢竟我們實際開發中哪見過這玩意

    console.log(Function instanceof Object); // true
    console.log(Object instanceof Function); // true

這樣大家是不是就明白了呢 函數是對象構造的 對象也是函數構造的,倆者即是函數也是對象,所以為什么構造函數它是一個函數卻返回一個對象,倆者是互相繼承的關系

    var o1 = new f1();
    typeof o1 //"object"  

重點講解一下原型prototype的用法,最主要的方法就是將屬性暴露成公用的,上代碼

    function Person(name,age){
        this.name = name;
        this.age = age;
        this.sayHello = function(){
            console.log(this.name + "say hello");
        }
    }
    var girl = new Person("bella",23);
    var boy = new Person("alex",23);
    console.log(girl.name);  //bella
    console.log(boy.name);   //alex
    console.log(girl.sayHello === boy.sayHello);  //false

再看下面的代碼

   function Person(name,age){
        this.name = name;
        this.age = age;
        
    }
    Person.prototype.sayHello=function(){
        console.log(this.name + "say hello");
    }
    var girl = new Person("bella",23);
    var boy = new Person("alex",23);
    console.log(girl.name);  //bella
    console.log(boy.name);   //alex
    console.log(girl.sayHello === boy.sayHello);  //true 

我們給函數Person的原型中聲明了sayHello方法,當我們的構造實例對象去訪問的時候訪問的方法是同一個,這就是prototype原型最大的作用,共享屬性和方法

那么prototype與proto有什么關系,先看這么一串代碼

    var obj = {}
     此處等價於 var obj = new Object()
    console.log(obj.__proto__ === Object.prototype)//true  

JS 在創建對象(不論是普通對象還是函數對象)的時候,都有一個叫做 __proto__ 的內置屬性,用於指向創建它的構造函數的原型對象。

根據前面的例子我們很清楚,obj是通過new Object 構造出來的一個對象,那我們Object的原型對象就是Object.prototype,在Object下面構造出來的所有對象,都有一個__proto__ 指向它的原型,我們稱這個為原型鏈

    var obj = []
    console.log(obj.__proto__ === Array.prototype)//true

這個也是一樣的道理

此處要理解,就是原型對象是誰構造的,是誰構造的我們看構造器

   console.log(Array.prototype.constructor)//Array{}

原型對象肯定是它本身構造的,接着看之前的構造函數代碼

   function Person(name,age){
    this.name = name;
    this.age = age;
    
    }
    Person.prototype.sayHello=function(){
        console.log(this.name + "say hello");
    }
    var girl = new Person("bella",23);
    var boy = new Person("alex",23);
    console.log(Person.prototype.constructor);  //Person
    console.log(girl.__proto__==Person.prototype);   //true
    console.log(girl.constructor==Person);  //true 

現在看是不是特別簡單 注意這里是兩個下划線__proto__ 叫 杠杠proto杠杠

接下來給一串代碼

  function Person(){

  }
  var person1=new Person()    

快速回答
1.person1.__proto__==

2.person1.constructor==

3.Person.__proto__==

4.Person.prototype.constructor==

5.person1.prototype.constructor==

6.Person.prototype==

大家可以測測輸入的答案打印是不是為true 如果你很快打出,說明你理解的已經很透徹了

接下來我找幾道題一起分析一下,深入了解

              function A(){
                }
                function B(a){
                  this.a = a;
                }
                function C(a){

                  if(a){
                    this.a = a;
                  }
                }
                A.prototype.a = 1;

                B.prototype.a = 1;

                C.prototype.a = 1;


                console.log(new A().a);  //1

                console.log(new B().a);//undefined

                console.log(new C(2).a);//2

我們先觀察第一個
new A() 很明顯它是A()構造的實例對象,在下面A函數prototype共享了一個屬性a=1,那么實例對象也可以通過這個屬性訪問到他的值==1

第一個比較簡單,我們看第二個
第二個其實是個坑,首先B()函數它聲明的時候設置了一個參數,注意里面的方法this,此時this指向的是window,我們都知道,然而在構造函數之后this指向了new B(),然而此時B 沒有傳入參數,也就是說此時的參數a==undefined,
因此new B()下的屬性a==this.a==undefined (不知道這么說能不能理解 ̄□ ̄||)

如果我們給B 傳入一個數(可以是a,但a一定要先聲明)

                console.log(new B(3).a); //3
                console.log(new B(5).a); //5

這樣應該就好理解了,總不能我們自己設置的屬性還覆蓋不來爸爸給你的屬性吧

第三個很好理解,從第二個的角度分析,如果傳入參數,輸出參數,如果沒有 輸出1

這道題沒什么拐彎,比較簡單,我們再看一題

    function Fun(){
        var getName = function(){
            console.log(1);
        }
        return this;
    }
    Fun.getName = function(){
        console.log(2);
    }
    Fun.prototype.getName = function(){
        console.log(3);
    }
    var getName = function(){
        console.log(4);
    }
    function getName(){
        console.log(5);
    }
            
    Fun().getName(); //4
    getName(); //4
    new Fun().getName();//3
    new new Fun().getName();//3

做題一定要一步步來,先看第一個 Fun().getName();
很明顯此時是Fun()調用getName(),我們看看Fun(),注意,函數的返回值永遠是return,此處return返回了this,在函數中這里的this指向window,也就是說第一步Fun( )返回的是window,轉換一下就是window.getName(),
我們在什么找一下全局的getName,最終輸出4

第二個其實很簡單,我感覺就是來迷惑我們的,前面省略了window,很明顯還是4

第三個也太簡單了,只要你理解了我上面寫的內容,一看就知道輸出3

第四個,兩個new? 不要慌 我們來分析一下

new后面跟的肯定是函數,用來構造實例對象,所以new Fun()這是不能拆的,
很明顯此時new Fun()是一個構造函數,雖然稱之為函數,但其實它是一個對象,因為函數是方法,而方法是不能調用屬性的,但是對象可以,既然是對象,何來new 構造呢,所以此處分析可以得到new Fun ().getName才是一體連着的,
那很明顯就是原型下的方法咯,所以輸出3

隨着框架應用的越來越廣泛,很多用法我們可能並不是那么熟悉,還是希望大家秉着學習之心,多多復習。

轉載於:https://www.jianshu.com/p/72156bc03ac1


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM