ES6 的類提供了幾點明顯的好處:html
兼容當前大量的代碼。
相對於構造器和構造器繼承,類使初學者更容易入門。
子類化在語言層面支持。
能夠子類化內置的構造器。
再也不須要繼承庫;框架之間的代碼變得更加輕便。
爲未來的高級特性奠基了基礎: traits (或者 mixins ), 不可變實例,等等。
使工具可以靜態分析代碼( IDE ,類型檢測器,代碼風格檢測器,等等)。git
ES6 類掩蓋了 JavaScript 繼承的本質;
類會禁錮你,由於強制性的 new。es6
function Point(x, y){ this.x = x; this.y = y; } Point.prototype.toString = function(){ return "(" + this.x + "," + this.y + ")"; } const p = new Point(1,2); console.log(p);//Point {x: 1, y: 2} console.log(p.toString());//(1,2)
上面代碼的改用class來寫github
class Points { constructor(x, y) { this.x = x; this.y = y; } toString(){ return '(' + this.x + ',' + this.y + ')'; } } const ps = new Points(1, 2); console.log(ps);//Points {x: 1, y: 2} console.log(ps.toString());//(1,2)
ES6的類能夠看做構造函數的另外一種寫法bash
class Cty{ //.... } console.log(typeof Cty);//function console.log(Cty === Cty.prototype.constructor);//true //類的數據類型是函數,類自己就指向構造函數
使用的時候,也是直接對類使用new命令,跟構造函數的用法徹底一致框架
class Bar { doStuff(){ console.log('stuff'); } } const b =new Bar(); b.doStuff();//stuff
類的實例上面的方法,其實就是調用原型上的方法函數
class B {}; const BS = new B(); console.log(BS.constructor === B.prototype.constructor);//true
class Poin{ constructor(x,y){ this.x = x; this.y = y; } toString(){ return `(${this.x},${this.y})`; } } class ColorPoin extends Poin{ constructor(x,y,color){ super(x,y); this.color = color; } toString(){ return super.toString() + " in " + this. color; } } // 類型 console.log(typeof Poin);//function //news實例 const cp = new ColorPoin(25,8,'green'); console.log(cp.toString());//(25,8) in green console.log(cp instanceof ColorPoin);//true console.log(cp instanceof Poin);//true // instanceof測試構造函數的prototype屬性是否出如今對象的原型鏈中的任何位置
下面是一些方法:工具
class ObjAssign { constructor(name, age){ this.name = name; this.age = age; } } Object.assign(ObjAssign.prototype,{ toString(){ console.log("string"); }, toValue(){ console.log("value") } }) const Obj = new ObjAssign('Bob',24); console.log(Obj); Obj.toString();//string Obj.toValue();//value console.log(Object.keys(ObjAssign.prototype));//["toString", "toValue"] console.log(Object.getOwnPropertyNames(ObjAssign.prototype));// ["constructor", "toString", "toValue"]
class Pott { constructor(x,y){ this.x = x; this.y = y; } toString() { return '(' + this.x + ',' + this.y + ')'; } } const pott = new Pott(2,3); pott.toString(); console.log(pott.hasOwnProperty("x"));//true console.log(pott.hasOwnProperty("y"));//true console.log(pott.hasOwnProperty("toString"));//false console.log(pott); console.log(pott.__proto__); console.log(pott.__proto__.hasOwnProperty("toString"));//true const p1 = new Pott(2,3); const p2 = new Pott(3,3); console.log(p1.__proto__ === p2.__proto__);//true p1.__proto__.printName = function(){ return "Oops"; } console.log(p1.printName());//Oops console.log(p2.printName());//Oops const p3 = new Pott(4,2); console.log(p3.printName());//Oops
prop屬性有對應的存值函數和取值函數測試
class MyClass { constructor(){ //... } get prop(){ return 'getter'; } set prop(value){ console.log("setter:" + value); } } const inst = new MyClass(); inst.prop = 123;//setter: 123 console.log(inst.prop)//getter
class CustomHTMLElement { constructor(element) { this.element = element; } get html() { return this.element.innerHTML; } set html(value) { this.element.innerHTML = value; } } const descriptor = Object.getOwnPropertyDescriptor( CustomHTMLElement.prototype, "html" ); console.log("get" in descriptor) // true console.log("set" in descriptor) // true
const MyCl = class Me { getClassName() { return Me.name; } } const inMe = new MyCl(); console.log(inMe.getClassName());//Me 只在class內部有定義
const person = new class{ constructor(name){ this.name = name; } sayName(){ console.log(this.name); } }('張三'); person.sayName();//張三
class Mine { //... } console.log(Mine.name);//Mine
this.printName = this.printName.bind(this)綁定解決this
class Logger{ constructor(){ this.printName = this.printName.bind(this); } printName(name = 'there'){ this.print(`Hello ${name}`); } print(text){ console.log(text); } } const logger = new Logger(); const {printName} = logger; printName();//Hello there
若是在一個方法前,加上static關鍵字,就表示該方法不會被實例繼承,而是經過類來調用
class Foo{ static classMethod() { return 'hello'; } } console.log(Foo.classMethod());//Hello const foo = new Foo(); // console.log(foo.classMethod())//foo.classMethod is not a function
class Fun { static bar(){ this.baz(); } static baz(){ console.log('hello'); } baz(){ console.log('world'); } } Fun.bar();//hello
父類靜態方法能夠被子類調用
class Func{ static classMethod() { return 'hello'; } } class Baa extends Func{ static classMethod(){ console.log(super.classMethod + ",too") ; } } Baa.classMethod();//hello,too
class IncreasingCounter{ // constructor(){ // this._count = 0; // } _count = 0; get value(){ console.log('getting the current value'); return this._count; } increment(){ this._count++; } }
確保函數只能經過new命令調用
function PersonMan(name){ if(new.target !== undefined){ this.name = name; }else{ throw new Error('必須使用new命令生成實例') } } function PersonWoman(name){ if(new.target === PersonWoman){ this.name = name; }else{ throw new Error('必須使用new命令生成實例') } } const personman = new PersonMan('張三'); const personwoman = new PersonWoman('張三'); // const personwoman2 = PersonWoman.call(PersonWoman,'張三');//報錯
內部調用new.target會返回當前的class
class Rectangle{ constructor(length,width){ console.log(new.target); console.log(new.target===Rectangle); this.length = length; this.width = width; } } const rectangle = new Rectangle(3,4);
子類繼承父類時,new.target會返回子類
class Rec{ constructor(length,width){ console.log(new.target); console.log(new.target===Rectangle); console.log(new.target===Square); this.length = length; this.width = width; //... } } class Square extends Rec{ constructor(length,width){ super(length,width); } } const squareA = new Square(3,6);//false/true