7.Class

引入了Class(類)這個概念,作為對象的模板。通過class關鍵字,可以定義類。

//定義類
class Point { 
   constructor(x, y) {
     this.x = x; this.y = y;
   }
//定義方法
   toString() { 
    return '(' + this.x + ', ' + this.y + ')';
   }
}

typeof Point // "function"
Point === Point.prototype.constructor // true

上面代碼表明,類的數據類型就是函數,類本身就指向構造函數。類的所有方法都定義在類的prototype屬性上面。

Object.assign(Point.prototype, { 
sayAge(){},
toValue(){}
});

類的屬性名,可以采用表達式。

let methodName = "getArea";
class Square{ 
  constructor(length) {  // ... } 
  [methodName]() { 
  // ... }
}

constructor方法

constructor方法是類的默認方法,通過new命令生成對象實例時,自動調用該方法。一個類必須有constructor方法,如果沒有顯式定義,一個空的constructor方法會被默認添加。
constructor方法默認返回實例對象(即this),完全可以指定返回另外一個對象。

class Foo { 
  constructor() {
     return Object.create(null); 
  }
}
new Foo() instanceof Foo // false

Class表達式

與函數一樣,類也可以使用表達式的形式定義。

const MyClass = class Me { 
  getClassName() { 
//me僅用于內部調用使用,外部無效
    return Me.name; 
  }
};
//如果內部無須引用  Me,可簡寫
const MyClass = class {...}

采用Class表達式,可以寫出立即執行的Class。

let person = new class { 
  constructor(name) {
     this.name = name;
   } 
  sayName() { 
    console.log(this.name);
   }
}('張三');
person.sayName(); // "張三"

this的指向

類的方法內部如果含有this,它默認指向類的實例。但是,必須非常小心,一旦單獨使用該方法,很可能報錯。

class Logger { 
  printName(name = 'there') { this.print(`Hello ${name}`); } 
  print(text) { console.log(text); }
}
const logger = new Logger();
const { printName } = logger;
printName(); // TypeError: Cannot read property 'print' of undefined

上面代碼中,printName方法中的this默認指向Logger類的實例。但是,如果將這個方法提取出來單獨使用,this會指向該方法運行時所在的環境,因為找不到print方法而導致報錯。
一個比較簡單的解決方法是,在構造方法中綁定this,這樣就不會找不到print
方法了。

class Logger {
   constructor() { this.printName = this.printName.bind(this); }  
// ...
}

另一種解決方法是使用箭頭函數。

class Logger {
   constructor() {
     this.printName = (name = 'there') => { 
        this.print(`Hello ${name}`);
     }; 
  }  
// ...
}

還有一種解決方法是使用Proxy,獲取方法的時候,自動綁定this

function selfish (target) {
   const cache = new WeakMap(); 
   const handler = { 
    get (target, key) { 
       const value = Reflect.get(target, key); 
      if (typeof value !== 'function') { return value; } 
      if (!cache.has(value)) { cache.set(value, value.bind(target)); } 
      return cache.get(value); 
    } 
  }; 
  const proxy = new Proxy(target, handler); 
  return proxy;
}
const logger = selfish(new Logger());

Class的繼承

class ColorPoint extends Point { 
  constructor(x, y, color) { 
    super(x, y); // 調用父類的constructor(x, y) 
    this.color = color; 
  } 
  toString() {
     return this.color + ' ' + super.toString(); // 調用父類的toString()
   }
}

子類必須在constructor方法中調用super方法,否則新建實例時會報錯。這是因為子類沒有自己的this對象,而是繼承父類的this對象,然后對其進行加工。如果不調用super方法,子類就得不到this對象。
如果子類沒有定義constructor方法,這個方法會被默認添加

Class的取值函數(getter)和存值函數(setter)

class CustomHTMLElement { 
  constructor(element) { this.element = element; } 
  get html() { return this.element.innerHTML; } 
  set html(value) { this.element.innerHTML = value; }
}
var descriptor = Object.getOwnPropertyDescriptor( CustomHTMLElement.prototype, "html");
"get" in descriptor  // true
"set" in descriptor  // true

Class的Generator方法

class Foo { 
  constructor(...args) { this.args = args; } 

  * [Symbol.iterator]() { 
    for (let arg of this.args) { yield arg; }
   }
}
for (let x of new Foo('hello', 'world')) { 
  console.log(x);
}
// hello
// world

上面代碼中,Foo類的Symbol.iterator方法前有一個星號,表示該方法是一個Generator函數。Symbol.iterator方法返回一個Foo類的默認遍歷器,for...of循環會自動調用這個遍歷器。

Class的靜態方法

類相當于實例的原型,所有在類中定義的方法,都會被實例繼承。如果在一個方法前,加上static關鍵字,就表示該方法不會被實例繼承(可被子類繼承),而是直接通過類來調用,這就稱為“靜態方法”。

class Foo { 
  static classMethod() { return 'hello'; }
}
var foo=new Foo()
foo.classMethod();// TypeError: foo.classMethod is not a function
class Bar extends Foo {}
Bar.classMethod(); 
// 'hello'

Class的靜態屬性和實例屬性

靜態屬性指的是Class本身的屬性,即Class.propname,而不是定義在實例對象(this)上的屬性。

ES7有一個靜態屬性的[提案]properties),目前Babel轉碼器支持。

類的實例屬性可以用等式,寫入類的定義之中。

class MyClass { 
  myProp = 42; 
  constructor() { 
    console.log(this.myProp); 
    // 42
   }
}
class ReactCounter extends React.Component { state = { count: 0 };}
//對于已經在對于那些在constructor里面已經定義的實例屬性,新寫法允許直接列出。
class ReactCounter extends React.Component { 
  constructor(props) { 
    super(props); 
    this.state = { 
      count: 0 
    }; 
  } 
  state;
}

類的靜態屬性只要在上面的實例屬性寫法前面,加上static關鍵字就可以了

class MyClass { 
  static myStaticProp = 42; 
  constructor() {
     console.log(MyClass.myProp);
     // 42
   }
}

new.target屬性
new是從構造函數生成實例的命令。ES6為new命令引入了一個new.target屬性,(在構造函數中)返回new命令作用于的那個構造函數。如果構造函數不是通過new命令調用的,new.target會返回undefined因此這個屬性可以用來確定構造函數是怎么調用的。

用處1:可以寫出不能獨立使用、必須繼承后才能使用的類
用處2:確保構造函數只能通過new,不是通過call方式命令調用

Mixin模式的實現

Mixin模式指的是,將多個類的接口“混入”(mix in)另一個類。它在ES6的實現如下。

function mix(...mixins) {
 class Mix {} 
  for (let mixin of mixins) { 
    copyProperties(Mix, mixin); 
    copyProperties(Mix.prototype, mixin.prototype); } 
    return Mix;
  }
  function copyProperties(target, source) { 
    for (let key of Reflect.ownKeys(source)) { 
    if ( key !== "constructor" && key !== "prototype" && key !== "name" ) {
       let desc = Object.getOwnPropertyDescriptor(source, key);         
       Object.defineProperty(target, key, desc); 
  } 
 }
}

上面代碼的mix
函數,可以將多個對象合成為一個類。使用的時候,只要繼承這個類即可。

class DistributedEdit extends mix(Loggable, Serializable) {  // ...}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,993評論 19 139
  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,779評論 18 399
  • class的基本用法 概述 JavaScript語言的傳統方法是通過構造函數,定義并生成新對象。下面是一個例子: ...
    呼呼哥閱讀 4,129評論 3 11
  • 很多人有這樣的感慨:為什么我和另外一個同事差不多同時進公司,工作能力差不多,做的事情也沒比對方少,但是對方就總能獲...
    好聽的暖陽閱讀 224評論 2 4
  • 年度音樂:《小人物》《歲月神偷》 年度歌手:金岐玟,李榮浩 年度閱讀:《與疲憊生活的正面交鋒》《向前一步》《我堅信...
    Banana06閱讀 172評論 0 0