1)class是原型的语法糖
class相当于声明一个对象,内部的方法相当于原型中的函数或属性。
class声明的类的原型是不可枚举的,但是可以通过Object.getOwnPropertyNames()得到所有的属性方法,ES5原型中的属性和类除了constructor都是可枚举的。
Object.keys()能得到所有可枚举属性。
Object.getOwnPropertyNames()在ES5中遍历不到constructor,在ES6里可以
2)class声明的类必须用new构造,否则报错
constructor中返回自定义对象,导致原型的constructor属性不等于构造函数。
如果属性没有显示的定义在this上,就在原型上即class上,定义在this上的可以用hasOwnProperty判断为true,没有用this即在原型上的用hasOwnProperty判断为false
3) 尽量不要用实例的proto属性,否则单方面改变原型的方法,影响别的实例使用。
4) 表达式定义的class 类名只能在class内部起作用 在类外访问不到类的name属性
非表达式定义的calss 类名可在外部通过class的构造函数名.name读取
5) 匿名类 let person = new class {}
6) class不存在变量提升
7) class私有方法
class的私有方法只能模拟,但是在类外还能访问到,解决办法有两种:
1.将私有方法移出class 变为模块的私有方法
2.symbol定义方法名,定义在内部,第三方无法访问
8)类方法内部的this指向类的实例 将类内部的方法单独在环境中调用,this又指向运行环境,解决方法有三个:
a. 构造函数中用箭头函数保存this
constructor() {
this.printName = (name = ‘there’) => {
this.print(‘${name}’);
}
}
b. Proxy
c. 在构造函数中为内部方法绑定this
class Logger {
constructor() {
this.print = this.printName.bind(this);
}
}
9)class内部的属性拦截
get和set是设置在Descriptor属性上的,
通过Object.getOwnPropertyDescriptor(Book.prototype, ‘age’) 得到该对象,与ES5一致。
class Book {
constructor() {
}
set age(value) {
console.log('setter ' + value);
}
get age() {
return 'getter';
}
}
10)class的遍历器
*[Symbol.iterator] () {
for(let arg of this.args) {
yield arg;
}
}
11)class的静态方法:不能被实例调用,不被实例继承
但是父类的静态方法可以被子类继承,可通过super调父类静态方法
静态属性 提案
实例属性 提案
class Foo {
static classMethod() {
return 'hello';
}
}
console.log(Foo.classMethod()); // hello
class FooChild extends Foo {
}
console.log(FooChild.classMethod()); // hello
// 静态属性
Foo.prop = 2;
12)new.target属性
确保构造函数只能通过new调用
子类继承父类,父类内部的new.target指向子类的构造。可用该属性实现不能独立使用,必须继承后才能使用的类。
class Shape {
constructor(length) {
if(new.target === Shape) { // 通过子类调用该构造不会报错
throw new Error('不能直接使用该类');
}
}
}
class circle extends Shape {
constructor(r) {
super(); // 调用父类构造函数成功
}
}
13)class继承 子类的构造函数中必须调用super,否则实例化时报错。子类没有this,必须调用父类构造函数得到父类的this。所以在子类中要使用this必须先super(),否则报错。
14)ES5与ES6继承的区别
ES5先构造子类this,将父类方法加到子类this上
ES6构造父类,将父类this给子类
15)判断继承Object.getPropertyOf(child) === parent
16) 子类调super,super虽然代表父类构造函数,但返回子类实例,父类构造函数内部的this指的是子类构造。类似java父类引用指向子类对象。
17) super()用于构造只能在构造函数中调用,不能在其它函数中调用,否则报错。
当做父类的引用,可在普通函数中使用
super.*() 调用父类的方法,此时super相当于parent.prototype。
【注意】super访问不到父类实例上的属性和方法。即父类通过this.定义的。只能访问父类通过原型定义的属性,即parent.prototype.color = ‘red’; 这样子类在函数中通过super.color可访问到。
18) 关于this指向变化的问题,子类调用super父类方法内部的this就变成子类的this了。
子类中的super在静态方法中指向父类,在普通方法中指向父类的原型对象。
使用super必须显示指定 是当做方法使用还是当做原型使用的。
19)类的prototype和proto
子类的proto指向父类构造
子类的prototype的proto指向父类prototype
// B extends A 的实现原理
// B的实例继承A的实例
Object.setPrototypeOf(B.prototype, A.prototype);
// B的实例继承A的静态属性
Object.setPrototypeOf(B,A);
const b = new B();
Object.setPrototypeOf = function(obj, proto) {
obj.__proto__ = proto;
return obj;
}
20)普通对象直接继承Function.prototype
class A{}
A.__proto__ === Function.prototype
A.prototype.__proto__ === Object.prototype (A构造返回一个空对象)
class A extends null {}
A.__proto__ === Function.prototype
A.prototype.__proto__ === undefined
// (A构造返回一个undefined内部Object.create(null))
21)ES6可以继承原生数据结构,自定义子类。
因为ES6的继承是先构造父类的,所以ES6可以取得父类的内部this,实现该功能。
ES5通过绑定this无法实现继承原生数据结构,原生的数据结构忽略绑定this,也不能得到内部this。