JavaScript中class的本质

156 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第8天,点击查看活动详情 

class

类的数据类型就是函数,类本身就指向构造函数。

typeof Point // "function"
Point === Point.prototype.constructor // true
class Point {
  constructor() {
    // ...
  }
  toString() {
    // ...
  }
  toValue() {
    // ...
  }
}
// 等同于
Point.prototype = {
  constructor() {},
  toString() {},
  toValue() {},
};

new.target

new.target会返回类实例本身。 需要注意的是

  1. new.target 只有通过 new 关键字或者Reflect.construct()创建的实例 new.target才会返回 class本身。 否则返回 undefined

  2. 子类 new.target 返回子类名称。

①利用以上特点可以判断类是否new 实例化的

class Person {
    constructor(props) {
        if (new.target === 'undefined') {
          throw new Error('必须通过new来实例化对象');
        }
      }
}

②创建只能用来继承的类

class Shape {
  constructor() {
    if (new.target === Shape) {
      throw new Error('本类不能实例化');
    }
  }
}

遍历对象

ES6 一共有 5 种方法可以遍历对象的属性。

(1)for…in

for...in循环遍历对象***自身的和继承***的可枚举属性(不含 Symbol 属性)。

(2)Object.keys(obj)

Object.keys返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名。

(3)Object.getOwnPropertyNames(obj)

Object.getOwnPropertyNames返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名。

(4)Object.getOwnPropertySymbols(obj)

Object.getOwnPropertySymbols返回一个数组,包含对象自身的所有 Symbol 属性的键名。

(5)Reflect.ownKeys(obj)

Reflect.ownKeys返回一个数组,包含对象自身的(不含继承的)所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举。

es5和 es6一些区别

1.实例方法都是在原型上的(es5和 es6都一样)

class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
        this.testFunction = this.test
    }
    c = '222'
    
    static __run() {
        console.log('__run')
    }
    
    test(){
        console.log('333')
    }
    toString() {
        return '( x:' + this.x + ', y:' + this.y+ ', c:' + this.c + ')';
    }
}

// super作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。
// super对某个属性赋值,这时super就是this
class Bar extends Point {
    constructor() {
        super();
        // super 
        super.test()
    }
    static classMethod() {
        return super.__run() + ', too';
    }
    draw() {
        super.test()
        // super对某个属性赋值,这时super就是this
        super.x = 3;
        console.log(this.x); // 3
    }
}



// 可以通过实例的__proto__属性为“类”添加方法。
var point = new Point(2, 3);
point.__proto__.functionName = function () {}

上述执行后的实例 img_5.png

静态方法在 new 出来的实例上是不存在的,只能用类名直接调用, 子类静态方法可以通过 super.父类静态方法名 来调用父类静态方法 es6 只有静态方法,没有静态属性,静态属性只能通过 className.prop = value来定义

super()实际上就是调用父类的构造函数

2. 继承的实现

  • es5 中实现继承
const child = new Child()
Parent.apply(child)
  • es6中
class Child extends Parent{
    constructor() {
      super()
    }
}

es6 直接是子类构造方法中调用 super() (实际相当于new Parent() ),因为在子类构造方法,所以直接加载到了子类 this 上了。实际相当于在子类构造方法里面调用了parent.prototype.constructor.call(this) 子类实例创建是基于父类实例,所以必须在super()后才可以用 this 关键字

Object.getPrototypeOf(Child) === Parent 从子类中获取父类实例。 用来判断继承关系

class A {
}
class B extends A {
}
A.__proto__ === Function.prototype // true
A.prototype.__proto__ === Object.prototype // true

B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true
// 子类B的__proto__属性指向父类A,子类B的prototype属性的__proto__属性指向父类A的prototype属性

继承可以实现对原生类的扩展比如 Array。 但是继承 Object 特殊,无法传参给 Object