傳統的javascript中只有對象,沒有類的概念。它是基於原型的面向對象語言。原型對象特點就是將自身的屬性共享給新對象。這樣的寫法相對於其它傳統面向對象語言來講,很有一種獨樹一幟的感腳!非常容易讓人困惑!
如果要生成一個對象實例,需要先定義一個構造函數,然后通過new操作符來完成。構造函數示例:
//函數名和實例化構造名相同且大寫(非強制,但這么寫有助於區分構造函數和普通函數) function Person(name,age) { this.name = name; this.age=age; } Person.prototype.say = function(){ return "我的名字叫" + this.name+"今年"+this.age+"歲了"; } var obj=new Person("laotie",88);//通過構造函數創建對象,必須使用new 運算符 console.log(obj.say());//我的名字叫laotie今年88歲了
構造函數生成實例的執行過程:
1.當使用了構造函數,並且new 構造函數(),后台會隱式執行new Object()創建對象;
2.將構造函數的作用域給新對象,(即new Object()創建出的對象),而函數體內的this就代表new Object()出來的對象。
3.執行構造函數的代碼。
4.返回新對象(后台直接返回);
ES6引入了Class(類)這個概念,通過class關鍵字可以定義類。該關鍵字的出現使得其在對象寫法上更加清晰,更像是一種面向對象的語言。如果將之前的代碼改為ES6的寫法就會是這個樣子:
class Person{//定義了一個名字為Person的類
constructor(name,age){//constructor是一個構造方法,用來接收參數
this.name = name;//this代表的是實例對象
this.age=age;
}
say(){//這是一個類的方法,注意千萬不要加上function
return "我的名字叫" + this.name+"今年"+this.age+"歲了";
}
}
var obj=new Person("laotie",88);
console.log(obj.say());//我的名字叫laotie今年88歲了
注意項
1.在類中聲明方法的時候,千萬不要給該方法加上function關鍵字
2.方法之間不要用逗號分隔,否則會報錯
由下面代碼可以看出類實質上就是一個函數。類自身指向的就是構造函數。所以可以認為ES6中的類其實就是構造函數的另外一種寫法!
console.log(typeof Person);//function
console.log(Person===Person.prototype.constructor);//true
以下代碼說明構造函數的prototype屬性,在ES6的類中依然存在着。
console.log(Person.prototype);//輸出的結果是一個對象
實際上類的所有方法都定義在類的prototype屬性上。代碼證明下:
Person.prototype.say=function(){//定義與類中相同名字的方法。成功實現了覆蓋!
return "我是來證明的,你叫" + this.name+"今年"+this.age+"歲了";
}
var obj=new Person("laotie",88);
console.log(obj.say());//我是來證明的,你叫laotie今年88歲了
當然也可以通過prototype屬性對類添加方法。如下:
Person.prototype.addFn=function(){
return "我是通過prototype新增加的方法,名字叫addFn";
}
var obj=new Person("laotie",88);
console.log(obj.addFn());//我是通過prototype新增加的方法,名字叫addFn
還可以通過Object.assign方法來為對象動態增加方法
Object.assign(Person.prototype,{
getName:function(){
return this.name;
},
getAge:function(){
return this.age;
}
})
var obj=new Person("laotie",88);
console.log(obj.getName());//laotie
console.log(obj.getAge());//88
constructor方法是類的構造函數的默認方法,通過new命令生成對象實例時,自動調用該方法。
class Box{
constructor(){
console.log("啦啦啦,今天天氣好晴朗");//當實例化對象時該行代碼會執行。
}
}
var obj=new Box();
constructor方法如果沒有顯式定義,會隱式生成一個constructor方法。所以即使你沒有添加構造函數,構造函數也是存在的。constructor方法默認返回實例對象this,但是也可以指定constructor方法返回一個全新的對象,讓返回的實例對象不是該類的實例。
class Desk{
constructor(){
this.xixi="我是一只小小小小鳥!哦";
}
}
class Box{
constructor(){
return new Desk();// 這里沒有用this哦,直接返回一個全新的對象
}
}
var obj=new Box();
console.log(obj.xixi);//我是一只小小小小鳥!哦
constructor中定義的屬性可以稱為實例屬性(即定義在this對象上),constructor外聲明的屬性都是定義在原型上的,可以稱為原型屬性(即定義在class上)。hasOwnProperty()函數用於判斷屬性是否是實例屬性。其結果是一個布爾值, true說明是實例屬性,false說明不是實例屬性。in操作符會在通過對象能夠訪問給定屬性時返回true,無論該屬性存在於實例中還是原型中。
class Box{
constructor(num1,num2){
this.num1 = num1;
this.num2=num2;
}
sum(){
return num1+num2;
}
}
var box=new Box(12,88);
console.log(box.hasOwnProperty("num1"));//true
console.log(box.hasOwnProperty("num2"));//true
console.log(box.hasOwnProperty("sum"));//false
console.log("num1" in box);//true
console.log("num2" in box);//true
console.log("sum" in box);//true
console.log("say" in box);//false
類的所有實例共享一個原型對象,它們的原型都是Person.prototype,所以proto屬性是相等的
class Box{
constructor(num1,num2){
this.num1 = num1;
this.num2=num2;
}
sum(){
return num1+num2;
}
}
//box1與box2都是Box的實例。它們的__proto__都指向Box的prototype
var box1=new Box(12,88);
var box2=new Box(40,60);
console.log(box1.__proto__===box2.__proto__);//true
由此,也可以通過proto來為類增加方法。使用實例的proto屬性改寫原型,會改變Class的原始定義,影響到所有實例,所以不推薦使用!
class Box{
constructor(num1,num2){
this.num1 = num1;
this.num2=num2;
}
sum(){
return num1+num2;
}
}
var box1=new Box(12,88);
var box2=new Box(40,60);
box1.__proto__.sub=function(){
return this.num2-this.num1;
}
console.log(box1.sub());//76
console.log(box2.sub());//20
class不存在變量提升,所以需要先定義再使用。因為ES6不會把類的聲明提升到代碼頭部,但是ES5就不一樣,ES5存在變量提升,可以先使用,然后再定義。
//ES5可以先使用再定義,存在變量提升
new A();
function A(){
}
//ES6不能先使用再定義,不存在變量提升 會報錯
new B();//B is not defined
class B{
}
原文鏈接:https://www.jianshu.com/p/86267fab4878