日常好奇-看看ES6的类如何实现的[一]

本文深入探讨了ES6中类的实现原理,通过对比传统构造函数的方式,详细解析了类的构造、属性定义及继承机制,并提供了示例帮助理解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

为了真正理解ES6中类的概念,来学习类是如何实现的

我们都知道在JS中,函数是“一等公民”,“类”的概念是在ES6中提出的,它好像跟我们自己写的函数构造器一样,但又有好像有些不一样的地方,那么它到底是如何实现的那?为了达到这个目的,我们利用babel来看下它编译后的代码。

不带继承的类

首先我们写一个简单的类,该类没有任何继承,只有一个简单的构造函数和getName函数

class App {
	constructor(name) {
		this.name = name;
	}
	
	getName() {
		return this.name;
	}
}
复制代码

然后babel一下,我们得到以下结果:

"use strict";

var _createClass = function () {
  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);
    }
  }
  return function (Constructor, protoProps, staticProps) {
    if (protoProps) defineProperties(Constructor.prototype, protoProps);
    if (staticProps) defineProperties(Constructor, staticProps);
    return Constructor;
  };
}();

function _classCallCheck(instance, Constructor) {
  if (!(instance instanceof Constructor)) {
    throw new TypeError("Cannot call a class as a function");
  }
}

var App = function () {
  function App(name) {
    _classCallCheck(this, App);

    this.name = name;
  }

  _createClass(App, [{
    key: "getName",
    value: function getName() {
      return name;
    }
  }]);

  return App;
}();
复制代码

东西还挺多,一眼并看不出来什么东西来,我们接下来一点点分析。我们先看最后一个函数:

// 立即执行函数
var App = function () {

  // 构造函数变形成这样
  function App(name) {
	
    // 从这个函数的名字上看,好像是类调用检查,我们暂时先不看这个函数
    _classCallCheck(this, App);

    this.name = name;
  }

  // 调用了一个_createClass函数,应该是在给App附加一些值
  _createClass(App, [{
    key: "getName",
    value: function getName() {
      return name;
    }
  }]);

  // 返回一个名为App的函数
  return App;
}();
复制代码

下面来看_createClass函数,该函数用来定义各个属性值:

// 从返回值看,该函数是一个高阶函数
var _createClass = function () {

  // 为目标值添加多个属性
  function defineProperties(target, props) {
    for (var i = 0; i < props.length; i++) {

      // 开始设定描述符对象
      var descriptor = props[i];

      // 默认不可枚举
      descriptor.enumerable = descriptor.enumerable || false;

      // 默认可配置
      descriptor.configurable = true;

      // 存在value值则默认可写
      if ("value" in descriptor) descriptor.writable = true;

      // 使用Object.defineProperty来设置属性
      Object.defineProperty(target, descriptor.key, descriptor);
    }
  }

  // 函数接收三个参数,分别是:构造函数,原型属性,静态属性
  return function (Constructor, protoProps, staticProps) {

    // 为构造函数prototype添加属性(即为用构造函数生成的实例原型添加属性,可以被实例通过原型链访问到)
    if (protoProps) defineProperties(Constructor.prototype, protoProps);

    // 为构造函数添加属性
    if (staticProps) defineProperties(Constructor, staticProps);
    return Constructor;
  };
}();
复制代码

好像很简单,跟我们平时使用函数实现差别不是很多,就相差了一个描述符的设定过程。最后看一下类调用检查函数_classCallCheck

// 类调用检查,不能像普通函数一样调用,需要使用new关键字
function _classCallCheck(instance, Constructor) {
  if (!(instance instanceof Constructor)) {
    throw new TypeError("Cannot call a class as a function");
  }
}
复制代码

增加了错误处理,当我们调用方式不正确时,抛出错误。

模拟实践

我们简单实现以下没有继承的方式,来加深我们的印象,为了简化不添加错误处理和描述符的设定过程。

var App = function(name) {
  this.name = name;
}

App.prototype.getName = function() {
  return this.name;
}

var app = new App('miniapp');

console.log(app.getName()); // 输出miniapp
复制代码

这个很简单,就是我们平常模拟“类”所使用的方法,js所有对象都是通过原型链的方式“克隆”来的。注意我们这里的App不能叫做类,在js中没有类的概念。它是一个函数构造器,它可以被当做普通函数调用,也可以被当做函数构造器调用,调用函数构造器使用new关键字,函数构造器会克隆它的prototype对象,然后进行一些其他操作,如赋值操作,最后返回一个对象。

下面想一个问题,实现继承我们一般都是利用原型链的方式,像下面这样:

var dog = {
  name: 'goudan'
};

var animal = {
	getName: function() {
    return this.name;
  }
}

// 对象的原型通过`__proto__`暴露出来(tip: 实际中不要这么写)
dog.__proto__ = animal;
console.log(dog.getName()); // 输出goudan
复制代码

我们如何在两个类之间继承那?在ES6中实现很简单

class Animal {
  constructor(name) {
    this.name = name;
  }

  getName() {
    return this.name;
  }
}

class Dog extends Animal{
  constructor(name) {
    super(name);
    this.name = name;
  }
}
复制代码

如果我们自己实现一个要怎么实现,我们先写一个:

var Animal = function(name) {
  this.name = name;
}

Animal.prototype.getName = function() {
  return this.name;
}

var Dog = function(name) {
	Animal.call(this, name);
  this.name = name;
}

Dog.prototype = Animal.prototype;

var dog = new Dog('goudan');
console.log(dog.getName()); // 输出goudan
复制代码

但这种方式总感觉不太好,那么ES6中的的继承是如何实现的?我们下一篇继续一起学习

资源下载链接为: https://pan.quark.cn/s/9648a1f24758 这个HTML文件是个专门设计的网页,适合在告白或纪念日这样的特殊时刻送给女朋友,给她带来惊喜。它通过HTML技术,将普通文字转化为富有情感和创意的表达方式,让数字媒体也能传递深情。HTML(HyperText Markup Language)是构建网页的基础语言,通过标签描述网页结构和内容,让浏览器正确展示页面。在这个特效网页中,开发者可能使用了HTML5的新特性,比如音频、视频、Canvas画布或WebGL图形,来提升视觉效果和交互体验。 原本这个文件可能是基于ASP.NET技术构建的,其扩展名是“.aspx”。ASP.NET是微软开发的个服务器端Web应用程序框架,支持多种编程语言(如C#或VB.NET)来编写动态网页。但为了在本地直接运行,不依赖服务器,开发者将其转换为纯静态的HTML格式,只需浏览器即可打开查看。 在使用这个HTML特效页时,建议使用Internet Explorer(IE)浏览器,因为些老的或特定的网页特效可能只在IE上表现正常,尤其是那些依赖ActiveX控件或IE特有功能的页面。不过,由于IE逐渐被淘汰,现代网页可能不再对其进行优化,因此在其他现代浏览器上运行可能会出现问题。 压缩包内的文件“yangyisen0713-7561403-biaobai(html版本)_1598430618”是经过压缩的HTML文件,可能包含图片、CSS样式表和JavaScript脚本等资源。用户需要先解压,然后在浏览器中打开HTML文件,就能看到预设的告白或纪念日特效。 这个项目展示了HTML作为动态和互动内容载体的强大能力,也提醒我们,尽管技术在进步,但有时复古的方式(如使用IE浏览器)仍能唤起怀旧之情。在准备似的个性化礼物时,掌握基本的HTML和网页制作技巧非常
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值