ES6 学习笔记 (Class)

class 内部属性/方法

Object.getOwnPropertyNames(class{}); // ["length", "prototype"]
Object.getOwnPropertyNames(class{}); // ["length", "prototype", "name"]
Object.getOwnPropertyNames(class cl{}.prototype); // ["constructor"]复制代码

基本用法

// 1️⃣ function 与 class 的区别
/*---------------- ES5 写法 ----------------*/
Object.getOwnPropertyNames(Function); // ["length", "name", "prototype"]
Object.getOwnPropertyNames(Function.prototype); // ["length", "name", "arguments", "caller", "constructor", "apply", "bind", "call", "toString"]
function Point(x, y) {
    this.x = x;
    this.y = y;
}
Point.prototype.toString = function () {
    return '(' + this.x + ',' + this.y + ')';
}
// 实例化时能够不须要new
const point = new Point(10, 20);
Object.keys(Point.prototype); // ['toString']
Object.getOwnPropertyNames(Object.prototype); // ['constructor', 'toString']
/*---------------- ES6 写法 ----------------*/
const methodName = 'getArea';
const privateMehod = Symbol('privateMethod');
const privateVariable = Symbol('privateMethod');
// Class 继承了 Function 的属性,如 name 等
// Class不像 Function 存在变量提高,必须先定义才能使用
// Class 实例化必须使用 new 调用,不然 TypeError: Class constructor Foo cannot be invoked without 'new'
class Point {
    // 该方法不被继承,只能 Point.staticMethod() 调用
    static staticMethod() { 
        // 这里 this 指的是类而不是实例
        this.toString(); 
    }
    // 容许静态和非静态方法重名
    static toString() { 
        console.log('');
    }
    // 类的实例属性
    state = {
        value: '100' 
    };
    // class 内部默认严格模式 'use strict'
    // class 内部的全部方法都是公开的,除了Symbol表达式定义的方法名除外
    // 构造函数,至关于上面的 ES5 Point,会默认添加
    constructor(x, y) { 
        console.log('new.target.name: ', new.target.name);
        // new.target 指向当前正在执行的function,即返回当前 class
        // 实例经过 new 构造时输出true,当在被继承的子类构造函数中执行时,输出 false,new.target只能在函数或类内部使用
        console.log(new.target === Point); 
        // x、y属性定义在其自己上,即 this 上
        // this 默认指向类的实例,经过 super 调用父类的方法时,super会绑定子类的this
        this.x = x; 
        this.y = y;
        // this.publicMethod = this.publicMehtod.bind(this);
        // 与上面等效,运行时绑定this到类内部
        this.publicMethod = (arg) => {
            this.publicMethod(arg);
        }
    }
    /* constructor(...args) { this.args = args; } //*/
    // 容许定义一个 Generator 函数
    * generatorFunc() { 
        for (let arg of this.args) {
            yield arg;
        }
    }
    publicMehtod(parm) {

    }
    // class 的全部方法都定义在 prototype 上
    toString() {
        return '(' + this.x + ',' + this.y + ')';
    }
    // 属性名能够用表达式
    [methodName]() { 

    }
    // 内部实际上将 privateMethodName 变成了私有方法
    execPrivateMehod(param) {
        privateMethodName.call(this, param)
    }
    // 私有方法
    [privateMethod](param) {
        return this[privateVariable] = param;
    }
    // 使用set/get
    set prop(value) {
        console.log('setting prop');
    }
    get prop() {
        return 'getter prop';
    }
}
// 类的静态属性,不容许直接写在类内部即 prop: '200' 或 static prop: '200'
Point.prop = '200';
function privateMethodName(name) {
    return this.name = name;
}
const point = new Point(10, 20);
point.prop = 'setting';
point.pop; // getting prop
const descriptor = Object.getOwnPropertyDescriptor(Point.prototype, 'prop');
'set' in descriptor; // true
'get' in descriptor; // true
point.hasOwnProperty('x'); // true
point.hasOwnProperty('toString'); // false
point.__proto__.hasOwnProperty('toString'); // true
point.__proto__.addMethod = function () { // 在原型对象上添加方法
    return 'something';
}
const point1 = new Point(30, 40);
// 共享原型对象
point.__proto__ === point1.__proto__; // true
point.constructor = Point.prototype.constructor; // true
typeof Point; // 'function'
Point === Point.prototype.constructor // true,类自己指向构造函数
Object.keys(Point.prototype); // []
Object.getOwnPropertyNames(Object.prototype); // ['constructor', 'toString']

// 2️⃣ 修改构造函数
class Foo {
    constructor() {
        return Object.create(null); // 返回空对象
    }
}
new Foo() instanceof Foo // false

// 3️⃣ Class 表达式
const ClassDefine = class Cn {
    getClassName() {
        return Cn.name;
    }
};
const classObj = new ClassDefine();
classObj.getClassName(); // Cn;
Cn.name; // ReferenceError: cn is not define

// 4️⃣ 当即执行 Class
const obj = new class {
    constructor(name) {
        this.name = name;
    }
    getName() {
        return this.name;
    }
}('name');
obj.getName(); // 'name'

