在JS裏面,它其實是沒有類的概念的,咱們若是想要實現一個面向對象的一個編程,那麼咱們只可以藉助構造函數,可是構造函數有着自身的缺點,首先第一點它的寫法不清晰,咱們須要繼承的話就須要手動的去操做prototype;其次它面向對象的思想也不是很強烈,因此在ES6它封裝了一層構造函數的語法糖,就叫Class,封裝後有了一個更清晰的寫法,並且更加像一個面向對象編程語言。編程
在ES5中:編程語言
// ES5 的構造函數 function Position(x, y) { this.x = x; this.y = y; } const position = new Position(1, 1);
在ES6中:編輯器
在ES6中,首先有一個class修飾符,class將告訴編輯器聲明瞭一個名叫Position的class。 class是構造函數的語法糖,咱們怎麼來看呢?咱們能夠看一下這個構造函數它的prototype的constructor是不是自己,若是是自己那就說明class的行爲跟構造函數是保持一致的。函數
class Position { constructor(x, y) { this.x = x; this.y = y; } } Position === Position.prototype.constructor; /* true */ const position = new Position(1, 1);
在ES5裏面,好比說咱們在構造函數裏面定義了一些方法,那麼咱們在實例裏面是有這一個方法的,可是ES6不同。在Class裏面,全部的方法所有都定義到了prototype屬性上面,也就是除了this.以外的其餘都定義在了prototype屬性上面去。ui
class Position { constructor(x, y) { this.x = x; this.y = y; } scan() { console.log(this.x, this.y); } } Position.prototype; /* { constructor: class Position scan: ƒ scan() __proto__: Object } */
內部的方法都到prototype上面去,可是這個prototype的方法倒是不可枚舉的,也就是說它的枚舉屬性是false。 若是這個時候咱們想要獲得這個枚舉,想要知道它有哪些方法的話,咱們就須要使用到getOwnPropertyNames。this
class Position { constructor(x, y) { this.x = x; this.y = y; } scan() { console.log(this.x, this.y); } } Object.keys(Position.prototype); /* [] */ Object.getOwnPropertyNames(Position.prototype); /* ["constructor", "scan"] */
class Position { constructor(x, y) { this.x = x; this.y = y; } scan() { console.log(this.x, this.y); } } const position = new Position(1, 1); // 調用自身掃描 position.scan(); // 1 1
解構出來再去調用this指向就不一致了,會出問題報錯:prototype
const { scan } = position; scan(); /* Cannot read property 'x' of undefined */
對於上面的this在解構後調用出現的指向問題出錯咱們能夠有兩種解決辦法: 第一種方法,藉助於箭頭函數: 箭頭函數在定義時this就已經指定了,也就是它什麼定義的和在哪裏定義的,它的this就是什麼。rest
class Position { constructor(x, y) { this.x = x; this.y = y; } // 將scan()函數改成一個箭頭函數,this永遠指向定義時的位置 scan = () => { console.log(this.x, this.y); } } const position = new Position(1, 1); const { scan } = position; scan(); /* 1 1 */
第二種方法,手動的bind:code
class Position { constructor(x, y) { this.x = x; this.y = y; this.scan = this.scan.bind(this); } scan() { console.log(this.x, this.y); } } const position = new Position(1, 1); const { scan } = position; scan(); /* 1 1 */
constructor 構造器,首先它是一個required,也就是它是一個必須的,若是咱們沒有傳入它,那麼就會默認生成一個空的一個constructor。對象
new命令使用時調用一次constructor
class Position { constructor(x, y) { this.x = x; this.y = y; console.log('constructor'); } } const position = new Position(1, 1); /* constructor */
默認返回實例對象
class Position { constructor(x, y) { return Object.create({ name: 'Eric' }); } } const position = new Position(1, 1); 控制檯輸入:position // 獲得一個空對象:{} position instanceof Position; /* false */
實例的屬性除this以外所有定義在原型對象上
hasOwnProperty();
get是攔截它的取,set是攔截它的存,getter和setter必須是成對出現的,並且咱們設置時須要設置getter和setter應該是哪個,應該是當前的對象不存在的,不然就會報一個堆棧溢出,由於它會有一個循環調用的問題。
class Position { constructor(x, y) { this.x = x; this.y = y; } get z() { return this.x; } set z(value) { this.x = 2; } } const position = new Position(1, 1);
static字面意思理解就是靜態,它不能被實例繼承,也就是說在實例裏面是不能調用靜態方法的,它只能經過類自己來調用,這時這個this指向的是當前class自己。
class Position { constructor(x, y) { this.x = x; this.y = y; this.z = 100; } static scan() { console.log(this); } } const position = new Position(1, 1); position.scan(); /* position.scan is not a function */ Position.scan(); /* class Position { constructor(x, y) { this.x = x; this.y = y; this.z = 100; } static scan() { console.log(this); } } */
在以前的代碼裏面屬性 x 和 y 都是定義在constructor內部的,這樣其實是比較清晰的可是有一個提案:把屬性所有放在構造函數的最頂層,咱們能夠直接在構造函數的內部來定義一些變量,固然這個變量是不須要加修飾符的,這時候就默認加到了this的對象上面去。
class Count { x = 0; reduce() { this.x = this.x - 1; console.log(this.x); } increment() { this.x = this.x + 1; console.log(this.x); } } const count = new Count(); count.increment(); count.increment(); count.increment(); count.increment(); // 1 2 3 4
class Point { constructor(x, y) { this.x = x; this.y = y; } scan = () => { console.log(this.x, this.y); } } // SubPoint 繼承了 Point class SubPoint extends Point { } const subPoint = new SubPoint(1, 1); subPoint; /* { scan: () => { console.log(this.x, this.y); } x: 1 y: 1 __proto__: Point } */
class Point { constructor(x, y) { this.x = x; this.y = y; } scan = () => { console.log(this.x, this.y); } } class SubPoint extends Point { constructor(...rest) { // super(...rest) } } const subPoint = new SubPoint(1, 1); /* Must call super constructor in derived class before accessing 'this' or returning from derived constructor */
class Point { constructor(x, y) { this.x = x; this.y = y; } scan = () => { console.log(this.x, this.y); } } class SubPoint extends Point { constructor(...rest) { super(...rest) } } const subPoint = new SubPoint(1, 1); Object.getPrototypeOf(subPoint); /* Point { constructor: class SubPoint __proto__: Object } */