class只是一个语法糖,类的所有方法都定义在类的prototype属性上,本质是通过ES5的原型链继承实现的。
注意:
prototype对象的constructor属性直接指向’类’本身,这与ES5的行为是一致的:
Point.prototype.constructor = Point;
使用:
class Point{
constructor(){ //... }
toValue(){ //... }
}
//等同于
Point.prototype = {
constructor(){},
toValue(){},
};
constructor方法:
是类的默认方法,通过new命令生成对象实例时自动调用该方法,没有显式定义时会默认添加一个空的无参构造方法。 构造方法默认返回实例对象,不过可以指定返回另外一个对象。 类必须使用new来调用。
实例的属性除非显式定义在其本身(this对象),否则都是定义在原型上。 所有的实例共享一个原型对象,__proto__属性都是相等的,因此可以通过实例的__proto__属性为“类”添加方法(不建议使用)。在生产环境中,一般通过Object.getPrototypeOf方法来获取实例对象的原型,再为原型添加方法/属性。
Class表达式:
const MyClass = class Me{ //... };
类的名字是MyClass而不是Me,Me只能在Class内部使用,用来指代当前类,因此可以省略Me。 采用Class表达式,可以写出立即执行的Class
注意:
严格模式: 类和模块的内部默认使用严格模式,所以不需要显示声明。
不存在变量提升
name属性: 本质上,ES6的类只是ES5的构造函数的一层包装,所以函数的许多特性都被Class继承,包括name属性。 name属性总是返回紧跟在Class关键字后的类名。
Generator 方法:在某个方法之前加上星号,就表示该方法是一个Generator函数
this的指向: 类的方法内部如果含有this,默认指向类的实例,但是一旦单独使用该方法,很有可能会报错。
解决方法:
在构造方法中绑定this:this.printName = this.printName.bind(this);
使用箭头函数:
this.printName = (name = 'there') => {this.print(`hello ${name}`); }
类相当于实例的原型,所有在类中定义的方法都会被实例继承,在方法前加上staic关键字,表示 该方法不会被实例继承,而是直接通过类调用,被称为静态方法。
父类的静态方法可以被子类继承。
ES6中明确规定,Class内部只有静态方法没有静态属性。 只能写成:
class Foo{//...}; Foo.prop = 1;
目前有一个新提案,其中对静态属性和实例属性都规定了新的写法:
静态属性:
只要在上边的实例属性写法前加上static关键字即可。
实例属性:
以前定义实例属性时只能写在构造函数里。,现在可以用等式写进类的定义中:myProp = 42; 为了获得更强的可读性,对于已经在构造函数中定义的实例属性,新写法允许将其直接列出。
私有方法:
ES6未提供,通过变通方法来模拟实现:
.将私有方法移除模块,因为模块内部的所有方法都是对外可见的。
.利用Symbol值得唯一性将私有方法得名字命名为一个Symbol值。
私有属性:
ES6不支持私有属性,目前有一个提案是在私有属性名前加上#来表示,也可以用来编写私有方法。
new是从构造函数生成实例的命令。ES6为new命令引入了new.target属 性 在构造函数里),返回new命令所作用的构造函 数,如果构造函数不是通过new命令调用的,new.target会返回undefined,因此这个属性可以用于确定构造 函数是怎么调用的。
子类继承父类时,new.target会返回子类,利用这个特点可以写出不能独立使用必须继承后才能使用的类:
if(new.target === super){ throw new Error('本类不能实例化');}
Class 可以通过extends
关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。
class Point {
}
class ColorPoint extends Point {
}
可以用来从子类上获取父类。
Object.getPrototypeOf(ColorPoint) === Point
// true
因此,可以使用这个方法判断,一个类是否继承了另一个类。
子类必须在constructor方法里调用super方法,否则不能新建实例。这是因为子类没有属于自己的this对象,而是继承了父类的this对象而对其进行加工。显然,只有调用了super方法之后,才可以使用this。
而super本身指代的是父类的实例对象,我们可以使用super.
的方式调用父对象的方法。此外,由于对象总是继承于其它对象,所以可以在ES6的任何一个对象中使用super关键字。
e.g.:
let obj = {arr : [1,2,3],
toString(){
console.log('super:'+super.toString.apply(this.arr));
}
};
obj.toString()
这里的super显然指向的是Object实例对象。
大多数浏览器的ES5实现之中,每一个对象都有__proto__属性,指向对应的构造函数的prototype属性。
子类实例的proto属性的proto属性,指向父类实例的proto属性。也就是说,子类的原型的原型,是父类的原型。
Class作为构造函数的语法糖,同时有 prototype属性和__proto__属性,因此同时存在两条继承链。
这些特点和ES5的寄生组合式继承完全一致,所以类的继承可以看做是寄生组合式继承的语法糖
原生构造函数是指语言内置的构造函数,通常用来生成数据结构。
Boolean()
Number()
String()
Array()
Date()
Function()
RegExp()
Error()
Object()
class定义:
class Person{
//构造函数,参数可以带默认值
constructor(name,address='wang'){
this.name = name;
this.address = address;
}
}
const person = new Person('hj')
继承extends:
class Person{
constructor(name,address='mu ke wang '){
this.name = name;
this.address = address;
}
}
class Child extends Person{
constructor(name,address='mu ke wang 2'){
super(name, address);
}
}
const child = new Child('jj');
console.log(child)
方法:
class Person{
constructor(name,address='mu ke wang '){
this.name = name;
this.address = address;
}
//定义一个原型方法方法获取属性
get getName(){
return this.name;
}
//定义一个原型方法方法设置属性
set setName(value){
this.name = value;
}
//方法
toString() {
return '(' + this.name + ', ' + this.address + ')';
}
}
const person = new Person('hj');
console.log(person.getName)
person.setName='hjj'
console.log(person.getName)
Mixin就是把多个类合并成一个类使用,实现起来就是把多个类的property合并起来,只要这个property不是构造函数(constructor),不是原型(prototype),不是类名(name),就合并起来用,但是这里有个问题,不同类之间的方法不能重名,否则会覆盖,所以不是很完美吧感觉。
这个东西合并多个底层模块的使用,先分,再和,这样写底层模块的时候,是区分各个模块去写的,使用的时候,又不用知道那么多模块,搞一个东西按需组装就好了,用起来,不用查到底是哪个底层模块的。
感觉比较优美,既区分了各个模块,好管理,又因为使用起来不用管到底是哪个模块的,透明化,而降低了模块的使用门槛。