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