一、Class
1、ES6中的class可以看作是一個語法糖,它的絕大部分功能,ES5都可以做到,新的class寫法只是讓對象原型的寫法更加清晰,更加面向對象編程的語法而已。
class Point { // 定義一個Point類
constructor(x,y){ // 構造方法
this.x=x;
this.y=y;
}
// =>類的所有方法都定義在類的prototype屬性上。
// =>在類的實例上調用方法,其實就是調用原型的方法。
// =>類的內部所有方法都是不可枚舉的
toString(){ // 定義toString方法
return '('+this.x+','+this.y+')';
}
}
// =>Object.assign方法可以方便地一次向類添加多個方法。
Object.assign(Point.prototype,{
toValue1(){ return "添加方法1"},
toValue2(){ return "添加方法2"}
})
console.log(typeof Point); // =>function =>類的數據類型就是函數
console.log(Point === Point.prototype.constructor) // =>true =>類的本身就指向構造函數
var point1=new Point(); // 使用的時候也是直接對類使用new命令,跟構造函數的用法完全一致。
console.log(point1.toString()); // =>(undefined,undefined)
var point2=new Point(1,1); // 使用的時候也是直接對類使用new命令,跟構造函數的用法完全一致。
console.log(point2.toString()); // =>(1,1)
2、類和模塊的內部默認使用嚴格模式,所以不需要使用 use strict 指定運行模式。只要將代碼寫在類或模塊之中,那麼就只有嚴格模式可用。
3、constructor 方法是類的默認方法,通過 new 命令生成對象實例時自動調用該方法。一個類必須有 constructor 方法,如果沒有顯示定義,一個空的 constructor 方法會被默認添加。
4、類必須使用 new 來調用,否則就會報錯。
5、實例的屬性除非顯示定義在其本身(即 this 對象)上,否則都是定義在原型(即 Class )上。
6、類的實例共享一個原型對象。
7、Class表達式
// =>Class也可以使用表達式的形式定義。
// =>注意這個類的名字是MyClass而不是Me,Me只在Class的內部代碼可用,指當前類。
const MyClass = class Me{
getClassName(){
return Me.name;
}
}
8、類不存在變量提升
9、ES6不提供私有方法,不支持私有屬性。目前有一個提案爲class加私有屬性。方法是在屬性名前,使用#來表示。
10、類的方法內部如果含有 this,它將默認指向類的實例。
11、name 屬性返回 class 後面的類名。
class Point{
}
console1.log(Point.name); //=>"Point"
12、在類的內部可以使用 get 和 set 關鍵字對某個屬性設置存儲函數和取值函數,攔截該屬性的存取行爲。
class MyClass{
constructor(){
// ...
}
get prop(){
return 'getter';
}
set prop(value){
console.log('setter'+value);
}
}
let inst=new MyClass();
inst.prop=123; // =>setter123
console.log(inst.prop); // =>getter
13、存值函數和取值函數是設置在屬性的Descriptor對象上的。
14、如果某個方法之前加上星號(*),就表示該方法是一個Generator函數。
15、類相當於實例中的原型,所有在類中定義的方法都會被實例繼承。如果在一個方法前加上static關鍵字,就表示該方法不會被實例繼承,而是直接通過類調用,稱之爲“靜態方法”。
class Foo{
static classMethod(){
return 'hello';
}
}
console.log(Foo.classMethod()); // =>hello
let foo=new Foo();
foo.classMethod(); // =>Uncaught TypeError: foo.classMethod is not a function
16、父類的靜態方法可以被子類繼承。
class Foo{
static classMethod(){
return 'hello';
}
}
class Bar extends Foo{
}
console.log(Bar.classMethod()); // =>hello
17、 靜態方法也可以從super對象上調用。
class Foo{
static classMethod(){
return 'hello';
}
}
class Bar extends Foo{
static classMethod(){
return super.classMethod()+',too';
}
}
console.log(Bar.classMethod()); // =>hello,too
18、靜態屬性指的是Class本身的屬性,即Class.propname,而不是定義在實例對象(this)上的屬性。Class內部只有靜態方法,沒有靜態屬性。正確寫法如下:
class Foo{
}
Foo.prop=1;
console.log(Foo.prop); // =>1
19、Class的實例屬性可以直接用等式寫入類的定義之中。
20、Class的靜態屬性只要在實例屬性寫法前面加上 static 關鍵字就可以了。
21、new.target屬性用於確定構造函數是怎麼調用的。
二、ES6中Class的繼承
首先我們要知道一下這些要點:
1、Class通過 extends 關鍵字實現繼承。
2、super 關鍵字既可以當作函數使用,也可以當作對象使用。
① super 作爲函數調用時表示父類的構造函數,用來新建父類的this對象。
同時需要注意:
a、子類必須在 constructor 方法中調用 super 方法,否則新建實例時會報錯。
b、如果子類沒有定義 constructor 方法,那麼這個方法就會被默認添加。
c、在子類的構造函數中,只有調用 super 方法之後才能夠使用 this 關鍵字,否則會報錯。
d、super 雖然代表了父類的構造函數,但是返回的是子類的實例,即 super 內部的 this 指定的是子類。
e、super () 只能用在子類的構造函數之中,用在其他地方會報錯。
② super 作爲對象時在普通方法中指向父類的原型對象;在靜態方法中指向父類。
同時需要注意:
a、由於 super 指向父類的原型對象,定義在父類實例上的方法或屬性是無法通過 super 調用的。如果定義在父類的原型上,super 就可以取到。
b、ES6規定,通過 super 調用父類的方法時,super 會綁定子類的 this。
c、如果通過 super 對某個屬性賦值,這時 super 就是 this,賦值的屬性會變成子類實例的屬性。
d、如果 super 作爲對象用在靜態方法之中,這時 super 將指向父類,在普通方法之中指向父類的原型對象。
class Point{ // 定義了一個名字爲Point的類
constructor(x,y){ // constructor是一個構造方法,用來接收參數
this.x=x;
this.y=y;
this.z=1;
this.a=3;
}
toString(){
return "顏色";
}
print(){
console.log(this.z);
}
static myMethod(msg){
console.log('static',msg);
}
myMethod(msg){
console.log('instance',msg);
}
}
class ColorPoint extends Point{
constructor(x,y,color){
super(x,y); // 調用父類的constructor(x,y)
this.z=2;
this.a=4;
super.a=5;
this.color=color;
console.log(super.a);
console.log(this.a);
}
toString(){ // 這是一個類的方法,注意前面沒有function
return this.color+' '+super.toString(); // 調用父類的toString()
}
m(){
super.print();
}
static myMethod(msg){
super.myMethod(msg);
}
myMethod(msg){
super.myMethod(msg);
}
}
let color1=new ColorPoint(1,1,"red");
let color2=new ColorPoint(2,2,"blue");
console.log(color1);
console.log(color1.toString());
color1.m();
ColorPoint.myMethod("你好");
color1.myMethod("你好");
console.log("===================");
console.log(color2);
console.log(color2.toString());
color2.m();
3、Class 作爲構造函數的語法糖,同時有 prototype 屬性和 __proto__ 屬性,因此同時存在兩條繼承鏈。
①子類 __proto__ 屬性表示構造函數的繼承,總是指向父類。
②子類 prototype 屬性的 __proto__ 屬性表示方法的繼承,總是指向父類的 prototype屬性。
class A{
}
class B extends A{
}
console.log(B.__proto__ === A); // =>true
console.log(B.prototype.__proto__ === A.prototype); // =>true
補充:這兩條繼承鏈還可以理解爲:作爲一個對象,子類(B)的原型( __proto__ 屬性)是父類(A);作爲一個構造函數,子類(B)的原型( prototype 屬性)是父類的實例。(感覺很像 組合繼承/僞經典繼承)
4、extends 關鍵字後只要是一個有 prototype 屬性的函數,就能被繼承(除了 Function.prototype 函數)。
下面討論三種特殊情況:
①子類繼承Object
這種情況下,子類就是構造函數 Object 的複製,A的實例就是 Object 的實例。
class B extends Object{
}
console.log(B.__proto__ === Object); // =>true
console.log(B.prototype.__proto__ === Object.prototype); // =>true
②不存在任何繼承
這種情況下,父類作爲一個基類(即不存在任何繼承)就是一個普通函數,所以直接繼承Function.prototype。但是,父類調用後返回的是一個空對象(即 Object 實例),所以 父類.prototype.__proto__ 指向構造函數( Object )的 prototype 屬性。
class B {
}
console.log(B.__proto__ === Function.prototype); // =>true
console.log(B.prototype.__proto__ === Object.prototype); // =>true
③子類繼承null
這種情況和第二種情況很類似。父類作爲一個基類(即不存在任何繼承)就是一個普通函數,所以直接繼承Function.prototype。
class B extends null{
}
console.log(B.__proto__ === Function.prototype); // =>true
console.log(B.prototype.__proto__ === undefined); // =>true
但是,父類調用後返回的對象不繼承任何方法,所以它的 __proto__ 指向 Function.prototype ,即執行了下面的代碼。
class B extends null{
constructor(){
return Object.create(null);
}
}
5、子類實例的 __proto__ 屬性的 __proto__ 屬性指向父類實例的 __proto__ 屬性。即子類的原型的原型是父類的原型。