首先class其实只是一个语法糖,绝大部分功能都可以用ES5实现。
类的所有方法都定义在类的prototype上
class MyFn {}
let a = new MyFn();
a.constructor === MyFn.prototype.constructor // ture
复制代码
可以用Object.assign向类里添加多个方法
Object.assign(MyFn.ptototype, {
toString() {},
toValue() {}
})复制代码
此外 MyFn.prototype.constructor === MyFn // true
与es5 不一致的地方,类内部定义的方法是不可枚举的
Object.keys(MyFn.prototype) // []
Object.getOwnPropertyNames(MyFn.prototype) // ['toString', 'toValue']复制代码
constructor方法
constructor方法是类的默认方法,通过new命令生成对象实例时自动调用的方法,如果没有显示定义,会自动添加一个空的constructor方法,constructor方法默认返回实例对象(this),不过我们可以指定返回另一个对象
class Foo {
constructor () {
return Object.create(null);
}
}
new Foo() instanceof Foo // false复制代码
与es5一样,实例的属性除非显示定义在其本身上(即this 对象上),否则都是定义在原型上(prototype)
看这段代码
var a = new Foo();
var b = new Foo();
a.__proto__.newProperty = function () { console.log("Attention !!!") }
b.newProperty(); // "Attention !!!"复制代码
这里__proto__指向的是构造原型(即Foo),所以修改原型必须谨慎,最好别用
利用Class表达式,可以写出立即函数形式
var foo = class {
constructor (val) {
this.value = val;
}
printVal () {
cosole.log(this.value)
}
}("money")复制代码
class 不存在变量提升
new Foo(); // ReferenceError
class Foo {}复制代码
注意类和模块的内部默认就是严格模式
class的继承
子类必须在constructor中调用super方法,否则新建实例会报错,这是因为子类没有自己的this 对象,而是继承了父类的this对象,然后对其进行加工。
es5的继承实质上是先创造子类的实例对象this,然后再将父类的方法添加到this上(Parent.apply(this));
es6的继承实质上是先创造父类的实例对象this,然后通过子类的构造函数修饰this;此时子类的this基于父类实例的加工,所以必须先调用super才能在子类里使用关键字this
类的prototype和__proto__ 属性
1.子类的__proto__指向的是父类,表示构造函数的继承;
2.子类的prototype.__proto__指向的是父类的prototype,表示的是方法的继承;
class A {};
class B {};
Object.setPrototypeOf(B.prototype, A.prototype);
Object.setPrototypeOf(B, A);复制代码
Object.setPrototypeOf = function (obj, proto) {
obj.__proto__ = proto;
return obj;
}复制代码
继承的几种情况
class A extend Object {}
A.__proto__ === Object // true
A.prototype.__proto__ === Object.prototype // true
复制代码
class A {} 即不存在继承
A.__proto__ === Function.prototype // true
A.prototype.__proto__ === Object.prototype // true复制代码
这时A作为一个基类,就是一个函数,直接继承Function.prototype,但是A被调用时返回的是一个对象,所以A.prototype.__proto__ 指向Object.prototype
class A extend null {}
A.__proto__ === Function.prototype // true
A.prototype.__proto__ === undefined // true复制代码
Object.getPrototypeOf() 可从子类上获取父类,该方法可判断一个类是否继承另一个类
子类实例的__proto__的__proto__指向父类实例的__proto__,因此可以通过子类来修改父类实例的行为
class Foo {}
class Bar extends Foo {}
var a1 = new Foo();
var a2 = new Bar();
a2.__proto__ === a1.__proto__; // false
a2.__proto__.__proto__ === a1.__proto__; // true
a2.__proto__.__proto__.printName = function () {
console.log("this is a2")
}
a1.printName() // "this is a2"复制代码
原声构造函数的继承
js的原生构造函数大概有九种
Boolean(); Number(); Array(); String(); Object(); Date(); RegExp(); Error(); Function();
以前这些基本的构造函数是无法被继承的,举个例子,Array构造函数内有一个内部属性[[DefineOwnProperty]]来控制定义性属性时增加length的值,这个内部属性无法在子类汇中获取,所以会出现子类length属性行为不正常。
但是es6可以
class MyArray extends Array {
constructor (...arg) {
super(...arg);
}
}
var myArray = new MyArray();复制代码
class 的 getter 和 setter
es6的getter和setter都是定义在属性的descriptor对象上的
可以用Object.getOwnPropertyDescriptor()
class A {
constructor () { *** }
get oneProp () { *** }
set oneProp () { *** }
}
var oDescriptor = Object.getOwnPropertyDescriptor(A , "oneProp");
"get" in oDescriptor; //true
"set" in oDescriptor; // true复制代码
class 的静态方法
在一个方法前加上 static关键字,该方法就不会被实例继承,但是子类可以继承父类的静态方法,同时静态方法也可以从super上调用。
class的静态属性
静态属性指的是class本身的属性,而不是定义在实例对象(this)上的属性,es6明确规定class只有静态方法没有静态属性
class Foo {
写法一
prop: 1
写法二
static prop : 1
}
Foo.prop // undefined 复制代码
这两种方法都无效,虽然不会报错
new.target 属性
返回new命令所调用的构造函数,如果构造函数不是new命令调用的,那么将返回undefined
以下两种方法用于确保构造函数只能用new调用
1.
function Person(name) {
if(new.target !== undefined) {
this.name = name;
}else {
throw new Error ("必须使用new调用")
}
}
2.
function Person (name) {
if(new.target === Person) {
this.name = name;
}else {
throw new Error ("必须使用new调用")
}
}复制代码
class内部调用new.target返回的是当前的class,子类继承父类时new.target返回的是子类,由此利用这个特点可以写出不能独立使用,只能继承后使用的类
class Shape {
constructor () {
if(new.target === Shape) {
throw new Error ("必须使用new调用")
}
}
}
class Rectangle extends Shape {
constructor () {
super()
***
}
}
var x = new Shape() // 报错
var x = Rectangle() // 正常复制代码
注意在函数外部调用new.target会报错
Mixin模式的实现
Mixin模式指的是将多个类的接口混入另一个类
function mix(...mixins) {
class Mix {}
for (let mixin of minins) {
copyProperties(Mix, mixin);
copyProperties(Mix.prototype, mixin.prototype);
}
return Mix;
}
function copyProperties(target, source) {
for( let key of Reflect.ownKeys(source)) {
if( key !== "constuctor" &&
key !== "prototype" &&
key !== "name") {
let desc = Object.getOwnPropertyDescriptor(source, key);
Object.defineProperties(target, key, desc);
}
}
}
class DistributedEdit extends mix(Loggable, Serializable) {
// ..
}复制代码