JS是物件導向語言,但與JAVA或C#等物件導向語言有一些差異,JS的物件實體化概念不是類別而是稱為原型(Prototype)
此種性質是以原型為基礎的物件導向
定義簡單的類別
var Member=function(){}
var mem=new Member();//以Member類別實體化一個mem物件
建構子初始化
var Member=function(age,tel){//在建構子加入屬性初始化
this.age=age;//this表示所建立的實體本身
this.tel=tel;
this.getData=function(){
return this.age+' '+this.tel;
}
};
var mem=new Member(20,'123456789');
console.log(mem.getData());
//建構子不允許有回傳值
動態新增建構子內的方法
var Member=function(age,tel){
this.age=age;
this.tel=tel;
};
var mem=new Member(20,'123456789');
mem.getData=function(){
return this.age+' '+this.tel;
}
console.log(mem.getData());
//其他實體呼叫getData方法會出現錯誤訊息,因為此種方法並不是針對Member類別
prototype屬性
透過prototype所新增的成員類別所建立的方法,所有實體都可使用
var OpNumber=function(x,y,){
this.x=x;
this.y=y;
};
OpNumber.prototype.addNum=function(){//使用prototype屬性加入addNum方法
return this.x+this.y;
};
//建立兩個物件都呼叫addNum方法
var sum=new OpNumber(3,4);
console.log(sum.addNum());
var sum2=new OpNumber(7,3);
console.log(sum2.addNum());
使用原型物件有以下優點
- 節省記憶體空間(使用參考的方式所以不會複製到實體中)
- 能立即反應新增或刪除的成員(意思是即使新增或刪除的成員是在建立實體之後,也能正常操作)
使用protype設定屬性
var Seq=function(){};
Seq.prototype.id='1';//使用prototype設定id屬性為1
var seq1=new Seq();
var seq2=new Seq();
console.log(seq1.id+','+seq2.id);//1,1
seq2.id='2';
console.log(seq1.id+','+seq2.id);//1,2
從以上執行結果可以看出使用prototype所設定的屬性是參考值,也就是實體假如沒有id屬性將會參考到由prototype所建立的原型屬性
之後因為seq2已經有建立自己的id屬性值,所以就不再參考到原型屬性
定義原型的物件常值
如果需要設定的原型屬性有多個,使用物件常值設定會較簡潔,而且可增加程式可讀性(同一物件的成員都被定義在同一個區塊)
var OpNumber=function(x,y){
this.x=x;
this.y=y;
};
OpNumber.prototype={
add:function(){//add方法
console.log(this.x+this.y);
},
minus:function(){//minus方法
console.log(this.x-this.y);
}
};
var num1=new OpNumber(9,5);
var num2=new OpNumber(9,5);
num1.add();
num2.minus();
定義靜態屬性與方法
靜態屬性與方法不需透過建立實體就可直接用物件呼叫,但是不可在原型物件中定義
var OpNumber=function(){};
OpNumber.num1=1;//定義兩個靜態屬性num1,num2
OpNumber.num2=2;
OpNumber.addNum=function(x,y){//定義靜態方法
return x+y;
};
console.log('num1= '+OpNumber.num1);//呼叫靜態屬性
console.log('num2= '+OpNumber.num2);
console.log(OpNumber.addNum(9,9));//呼叫靜態方法
var op=new OpNumber();
op.num1//使用實體呼叫靜態方法或屬性會發生錯誤
靜態屬性的使用特點
基本上都使用在唯讀屬性
靜態方法中不可有this
物件繼承
使用物件繼承就不需要在多個類別中建立多個重複的方法
var OpNumber=function(){
};
OpNumber.prototype={
add:function(){
console.log(this.x+this.y);
}
};
var Sum=function(x,y){
this.x=x;
this.y=y;
OpNumber.call(this);//設定Sum物件實體參考到OpNumber
};
Sum.prototype=new OpNumber();//使用目前的this呼叫OpNumber建構子
Sum.prototype.minus=function(){
console.log(this.x-this.y);
};
var num1=new Sum(5,3);
num1.add();
num1.minus();
私有成員
私有成員只有類別內部的方法可以存取的屬性與方法
function Cal(){
//在建構子內使用var定義私有成員與方法,不是使用this
var _x;
var _y;
var _add=function(){
return _x+_y;
}
//因為私有成員無法直接透過外部存取,所以需要透過下面三個存取器來設定與取得屬性值
this.set_xy=function(x,y){//設定器
_x=x;
_y=y;
}
this.get_xy=function(){//取得器
return _x,_y;
}
this.get_add=function(){
return _add();
}
}
var num1=new Cal();
num1.set_xy(6,5);
console.log(num1.get_xy());
console.log(num1.get_add());
console.log(num1._x);//直接存取會出現undefined
console.log(num1._y);//直接存取會出現undefined
//JS的私有成員無法像C#或JAVA等物件導向語言,可以在相同類別內存取,必須使用如閉包一樣的方式
定義類別-class
ES6之後JS導入了class指令,如同C#或JAVA的方式
class OpNumber{//建立OpNumber類別
constructor(x,y){//建構子
this.x=x;
this.y=y;
}
getAdd(){//相加方法
return this.x+this.y;
}
getMinus(){//相減方法
return this.x-this.y;
}
}
let num1=new OpNumber(10,5);
console.log(num1.getAdd());
console.log(num1.getMinus());
類別內定義靜態方法
class OpNumber{
//在類別內靜態方法與屬性要用static指令定義
static x=10;//定義靜態屬性
static y=5;//定義靜態屬性
constructor(x,y){
this.x=x;
this.y=y;
}
static getAdd(){//靜態方法
return OpNumber.x+OpNumber.y;//靜態方法不可使用this
}
getMinus(){
return this.x-this.y;
}
}
let num1=new OpNumber(10,5);
console.log(OpNumber.getAdd());//使用類別名稱.屬性方式呼叫
console.log(num1.getMinus());
繼承類別
class Son extends Father//使用extends指令繼承
模組功能
export class OpNumber{...}//設定要匯出模組的類別
import{OpNumber} form 'Url'//載入模組