class
对象原型的语法糖。使用class关键字定义类。
基础语法
let methodName = "getArea";
class Point{
constructor(x,y){
this.x = x;
this.y = y;
}
toString(){
return '('+this.x+','+this.y+')';
}
[methodName](){
return "使用变量声明方法";
}
}
typeof Point //"function"
Point === Point.prototype.constructor //true
//使用Object.assign()方法对一个类添加方法。
Object.assign(Point.prototype,{
toValue(){}
});
1.定义类不需要加function保留字;
2.类中的方法不需要逗号分隔;
3.类的数据类型是函数,类本身就是构造函数;
4.类中定义的方法是定义在类的prototype属性上,调用实例方法就是调用原型上的方法。
类中定义的方法都是不可枚举的。
//不能枚举类中定义的toString方法及构造函数
Object.keys(Point.prototype); //["toValue"]
//获取到所有的内部方法
Object.getOwnPropertyNames(Point.prototype);
//["constructor","toString","toValue"]
实例调用
construct默认构造函数,绑定this对象;显示定义或者默认添加。
实例对象使用new命令,所有的实例对象共享一个原型对象。
let p1 = new Point(1,2);
let p2 = new Point(3,4);
p1.__proto__ === p2.__proto__; //true
//this绑定到实例对象p1自身的属性,方法定义在原型上
p1.hasOwnProperty('x'); //true
p1.hasOwnProperty('toString'); //false
p1.__proto__.hasOwnProperty('toString'); //true
在实例上定义方法时,不要使用
__proto__
属性定义,所有的实例共享一个原型对象。
name属性总是返回class关键字的类名。
Point.name //"Point"
可以使用表达式定义类
//类名时Myclass不是my,my供内部使用
const Myclass = class my{
getClassName(){
return my.name;
}
}
//my可省略,可写出立即执行的类
const Myclass = new class{
//...
}();
不存在变量提升。子类必须定义在父类后
类继承
使用extends关键字实现继承。
class ColorPoint extends Point{
constructor(x,y,color){
super(x,y); //调用父类的构造函数
this.color = color
}
toString(){
return this.color+' '+super.toString(); //调用父类的toString()
}
}
子类必须在construct方法中调用super方法;super指代父类的this对象。
子类没有自己的this对象,调用super方法得到父类的this对象。
子类只有调用了super方法后,才可以使用this关键字。
类的prototype属性和__proto__
属性
每一个对象都有
__proto__
属性,指向对应的构造函数的prototype属性,class类同时存在这两条属性,存在两条继承链。
1.子类的__proto__
属性表示构造函数的继承,总是指向父类。
2.子类prototype属性的__proto__
属性表示方法的继承,总是指向父类的prototype属性。
//作为对象,子类的__proto__属性是父类
ColorPoint.__proto__ === Point //true
//作为构造函数,子类的prototype原型__proto__属性是父类的实例
ColorPoint.prototype.__proto__ === Point.prototype //true
Object.getPrototypeOf();
//可以用于从子类上获取父类;判断一个类是否继承另一个类。
子类实例的__proto__
属性的__proto__
属性是父类实例的__proto__
属性。
原生构造函数的继承
Boolean(),Number(),String(),Array(),Date(),Function(),RegExp(),Error(),Object()
ES5是先新建子类的实例对象this,再将父类的属性添加到子类上,
由于父类的内部属性无法获取,导致无法继承原生的构造函数。
ES6允许继承原生构造函数定义子类。ES6先新建父类的实例对象this,然后再
用子类的构造函数修饰this,这使得父类的所有属性都可以继承。
ES6可以在原生数据结构上定义自己的数据结构。
Class的静态方法,使用static关键字;表示该方法不会被实例继承,直接通过类调用该方法。
Class的静态属性,定义在实例对象上的属性。
//目前支持的写法
Point.name = "point";
Point.name //"point"
//es7
class Point{
name = "li";
static age = "23";
}
new.target属性,返回new命令所作用的构造函数。如果不是new命令调用的,则返回undefined。
可用于确定构造函数是怎么调用的。
1.子类继承父类时new.target返回子类。
2.可以用作父类不能独立使用,必须继承
class Point{
constructor(){
if(new.target == Point){
throw new Error("不能实例化");
}
}
}
3.在函数外部使用则会报错
Mixin模式,将多个类的接口“混入”另一个类。
function mix(...mixins){
class Mix{}
for(let mixin of mixins){
copyProperties(Mix,mixin);
copyProperties(Mix.prototype,mixin.prototype);
}
return Mix;
}
function copyProperties(target,source){
for(let key of Reflect.ownKeys(source)){
if(
key!=="constructor"
&&key!=="prototype"
&&key!=="name" //类属性,返回class关键字后的类名。
){
let desc = Object.getOwnPropertyDescriptor(source,key);
Object.defineProperty(target,key,desc);
}
}
}
修饰器
是一个表达式,用于修改类的行为;是在编译时发生。ES7,使用Babel转码器
function decorator(target){
target.name = "li";
}
@decorator
class User{}
User.name; //"li"
//修饰器函数可接受三个参数,目标函数、属性名和该属性的描述对象(descriptor)
function decorator(name){
return function(target){
target.name = name;
}
}
@decorator("lu")
class User{}
User.name; //"lu"
方法的修饰
//name方法只读
class User{
@readonly
name(){return `$(this.name)`}
}
//三个参数,修改方法的默认行为
readonly(User.prototype,'name',descriptor);
function readonly(target,name,descriptor){
/**
{
value:name,
enumerable:false,
configurable:true,
writable:true
}
*/
descriptor.writable = false;
return descriptor;
}
修饰器不能用于函数,函数存在提升。
可以使用第三方已经定义好的修饰器,core-decorator.js
1.使用修饰器实现自动发布事件,在修饰器中定义事件,在对象的方法调用时自动发出。
2.Mixin模式,在一个对象汇中混入另外一个对象的方法。对象继承的一种替代方案。
3.Trait是一种修饰器,traits-decorator