參考文章:阮一峰 Class 的基本語法
類的由來
JavaScript語言的傳統方法是通過構造函數定義並生成新對象,這種寫法和傳統的面向對象語言差異較大。所以,ES6引入了Class這個概念作為對象的模板。
class可以看作只是一個語法糖,它的絕大部分功能,ES5 都可以做到。
// es5 中的構造函數
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.toString = function () {
return '(' + this.name + ',' + this.age + ')';
}
var p = new Person('xiaoMing', 18);
// es6 通過class實現
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
toString() {
return '(' + this.name + ',' + this.age + ')';
}
}
var p = new Person('xiaoMing', 18);
上面代碼定義了一個“類”,可以看到里面有一個constructor方法,這就是構造方法,而this關鍵字則代表實例對象。也就是說,ES5 的構造函數Point,對應 ES6 的Point類的構造方法。
ES6的類可以看作構造函數的另一種寫法。
class Person {
// ...
}
typeof Person // "function"
Person === Person.prototype.constructor // true
上面代碼表明,類的數據類型就是函數,類本身就指向構造函數。
ES6的類在es5中的實現原理
es6中的class的具體使用可以參考阮一峰Class 的基本語法。
我們現在看下es6中的class是怎么通過es5語法實現的,讓我借助babel來一步一步看一下class轉換成es5的代碼
先添加一個類
// es6
class Person {
}
// 通過babel轉換成的es5語法
"use strict";
// 判斷某對象是否為某構造器的實例
function _instanceof(left, right) {
if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
return !!right[Symbol.hasInstance](left); } else { return left instanceof right; }
}
// 檢查聲明的class類是否通過new的方式調用,否則會報錯
function _classCallCheck(instance, Constructor) { if (!_instanceof(instance, Constructor)) {
throw new TypeError("Cannot call a class as a function"); }
}
var Person = function Person() {
_classCallCheck(this, Person);
};
類必須使用new調用,否則會報錯。這是它跟普通構造函數的一個主要區別,后者不用new也可以執行。
添加屬性和方法(包括靜態方法)
// es6
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
eat() {
return 'eat'
}
static say() {
return 'say'
}
}
// 通過babel轉換成的es5語法
"use strict";
// 判斷某對象是否為某構造器的實例
function _instanceof(left, right) {
if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
return !!right[Symbol.hasInstance](left); } else { return left instanceof right; }
}
// 檢查聲明的class類是否通過new的方式調用,否則會報錯
function _classCallCheck(instance, Constructor) { if (!_instanceof(instance, Constructor)) {
throw new TypeError("Cannot call a class as a function"); }
}
/**
*將方法添加到原型上,如果是靜態方法添加到構造函數上,
**/
function _defineProperties(target, props) {
// 遍歷函數數組,分別聲明其描述符 並添加到對應的對象上
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false; // 設置該屬性是否能夠出現在對象的枚舉屬性中。默認為 false
descriptor.configurable = true; // 設置該屬性描述符能夠被改變,同時該屬性也能從對應的對象上被刪除。
if ("value" in descriptor) descriptor.writable = true; // 如果屬性中存在value, value設置為可以改變。
Object.defineProperty(target, descriptor.key, descriptor); // 寫入對應的對象上
}
}
// 收集公有函數和靜態方法,將方法添加到構造函數或構造函數的原型中,並返回構造函數。
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps); // 共有方法寫在property原型上
if (staticProps) _defineProperties(Constructor, staticProps); // 靜態方法寫到構造函數上
return Constructor;
}
var Person = function () {
function Person(name, age) {
_classCallCheck(this, Person);
this.name = name;
this.age = age;
}
_createClass(Person, [{
key: "eat",
value: function eat() {
return 'eat';
}
}], [{
key: "say",
value: function say() {
return 'say';
}
}]);
return Person;
}();