ES6 的class可以看作只是一个语法糖,注意以下几个可能会犯错误的点。
实例属性
除了定义在constructor()方法里面的this上面,也可以定义在类的最顶层(等于号赋值)
。
类中的方法是定义在原型链上(Animal.prototyp)
,包括getter/setter函数。
详细可看Babel Class解析为ES5的代码 (opens new window)
class Animal {
// 实例属性,定义在类实例对象上(a.speak1)
name = 'MyAnimal'
speak1 = () => {
return this
}
// 原型链方法,定义在原型上(Animal.prototype.speak)
speak() {
return this;
}
// get也是定义在原型上(Animal.prototype.[get getName])
get getName() {
return this.name
}
}
const a = new Animal()
console.log(a) // { name: 'MyAnimal', speak1: f , __proto__: {constructor, speak, ...} }
实例属性this在定义时就已确定指向当前实例(箭头函数)。
类的方法(包括get/set函数)this默认指向当前实例,但一旦遇到解构时(上下文环境变化),this的值可能变化。
class Animal {
// 实例属性。this在书写代码时,就已经确定为当前实例对象(箭头函数)
speak1 = () => {
console.log(this, 'speak1') // 在代码
return this
}
// 原型链方法。this依赖于运行环境,默认是当前实例对象
speak() {
console.log(this, 'speak')
return this;
}
}
let a = new Animal()
a.speak() // a
a.speak1() // a
let { speak, speak1 } = a
speak() // undefined。重要:解构后原型链上的方法,this为undefined
speak1() // a
类继承时,属性是直接继承,而原型链上的方法是一层一层的继承。
class B extends Animal {
b1 = () => {
console.log(this)
}
b() {
console.log(this)
}
}
// B(属性)->B.prototype -> A.prototype
console.log(new B()) // { speak1, b1, __proto__(B): {b, __proto__(A): {speak} } }
class C extends B {
c1 = () => {
console.log(this)
}
c() {
console.log(this)
}
}
// C(属性) -> C.prototype ->B.prototype -> A.prototype
console.log(new C()) // { speak1, b1, c1, __proto__(C): {c, __proto__(B): {b, __proto__(A):{speak} } } }
ES5的继承
,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。
ES6的继承
则不同,在单重继承时,基本同ES5一致;当多重继承时,子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。详细看以下babel es6转es5 _possibleConstructorReturn应用。
es6:
class Person {
static body = 'bofy';
constructor(age) {
this.name = 123
this.age = age
}
getName() {
return this.name
}
}
class Doctor extends Person {
constructor(age) {
super(age)
this.skill = 'shoushu'
}
}
// 多重继承
class Superman extends Doctor {
}
var d = new Doctor(35)
d.name
babel 转译为 es5:
"use strict";
function _typeof(obj) {
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
_typeof = function _typeof(obj) {
return typeof obj;
};
} else {
_typeof = function _typeof(obj) {
return obj &&
typeof Symbol === "function" &&
obj.constructor === Symbol &&
obj !== Symbol.prototype
? "symbol"
: typeof obj;
};
}
return _typeof(obj);
}
function _possibleConstructorReturn(self, call) {
// call有值,则返回call
if (call && (_typeof(call) === "object" || typeof call === "function")) {
return call;
}
return self
}
function _getPrototypeOf(o) {
// setPrototypeOf是ES6提案
_getPrototypeOf = Object.setPrototypeOf
? Object.getPrototypeOf
: function _getPrototypeOf(o) {
// getPrototypeOf在ES5就有,只是ES6中有一些改动
return o.__proto__ || Object.getPrototypeOf(o);
};
return _getPrototypeOf(o);
}
// 原型链继承
function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function");
}
// 实现继承
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: { value: subClass, writable: true, configurable: true }
});
// 设置subClass.__proto__ = superClass,为的是getPrototypOf时能拿到superClass这个构造函数。
if (superClass) _setPrototypeOf(subClass, superClass);
}
function _setPrototypeOf(o, p) {
_setPrototypeOf =
Object.setPrototypeOf ||
function _setPrototypeOf(o, p) {
o.__proto__ = p;
return o;
};
return _setPrototypeOf(o, p);
}
function _instanceof(left, right) {
if (
right != null &&
typeof Symbol !== "undefined" &&
right[Symbol.hasInstance]
) {
return right[Symbol.hasInstance](left);
} else {
return left instanceof right;
}
}
// 只允许new方式创建class
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;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
var Person =
/*#__PURE__*/
(function() {
function Person(age) {
// 检查必须使用new关键字创建类实例
_classCallCheck(this, Person);
this.name = 123;
this.age = age;
}
// class中的方法,都是在Person.prototype
// class中的静态属性,都是在Person上
_createClass(Person, [
{
key: "getName",
value: function getName() {
return this.name;
}
}
]);
return Person;
})();
_defineProperty(Person, "body", "bofy");
var Doctor =
/*#__PURE__*/
(function(_Person) {
// Doctor继承自Person
// Doctor.prototype.__proto__ === Person.prototype(因为Doctor.prototype = Object.create(Person.prototype))
// Doctor.__proto__ === Person(因为Object.setPrototypeOf(Doctor, Person))
_inherits(Doctor, _Person);
function Doctor() {
var _this;
_classCallCheck(this, Doctor);
/**
* 子类必须在constructor方法中调用super方法,否则新建实例时会报错。
* 等同于
* _this = Person.call(this, age) ?Person.call(this, age) : this
* 在单重继承时,Person.call()执行后返回空;多重继承时, Person.call()有值返回
* **/
_this = _possibleConstructorReturn(
this,
_getPrototypeOf(Doctor).call(this, age)
);
_this.skill = "shoushu";
// 返回this。在多重继承中,this是父类的this(即:子类实例的构建,基于父类实例。)
return _this;
}
return Doctor;
})(Person);
var Superman =
/*#__PURE__*/
function (_Doctor) {
_inherits(Superman, _Doctor);
function Superman() {
_classCallCheck(this, Superman);
/**
* class有多重继承特征,此时子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。
**/
return _possibleConstructorReturn(this, _getPrototypeOf(Superman).apply(this, arguments));
}
return Superman;
}(Doctor);
var d = new Doctor(35);
d.name;