摘要:判斷一個對象是否指向該構造函數的原型對象,可以使用方法來測試。如何讓指向呢直接強制指向即可運行中重寫原型,不會保留之前原型的任何信息,把原來的原型對象和構造函數對象的實例切斷了。
創建對象
var box = new Object();//創建對象 box.name = "Lee"; //添加屬性 box.age = 100; box.run = function(){ return this.name + this.age + "運行中"; //this 表示當前作用域下對象 } // this 表示new Object()實例出來的那個對象 alert(box.run());
這就是創建對象最基本的方法,但是有個缺點,想創建一個類似的對象,就會產生大量的代碼。
工廠模式為了解決多個類似對象聲明的問題,我們可以使用一種叫做工廠模式的方法,這種方法就是為了解決實例化對象產生大量重復的問題。
function createObject(name,age){ var obj = new Object(); obj.name = name; obj.age = age; obj.run = function(){ return this.name+this.age+"歲年齡"; } return obj; } var box1 = createObject("Lee",20); var box2 = createObject("Jack",30); console.log(box1.run()); console.log(box2.run());
工廠模式解決了重復實例化的問題,但還有一個問題,那就是識別問題,因為根本無法搞清他們到底是哪個對象的實例。
alert(typeof box1); //Object alert(box1 instanceof Object);//true構造函數
ECAMScript中采用構造函數(構造方法)可用來創建特定的對象。類似于Object對象。
//構造函數 function Box(name,age){ this.name = name; this.age = age; this.run = function(){ return this.name + this.age +"運行中..."; }; }; var box1 = new Box("Lee",100); var box2 = new Box("Jack",200); console.log(box1.run()); console.log(box2.run());
使用構造函數的方法,即解決了重復實例化的問題,又解決了對象識別的問題,但問題是,這里并沒有new Object(),為什么可以實例化Box(),這個是哪里來的呢?
使用了構造函數的方法,和使用工廠模式的方法他們不同之處如下:
1.構造函數方法沒有顯示的創建對象(new Objectt()),但它在后臺自動var obj = new Object();
2.直接將屬性和方法賦值給this對象,this就相當于obj;
3.沒有return語句,不需要返回對象引用,它是在后臺自動返回的。
//構造函數 function Box(name,age){ this.name = name; this.age = age; this.run = function(){ return this.name + this.age +"運行中..."; }; }; function Dack(name,age){ this.name = name; this.age = age; this.run = function(){ return this.name + this.age +"運行中..."; }; }; var box1 = new Box("Lee",100); var box2 = new Box("Jack",200); var box3 = new Dack("MrLee",300); console.log(box1.run()); console.log(box2.run()); console.log(box3.run()); //解決了對象識別問題 console.log(box1 instanceof Box); //true console.log(box2 instanceof Box); //true console.log(box3 instanceof Box); //false console.log(box3 instanceof Dack);//true
對象冒充:使用call()方法
var o= new Object(); Box.call(o,"Lee",100); console.log(o.run());
看下一個問題:
var box1 = new Box("Lee",100); //實例化后地址為1 var box2 = new Box("Lee",100); //實例化后地址為2 console.log(box1.name == box2.name); //true console.log(box1.age == box2.age); //true console.log(box1.run() == box2.run());//true //構造函數體內的方法的值是相當的 console.log(box1.run == box2.run); //false //因為他們比較的是引用地址
上面的代碼運行說明引用地址不一樣,那么構造函數內的方法也可以這樣寫:
this.run = new Function("return this.name + this.age +"運行
如何讓他們的引用地址一樣,下面代碼:
function Box(name,age){ this.name = name; this.age = age; this.run = run; }; function run(){ return this.name +this.age+"運行中..."; } var box1 = new Box("Lee",100); //實例化后地址為1 var box2 = new Box("Lee",100); //實例化后地址為2 console.log(box1.run == box2.run); //true //因為他們比較的是引用地址
把構造函數內部的方法通過全局來實現引用地址一致。
雖然使用了全局函數run()來解決了保證引用地址一致的問題,但是這種方式又帶來了一個新的問題,全局中的this在對象調用的時候是Box本身,而當普通函數調用的時候,this又代表window。
function Box(){ //構造函數函數體內什么都沒有,這里如有過,叫做實例屬性,實例方法 } Box.prototype.name="Lee"; //原型屬性 Box.prototype.age=100; Box.prototype.run=function(){ //原型方法 return this.name+this.age+"運行中..."; } var box1=new Box(); var box2=new Box(); console.log(box1.run == box2.run); //true console.log(box1.prototype);//這個屬性是一個對象,訪問不到 console.log(box1.__proto__);//這個屬性是一個指針指向prototype原型對象。
如果是實例方法,不同的實例化,他們的方法地址是不一樣的,是唯一的。
如果是原型方法,那么他們的地址是共享的,大家都一樣。
console.log(box1.constructor); //構造屬性,可以獲取構造函數
PS:IE瀏覽器在腳本訪問__proto__會不能識別,火狐和谷歌及其他某些瀏覽器能識別。雖然可以輸出,但是無法獲取內部信息。
判斷一個對象是否指向該構造函數的原型對象,可以使用isPrototypeOf()方法來測試。
console.log(Box.prototype.isPrototypeOf(box1)); //true //只要實例化對象,即都會指向
原型模式的執行流程:
1.先查找構造函數實例里的屬性或方法,如果有,立刻返回;
2.如果構造函數實例里沒有,則去它的原型對象里找,如果有,就返回。
如何判斷屬性時構造函數的實例里,還是原型里?可以使用hasOwnProperty()函數來驗證:
console.log(box1.hasOwnProperty("name"));//如果實例里有返回true,否則返回false
如何判斷屬性是原型里的?
function Box(){ } Box.prototype.name="Lee"; //原型屬性 Box.prototype.age=100; Box.prototype.run=function(){ //原型方法 return this.name+this.age+"運行中..."; } function isProperty(object,property){ return !object.hasOwnProperty(property) && (property in object); } var box1=new Box(); console.log(isProperty(box1,"name"));
為了讓屬性和方法更好的體現封裝的效果,并且減少不必要的輸入,原型的創建可以使用字面量的方式
使用字面量的方式創建原型對象,這里的{}就是對象,是object,new Object就相當于{}
function Box(){} Box.prototype={ name:"Lee", age:100, run:function(){ return this.name+this.age+"運行中..."; } } var box = new Box(); console.log(box.constructor == Box); //false
字面量創建的方式使用constructor屬性不會指向實例,而會指向Object,構造函數創建的方式則相反。
這里的Box.prototype={}就相當于創建了一個新的對象,所以 box.constructor是Object。
如何讓box.constructor指向Box呢?
function Box(){} Box.prototype={ constructor:Box,//直接強制指向即可 name:"Lee", age:100, run:function(){ return this.name+this.age+"運行中..."; } } var box = new Box(); console.log(box.constructor == Box); //true
重寫原型,不會保留之前原型的任何信息,把原來的原型對象和構造函數對象的實例切斷了。
function Box(){} Box.prototype={ constructor:Box, name:"Lee", age:100, run:function(){ return this.name+this.age+"運行中..."; } } //重寫原型 Box.prototype={ age:200 } var box = new Box(); console.log(box.name); //undefined
查看sort是否是Array原型對象里的方法
alert(Array.prototype.sort);
在如下 判斷String原型對象里是否有substring方法
alert(String.prototype.substring);
給String 添加addstring方法:
String.prototype.addstring=function(){ return this+",被添加了!"; } var box="Lee"; console.log(box.addstring());
注:原型模式創建對象也有自己的缺點,它省略了構造函數傳參初始化這一過程,帶來的缺點就是初始化的值都是一致的。而原型最大的缺點就是它最大的優點,那就是共享。
原型中所有屬性是被很多實例共享的,共享對于函數非常合適,對于包含基本值的屬性也還可以。但如果屬性包含引用類型,就存在一定的問題:
function Box(){} Box.prototype={ constructor:Box, name:"Lee", age:100, family:["哥哥","姐姐","妹妹"], run:function(){ return this.name+this.age+"運行中..."; } }; var box1 = new Box(); console.log(box1.family); //"哥哥","姐姐","妹妹" box1.family.push("弟弟"); console.log(box1.family);//"哥哥","姐姐","妹妹","弟弟" var box2 = new Box(); console.log(box2.family);//"哥哥","姐姐","妹妹","弟弟"
從上面代碼可以看出,在第一個實例修改后引用類型,保持了共享。box2.family共享了box1添加后的引用類型的原型。
為了解決構造傳參和共享問題,可以組合構造函數+原型模式:
function Box(name,age){ //保持獨立的用構造函數 this.name=name; this.age=age; this.family=["哥哥","姐姐","妹妹"]; } Box.prototype={ //保持共享的用原型 constructor:Box, run:function(){ return this.name+this.age+"運行中..."; } } var box1 = new Box("Lee",100); console.log(box1.family); //"哥哥","姐姐","妹妹" box1.family.push("弟弟"); console.log(box1.family);//"哥哥","姐姐","妹妹","弟弟" var box2 = new Box("Jack",200); console.log(box2.family); //"哥哥","姐姐","妹妹" //引用類型沒有使用原型,所以沒有共享
動態原型模式
//把原型封裝到構造函數里 function Box(name,age){ this.name=name; this.age=age; this.family=["哥哥","姐姐","妹妹"]; console.log("原型初始化開始"); //執行了兩次 Box.prototype.run=function(){ return this.name+this.age+"運行中..."; } console.log("原型初始化結束"); //執行了兩次 } //原型的初始化,只要第一次初始化就可以了,沒必要每次構造函數實例化的時候都初始化 var box1 = new Box("Lee",100); var box2 = new Box("Jack",200);
為了只讓第一次初始化,那么就判斷
function Box(name,age){ this.name=name; this.age=age; this.family=["哥哥","姐姐","妹妹"]; if(typeof this.run!="function"){ console.log("原型初始化開始"); //執行了一次次 Box.prototype.run=function(){ return this.name+this.age+"運行中..."; }; console.log("原型初始化結束"); //執行了一次 } } //原型的初始化,只要第一次初始化就可以了,沒必要每次構造函數實例化的時候都初始化 var box1 = new Box("Lee",100); var box2 = new Box("Jack",200);
寄生構造函數
如果以上都不能滿足需要,可以使用一下寄生構造函數。
寄生構造函數=工廠模式+構造函數
function Box(name,age){ var obj = new Object(); obj.name=name; obj.age=age; obj.run=function(){ return this.name+this.age+"運行中..."; } return obj; } var box1 = new Box("Lee",100); var box2 = new Box("Jack",200);
穩妥構造函數
在一些安全的環境中,比如禁止使用this和new,這里的this是構造函數里不使用的this,這里的new是在外部實例化構造函數時不使用new。這種創建方式叫做穩妥構造函數。
function Box(name,age){ var obj = new Object(); obj.name=name; obj.age=age; obj.run=function(){ return this.name+this.age+"運行中..."; } return obj; } var box1 = Box("Lee",100); var box2 = Box("Jack",200);繼承
繼承是面向對象中一個比較核心的概念。其它正統面向對象語言都會用兩種方式實現繼承:一個是接口實現,一個是繼承。而ECMAScript只支持繼承,不支持接口實現,而實現繼承的方式依靠原型鏈完成。
function Box(){ this.name="Lee"; } function Jack(){ this.age=100; } Jack.prototype = new Box(); var jack = new Jack(); console.log(jack.name); //Lee
為了解決引用共享和超類型無法傳參的問題,我們采用一種叫借用構造函數的技術,或者成為對象冒充(偽造對象、經典繼承)的技術解決這兩個問題。
function Box(name){ this.name=name; } Box.prototype.age=200; function Jack(name){ Box.call(this,name); } var jack = new Jack("Lee"); console.log(jack.name);//Lee console.log(jack.age);//undefined
但是上面的代碼可以看出,對象冒充沒有繼承原型鏈上的age屬性。所以要繼承Box的原型,就出現下面的組合繼承。
組合繼承即是原型鏈+借用構造函數的模式
function Box(name){ this.name=name; } Box.prototype.age=200; function Jack(name){ Box.call(this,name); } Jack.prototype = new Box(); var jack = new Jack("Lee"); console.log(jack.name);//Lee console.log(jack.age);//200
原型式繼承
//臨時中轉函數 function obj(o){ function F(){}; F.prototype = o; return new F(); } //這是字面量的聲明方式,相當于var box = new Box(); var box={ name:"Lee", age:100, family:["哥哥","姐姐","妹妹"] }; var box1 = obj(box); console.log(box1.family);//"哥哥","姐姐","妹妹" box1.family.push("弟弟"); console.log(box1.family);//"哥哥","姐姐","妹妹","弟弟" var box2 = obj(box); console.log(box2.family);//"哥哥","姐姐","妹妹","弟弟"
存在的問題就是引用類型共享了。
寄生式繼承
把原型式與工廠模式結合起來。
//臨時中轉函數 function obj(o){ function F(){}; F.prototype = o; return new F(); } //寄生函數 function create(o){ var f=obj(o); f.run=function(){ return this.name+"方法"; } return f; } //這是字面量的聲明方式,相當于var box = new Box(); var box={ name:"Lee", age:100, family:["哥哥","姐姐","妹妹"] }; var box1 = create(box); console.log(box1.run());
寄生組合繼承
//臨時中轉函數 function obj(o){ function F(){}; F.prototype = o; return new F(); } //寄生函數 function create(box,desk){ var f=obj(box.prototype); f.constructor=desk; //調整原型構造指針 desk.prototype=f; } function Box(name,age){ this.name=name; this.age=age; } Box.prototype.run=function(){ return this.name+this.age+"運行中..."; } function Desk(name,age){ Box.call(this,name,age); //對象冒充 } //通過寄生組合繼承來實現繼承 create(Box,Desk); //這句話用來替代Desk.prototype = new Box(); var desk = new Desk("Lee",100); console.log(desk.run());
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/78845.html
摘要:個人前端文章整理從最開始萌生寫文章的想法,到著手開始寫,再到現在已經一年的時間了,由于工作比較忙,更新緩慢,后面還是會繼更新,現將已經寫好的文章整理一個目錄,方便更多的小伙伴去學習。 showImg(https://segmentfault.com/img/remote/1460000017490740?w=1920&h=1080); 個人前端文章整理 從最開始萌生寫文章的想法,到著手...
摘要:很多情況下,通常一個人類,即創建了一個具體的對象。對象就是數據,對象本身不包含方法。類是相似對象的描述,稱為類的定義,是該類對象的藍圖或原型。在中,對象通過對類的實體化形成的對象。一類的對象抽取出來。注意中,對象一定是通過類的實例化來的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 馬上就要到七夕了,離年底老媽老爸...
摘要:很多情況下,通常一個人類,即創建了一個具體的對象。對象就是數據,對象本身不包含方法。類是相似對象的描述,稱為類的定義,是該類對象的藍圖或原型。在中,對象通過對類的實體化形成的對象。一類的對象抽取出來。注意中,對象一定是通過類的實例化來的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 馬上就要到七夕了,離年底老媽老爸...
摘要:很多情況下,通常一個人類,即創建了一個具體的對象。對象就是數據,對象本身不包含方法。類是相似對象的描述,稱為類的定義,是該類對象的藍圖或原型。在中,對象通過對類的實體化形成的對象。一類的對象抽取出來。注意中,對象一定是通過類的實例化來的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 馬上就要到七夕了,離年底老媽老爸...
摘要:關于中面向對象的理解面向對象編程它是一種編程思想我們的編程或者學習其實是按照類實例來完成的學習類的繼承封裝多態封裝把實現一個功能的代碼封裝到一個函數中一個類中以后再想實現這個功能,只需要執行這個函數方法即可,不需要再重復的編寫代碼。 關于js中面向對象的理解 面向對象編程(oop) 它是一種編程思想 (object-oriented programming ), 我們的編程或者學習其...
閱讀 1866·2021-11-15 11:39
閱讀 1073·2020-12-03 17:06
閱讀 729·2019-12-27 11:42
閱讀 3267·2019-08-30 13:59
閱讀 1452·2019-08-26 13:22
閱讀 3281·2019-08-26 12:15
閱讀 2471·2019-08-26 10:22
閱讀 1558·2019-08-23 18:40