1、面向對象編程(OOP)的特點:
抽象:抓住核心問題
封裝:只能通過對象來訪問方法
繼承:從已有的對象下繼承出新的對象
多態:多對象的不同形態
一、創建對象的幾種方式
javascript 創建對象簡單的來說,無非就是使用內置對象或各種自定義對象,當然還可以使用JSON,但寫法有很多,也能混合使用。
1、工廠方式創建對象:面向對象中的封裝函數(內置對象)
function createPerson(name){ //1、原料 var obj=new Object(); //2、加工 obj.name=name; obj.showName=function(){ alert(this.name); } //3、出場 return obj; } var p1=createPerson('小米'); p1.showName();
與系統對象的區別:
var arr=new Array();//生成一個系統數組對象
1、系統對象是直接用 new 在外面生成,而工廠定義的是在函數內部生成
2、工廠定義的函數名稱第一個是小寫開頭,而系統定義的是大寫開頭
工廠模式的優缺點:雖然解決了創建相似對象的問題,但是卻沒有解決對象識別問題(即怎樣知道一個對象的類型)。
2、構造函數創建對象
當new去調用一個函數,這個時候函數中的this就是創建出來的對象,而且函數的返回值就是this(隱式返回)
new后面的函數叫做構造函數
<1>有參數的構造函數
function CreatePerson(name){ this.name=name; this.showName=function(){ alert(this.name); } } var p1=new CreatePerson('小米');
<2>無參數的構造函數
function CreatePerson(){} var p1=new CreatePerson(); p1.name="小米"; p1.showName=function(){ alert(p1.name); } p1.showName();
構造函數模式的優缺點:
1、優點:創建自定義函數意味着將來可以將它的實例標識為一種特定的類型,這是構造函數勝過工廠模式的地方
2、缺點:每個方法都要在每個實例上重新創建一遍
3、對象字面量方式創建對象
person={
name:"小米",
age:23
};
4、用原型方式
1、原型對象:只要創建了一個新函數,就會為該函數創建一個prototype屬性,這個屬性指向函數的原型對象。在默認情況下,所有的原型對象都會自動獲得一個constructor(構造函數)屬性,這個屬性是一個指向prototype屬性所在函數的指針
2、可以通過isPrototypeOf()方法來確定對象之間是否存在這種關系
function Person(){} Person.prototype.name="小米"; Person.prototype.showName=function(){ alert(this.name); } var p1=new Person(); p1.showName();
原型模式的優缺點:
1、優點:可以讓所有的對象實例共享它所包含的屬性和方法
2、缺點:原型中是所有屬性都是共享的,但是實例一般都是要有自己的單獨屬性的。所以一般很少單獨使用原型模式。
5.混合模型
構造函數模式定義實例屬性,而原型模式用於定義方法和共享的屬性
function CreatePerson(name){ this.name=name; } Create.prototype.showName=function(){ alert(this.name); } var p1=new CreatePerson('小米'); p1.showName();
var p2=new CreatePerson('小米'); p2.showName();
alert(p1.showName==p2.showName);//true;原因:都是在原型下面,在內存中只存在一份,地址相同
總結:
function 構造函數(){
this.屬性;
}
構造函數.原型.方法=function(){};
var 對象1=new 構造函數();
對象1.方法();
原型:去改寫對象下面公用的的方法或屬性,讓公用的方法或屬性在內存中存在一份(提高性能)
原型:prototype:要寫在構造函數的下面
var arr=[]; arr.number=10; Array.prototype.number=20; alert(arr.number);//10,
//原因:普通定義的要比原型定義的權重大,先會找自身的,自身沒有的話再沿着原型鏈找原型上是否有
屬性是否要放在原型下面,就要看該屬性是否是可變的,如果不是可變的,就可以放在原型下面,用來公用屬性,可變的話放在構造函數下面。
this的指向問題:在事件或者定時器下比較容易出問題
二、包裝對象
1、我們把系統自帶的對象,叫做系統對象。例如:Array,Date
2、包裝對象:基本類型都有自己對應的包裝對象:String,Number,Boolean
var str='hello';//基本類型:字符串類型
str.charAt(0);//基本類型會找到對應的包裝對象類型,然后包裝對象把所有的屬性和方法給了基本類型,然后包裝對象消失。
str.number=10;//在包裝對象下創一個對象,將屬性創建在對象下面,然后包裝對象就消失了,
alert(str.number);//會彈出undefined;原因:會在包裝對象下重新創建一個對象
三、原型鏈
原型鏈:實例對象與原型之間的連接,叫做原型鏈
_proto_(隱式連接)
Object對象類型是原型鏈的最外層
實例對象->先查找自己本身下面的屬性和方法->自身沒找到會沿着原型鏈找到該對象的原型,再查看原型上是否有要查找的屬性或方法->依次繼續查找如果找到的話則返回,否則找到最頂層Object上還沒有就真沒有了
四、面向對象中的屬性和方法
1、hasOwnProperty():看是否為對象自身下面的屬性和方法
只有對象自己定義的屬性和方法則返回true,如果在prototype下定義發屬性和方法為公用的,所以返回為false;
2、constructor:查看對象的構造函數
(可以用來檢測函數類型例如檢測是否是數組)
每個原型函數都會自動添加constructor屬性(只會生成這一個屬性)
for in的時候有些屬性是找不到的(系統自帶的屬性是for in找不到的,自己定義的可以找到)
避免修改constructor屬性
function Aaa(){} //Aaa.prototype.name='小米'; //Aaa.prototype.age=6; //alert(a1.constructor);//Aaa 原因:只是給原型添加了屬性,並不是重新賦值了,自動添加的constructor屬性還在。 Aaa.prototype={ // constructor:'Aaa',//需要手動修正指向問題 name:'小米', age:6 } var a1=new Aaa(); alert(a1.constructor);//Object 原因:將原型的prototype重新賦值了,但里面沒有constructor
注意:以這種方式重設constructor屬性會使它的[Enumerable]特性被設置為true,默認情況下,原生的constructor屬性是不可枚舉的。可以通過Object.defineProperty()來修改。
3、instanceof:運算符
對象與構造函數在原型鏈上是否有關系,也可以用作類型判斷但不是最好的方案,最好的方案是用toString 方法來判斷。
4、toString():object上的方法,把對象轉化為字符串
var arr=[]; alert(arr.toString==Object.prototype.toString);//false //原因:系統對象下面都是自帶的(例如數組的toString在Array.prototype下),自己寫的對象都是通過原型鏈找到object下面的toString function Aaa(){} var a1=new Aaa(); alert(a1.toString==Object.prototype.toString);//true
1>利用toString 進制轉化
Number.toString(進制);
var num=255; alert(num.toString(16));//ff---轉化為16進制,默認不寫轉化為十進制
2>利用toString做類型判斷:
//跨頁面的情況下上面兩種情況會失效 var oF=document.createElement('iframe'); document.body.appendChild('oF'); var ifArray=windows.frames[0].Array;//iframe下的Array數組 var arr=new ifArray(); alert(arr.constructor==Array);//false alert(arr instanceof Array);//false alert(Object.prototype.toString.call(arr)=='[object Array]');//true
var arr=[]; alert(Object.prototype.toString.call(arr));//[Object Array] var arr={}; alert(Object.prototype.toString.call(arr));//[Object Object] var arr=new Date; alert(Object.prototype.toString.call(arr));//[Object Date] var arr=new RegExp; alert(Object.prototype.toString.call(arr));//[Object RegExp] var arr=null; alert(Object.prototype.toString.call(arr));//[Object Null]
五 、繼承
1、繼承方式:
1、拷貝繼承:通用型 有new無new都可以用
2、類式繼承:new構造函數---利用構造函數(類)繼承的方式
3、原型繼承:無new的對象---借助原型來實現對象繼承對象
屬性繼承:調用父類的構造函數call
方法繼承:用for in的形式 拷貝繼承(jq也用拷貝繼承)
var a = {
name: '小米'
};
//拷貝繼承
function extend(obj1, obj2) {
for (var attr in obj2) {
obj1[attr] = obj2[attr];
}
}
//原型繼承
var b=cloneObj(a);
b.name='小喬';
alert(a.name);
alert(b.name);
function cloneObj(obj) {
var F=function () {};
F.prototype=obj;
return new F();
}
//類式繼承
function A() {//父類
this.name='小米';
}
A.prototype.showName=function () {
alert(this.name);
}
function B() {//子類
A.call(this);//屬性和方法分開繼承
}
//B.prototype=new A();//一句話實現繼承,但會有很多問題,比如指向問題,屬性會互相影響
//類式繼承改進:至少由以下四句實現方法的繼承,屬性需要分開繼承
var F=function () {};
F.prototype=A.prototype;
B.prototype=new F();
B.prototype.constructor=A;//修正指向問題
var b1=new B();
b1.name='笑笑';
b1.showName();