// 5️⃣ 继承(extends)
class ColorPoint extends Point {
    // 如子类没显式定义constructor函数,自动添加 constructor(...args) { super(...args) }
    constructor() {
        // 只有先调用super以后才可使用this关键字,不然报 ReferenceError
        // 至关于 Point.prototype.constructor.call(this);
        // super() 函数只能用在构造函数中
        // super 当作对象使用时至关于 Point.prototype,注意:只能调取父类的prototype属性或方法(即父类显式定义的),而不能调用其实例方法或属性
        super(); // 调用父类的构造函数,返回父类实例
        super.staticMethod(); // 能够直接调用父类的静态方法
    }
}
ColorPoint.staticMethod(); // 能够直接调用父类的静态方法
const cp = new ColorPoint(); // 输出new.target.name: ColorPoint
// cp 同时是子类和父类的实例
cp instanceof Point; // true
cp instanceof ColorPoint; // true
Object.getPrototypeOf(ColorPoint) === Point; // true

// 6️⃣ 使用 Proxy绑定this
function selfish (target) {
    const cache = new WeakMap();
    const handler = {
        get (target, key) {
            const value = Relect.get(tartget, key);
            if (typeof value !== 'function') {
                return value;
            }
            if (!cache.has(value)) {
                cache.set(value, value.bind(target));
            }
            return cache.get('value');
        }
    };
    const proxy = new Proxy(target, handler);
    reutn proxy;
}复制代码

继承

ES5 的继承,实质是先创造子类的实例对象 this,而后再将父类的方法添加到 this 上面(Parent.call(this)
ES6 的继承,是先创造父类的实例对象 this(super()), 而后再用子类的构造函数修改 thisjavascript

// 1️⃣ 基本用法和注意事项
class Point {
  static hello() {
    console.log('hello, world');
  }
  p() {
    return 2;
  }
}
// 继承同时会继承父类的静态方法
class ColorPoint extends Point {
  // 若是显式写了 constructor,内部必需要有 super(),不然实例化时报: Uncaught ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
  constructor() {
    // 子类没有本身的 this 对象,而是继承 父类的 this 对象,若是不调用 super 方法,子类就得不到 this 对象,在constructor 中使用 this 会报 Uncaught ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
    // super() 这种函数用法只能用在子类 constructor 中
    // 至关于调用父类构造函数,可是返回的是子类的实例,见 3️⃣
    super();
    // 等效于
    // Point.prototype.constructor.call(this);

    // super 做为对象使用,能够在 constructor 和子类的其它方法中使用,至关于调用 Point.prototype.p,见4️⃣
    console.log(super.p()); // 2
  }
  // 若是没有显式定义 constructor,则会隐式添加 constructor(...args){super(...args)}
}
let cp = new ColorPoint();
// ES5 和 ES6 这两种行为一致
cp instanceof ColorPoint; // true
cp instanceof Point; // true

// 2️⃣ 判断继承关系
Object.getPrototypeOf(ColorPoint) === Point; // true

// 3️⃣ 子类的super()中this指向子类
class A {
  constructor() {
    console.log(new.target.name);
  }
}
class B extends B {
  constructor() {
    super();
  }
}
new A; // A
new B; // B

// 4️⃣ supr 当作对象使用时指向父类prototype
class A {
  constructor() {
    this.p = 2;
  }
}
A.prototype.x = 2;
class B extends A {
  get m() {
    // super 指向父类原型对象
    return super.p;
  }
  get x() {
    return super.x;
  }
}
let b = new P();
b.m; // undefined
b.x; // 2

// 5️⃣ 经过suer调用父类的方法时,方法内部的this指向子类
class A {
  constructor() {
    this.x = 1;
  }
  print() {
    // 此处 this 指向子类B
    console.log(this.x);
  }
}
class B extends A {
  constructor() {
    super();
    this.x = 2;
    // 若是经过 super 对属性赋值,这时 super 就是 this
    super.x = 3;
    // 此处super仍是指向 A.prototype,因此打印 undefined
    console.log(super.x); // undefined
    console.log(this.x); // 3
  }
  m() {
    // 至关于 A.prototype.print()
    super.print();
  }
}
let b = new B();
b.m() // 2

// 6️⃣ 在静态方法中使用super,指向父类,在普通方法中super指向父类的prototype
class Parent {
  static myMethod(msg) {
    console.log('static', msg);
  }
  myMethod(msg) {
    console.log('instance', msg);
  }
}
class Child extends Parent {
  static myMethod(msg) {
    super.myMethod(msg);
  }
  myMethod(msg) {
    super.myMethod(msg);
  }
}
Child.myMethod(1); // static 1
var child = new Child();
child.myMethod(2); // instance 2

// 7️⃣ super不能单独使用
class A2 {}
class B2 extends A2 {
  constructor() {
    super();
    // super.valueOf() 返回 B 的实例
    console.log(super.valueof() instanceof B); // true
    // 编译阶段就会报错
    console.log(super); // Uncaught SyntaxError: 'super' keyword unexpected here
  }
}
let b = new B();

// 8️⃣ 对象老是继承其它对象,因此能够用super
var obj = {
  toString() {
    return 'MyObject: ' + super.toString();
  }
}
obj.toString(); // "MyObject: [object Object]"复制代码

类的 prototype 属性和 proto 属性

原生构造函数的继承

  • Boolean()
  • Number()
  • String()
  • Array()
  • RegExp()
  • Date()
  • Function()
  • Error()
  • Object()
    ES5 没法继承原生构造函数
    ES6 容许继承原生构造哈数定义子类

Mixin 模式的实现

相关文章
相关标签/搜索