Class
class是es5原型链继承的语法糖,通过babel编译之后还是构造函数,es6的类在次继承上又添加了静态方法、静态属性
class不存在变量提升的问题
constructor
constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加
constructor 方法默认返回实例对象,完全可以返回另外一个对象 Object.create(null)
class Person {
constructor(name,age){
this.name = name;
this.age = age;
}
func() {}
}
const p = new Person("张大炮",20);
//实例化一个类依然使用new关键字,如果没有会报错
// null
class Foo {
constructor() {
return Object.create(null);
}
}
为一个类添加方法,此方法是实例方法,可以被实例对象访问,也可以被子类继承。
但类的内部所有定义的方法,都是不可枚举的
下面采用es5的方式,是可以枚举的
class Point {
constructor(x, y) {
// ...
}
toString() {
// ...
}
}
Object.keys(Point.prototype)
// []
Object.getOwnPropertyNames(Point.prototype)
// ["constructor","toString"]
instance
所有的实例共享一个原型对象
const p1 = new Point(2,3);
const p2 = new Point(3,2);
p..__proto__ === p2.__proto__; // true
所以通过原型添加方法,其他实例也可以使用到该方法
const p1 = new Point(2,3);
p1.__proto__.print = fuction(arg) {
consoel.log(arg)
}
const p2 = new Point(3,2);
p2.print(1) // logs -> 1
由于p1
的原型就是p2
的原型,因此p2
也可以调用这个方法。而且,此后新建的实例也可以调用这个方法。这意味着,使用实例的__proto__
属性改写原型,必须相当谨慎,不推荐使用,因为这会改变Class的原始定义,影响到所有实例
extends
子类必须再constructor方法中调用super方法,否则新建实例时会报错,因为子类没有自己的this对象,而是继承父类的this对象
继承是先创造父类的实例对象this,然后再调用子类的构造函数来修改this,如果子类没有显示的定义constructor,也会被默认的添加。
子类的proto属性指向父类,表示构造函数的继承
子类的prototype属性的proto属性,指向父类的prototype属性,表示方法的继承
class Person {
constructor(name,age){
this.name = name;
this.age = age;
console.log(new.target.name) //指向当前正在执行的函数
}
flag = true
sayName(){
return this.name;
}
static classFunc(){
console.log("此乃静态方法,不会被实例所继承")
}
}
class Child extends Person {
constructor(bgk){
super("name",20)
this.bgk = bgk
}
}
const p1 = new Person();
const c1 = new Child();
c1.__proto__ === p1.__proto__ //false
c1.__proto__.__proto__ === p1.__proto__ //true 子类的原型的原型,是父类的原型。
class A {
}
class B extends A {
}
B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true
instance child
子类的实例的 proto_ 属性的 proto__ 属性,指向的父类实例的 proto属性。既子类的原型的原型,是父类的原型_
const p1 = new Person();
const c1 = new Child();
c1.__proto__.__proto__ === p1.__proto__; // true
this
类的方法内部如果含有this,它默认指向类的实例。
解决 :
当为函数时, 必须在子类的constructor里调用,表示父类的构造函数 ; 既super内部的this默认指向的是父类
当为对象使用时,指向的父类的原型对象。由于指向的父类的原型对象,所以父类的实例方法,super无法访问
super 在被当作对象调用时,this指向的子类
class Parent {
constructor() {
this.site = “上海”
}
fn() {
console.log("fn")
}
}
class Child extends Parent{
constructor() {
super();
super.fn(); // logs -> fn
super.site // logs -> undefined
}
}
const b = new Child();
new/target/name
new 是从构造函数生成实例的命令。
如果构造函数不是通过new 命令调用的,new.target 会返回undefiend
总是指向被执行的函数,被实例化的时候指向自己。
子类继承父类的时候,new.target 会返会子类
static
类相当于实例的原型,所有类中的方法都可以被实例继承, 一个方法或属性前加上static关键字,表示该方法不会被实例继承,只可以通过类来调用. 称之为静态方法
父类的静态方法,可以被子类继承,也可以从super对象上调用
getter/setter
在Class内部可以使用
get
和set
关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为class Get{ static mso = 10 get mo() { return Get.mso } set mo(value) { console.log("newValue --> %s", value); Get.mso = value; } } const g = new Get() console.log(g.mo) // get this.mso g.mo = "新的值" // set this.mso = "新的值"
Mixin
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" ) {
let desc = Object.getOwnPropertyDescriptor(source, key);
Object.defineProperty(target, key, desc);
}
}
}
// 使用
class Foo extends min(Cl1, Cl2) {}
Decorator
修饰器是一个函数,用来修改类的行为
修改类的行为,是在代码编译时发生的,而不是运行时。
类的修饰
~~ 一个简单的栗子
function test(target) {
target.Desc = "一个类的表述"
}
@test
class Foo{}
log(Foo.Desc) // -> 一个类的描述
实际应用中一个修饰器可以做很多事,当前也需要很多参数
function test(targs) {
return function(target) {
target.Desc = targs
}
}
@test(`自定义描述`)
class Foo{}
log(Foo.Desc) // -> 自定义描述
方法的修饰
修饰器还可以修饰方法
属性的话可以使用getter
方法的修饰参数
- target 修饰的目标对象
- name 要修饰的属性名
- descoptor 该属性的描述对象
~~ 简单的栗子
class Person {
@nonenumerable
get name() { return "张大炮"; }
}
function nonenumerable(target, name, descriptor) {
descriptor.enumerable = false;
return descriptor;
}
上面修饰符的作用修改属性为不可枚举性
多个修饰符的叠加是一层一层进行的,