乐趣区

关于class:探索-Class-底层原理

ECMAScript6 实现了 class,实际上它是一个语法糖,然而它的呈现能使 JS 编码更清晰,更靠近 面向对象编程

实现原理

首先咱们来看 ES6class 的实现和 ES5 构造函数的实现,两者相比拟不难看出 constructor 其实就是构造方法,指向 ES5 的构造函数,那么 class 自身指向的是构造函数,换言之底层仍旧是构造函数。

ES6

class Person {constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  static run() {console.log("run");
  }
  say() {console.log("hello!");
  }
}

ES5

function Person(name, age) {
  this.name = name;
  this.age = age;
}

Person.prototype.say = function () {console.log("hello!");
};

Person.run = function () {console.log("run");
};

babel 编译剖析

通过 babel 编译器将 ES6 代码 转换成 ES5 代码之后(代码转换能够试用 babel 官网在线工具),可失去这两个要害函数 _defineProperties_createClass,当初咱们来一一解析阐明。

...
var Person = /*#__PURE__*/ (function () {
  "use strict";

  function Person(name, age) {_classCallCheck(this, Person);

    this.name = name;
    this.age = age;
  }

  _createClass(
    Person,
    [
      {
        key: "say",
        value: function say() {console.log("hello!");
        },
      },
    ],
    [
      {
        key: "run",
        value: function run() {console.log("run");
        },
      },
    ]
  );

  return Person;
})();

\_createClass

_createClass 函数次要用于配置构造函数或构造函数原型上的私有函数和静态方法,并返回构造函数自身。

function _createClass(Constructor, protoProps, staticProps) {if (protoProps) _defineProperties(Constructor.prototype, protoProps);
  if (staticProps) _defineProperties(Constructor, staticProps);
  return Constructor;
}

\_defineProperties

_defineProperties 函数次要用于申明私有函数和静态方法的描述符,并将其挂载到以后的构造函数或构造函数原型。它接管两个参数 target()和 props

  • target 指向以后的构造函数或构造函数原型
  • props 数组类型,指向私有函数和静态方法

在遍历数组时,咱们能够看到 enumerable 默认是 false,也就是说 class 类上的外部属性默认是不可枚举的,不能应用 Object.keys 遍历,具体如下:

Object.keys(Person.prototype); // []
Object.keys(Person); // []

同时在遍历的时候还会判断以后描述符是否存在 value 值,如果存在就设置可写属性 writabletrue,反之就应用 getset 属性。在遍历的开端,通过 Object.defineProperty 将描述符配置到以后的构造函数或构造函数原型上,至此就是 class 的根本实现了。

function _defineProperties(target, props) {for (var i = 0; i < props.length; i++) {var descriptor = props[i];
    descriptor.enumerable = descriptor.enumerable || false;
    descriptor.configurable = true;
    if ("value" in descriptor) descriptor.writable = true;
    Object.defineProperty(target, descriptor.key, descriptor);
  }
}

构造函数的区别

暂时性死区

class 不会申明晋升,存在 暂时性死区,构造函数的实质是函数,函数申明会有晋升作用。

// 👌
{const foo = new Foo();
  function Foo() {}
}

// error
{const foo = new Foo(); // Cannot access 'Foo' before initialization
  class Foo {}}

严格模式

class 申明外部默认会启用 严格模式 ,构造函数默认 非严格模式

// 👌
{const foo = new Foo();
  function Foo() {size = 20;}
}

// error
{
  class Foo {constructor() {size = 20; //  size is not defined}
  }
  const foo = new Foo();}

外部办法不可枚举

class 的所有办法(包含 静态方法 实例办法 )都 不可枚举,上文的 _defineProperties 函数办法实现中有提到,具体可参照上文,构造函数可枚举所有办法。

{function Foo() {}

  Foo.print = function () {};
  Foo.prototype.format = function () {};

  console.log(Object.keys(Foo)); //  ["print"]
  console.log(Object.keys(Foo.prototype)); //  ["format"]
}

{
  class Foo {constructor() {}

    static print() {}

    format() {}
  }
  console.log(Object.keys(Foo)); //  []
  console.log(Object.keys(Foo.prototype)); //  []}

原型对象 prototype

class 的所有办法(包含 静态方法 实例办法)都没有原型对象 prototype,因而也没有 [[construct]],不能通过 new 来调用,构造函数则反对 new 调用。

// 👌
{function Foo() {}
  Foo.prototype.format = function () {};

  const foo = new Foo();
  const fooFormat = new foo.format();}

// error
{
  class Foo {static print() {}
    format() {}
  }

  const foo = new Foo();
  const fooFormat = new foo.format(); // foo.format is not a constructor
  const fooPrint = new foo.print(); // foo.print is not a constructor}

new 调用

class 必须应用 new 调用,构造函数的实质是函数,反对间接调用。

// 👌
{function Foo() {}

  const foo = Foo();}

// error
{class Foo {}

  const foo = Foo(); //  Class constructor Foo cannot be invoked without 'new'}

类名重写

class 外部无奈重写类名,构造函数可任意更改。

// 👌
{function Foo() {Foo = "yo";}
  const foo = new Foo();}

// error
{
  class Foo {constructor() {// Foo = 'yo' // TypeError: Assignment to constant variable}
  }

  const foo = new Foo();
  Foo = "yo"; // 👌
}
退出移动版