ES6 中 class 和 extends 实现原理

在学习中,我们通常会遇到这种场景,在阅读某段实现源码时我们往往能看懂大部分代码,但是却卡在其中的一两个点,导致无法继续阅读。 所以在这里我会先列出 class 和 extends 需要的预备知识。

属性描述符

Object.getOwnPropertyDescriptor() 方法获取元素的属性描述,

/*** 知识点:属性描述符* 从 ES5 开始,所有的属性都具备了属性描述符**/
var obj = {name: "jason"
};/*** getOwnPropertyDescriptor* 获取属性描述符*/
var des = Object.getOwnPropertyDescriptor(obj, "name");
console.log(des);/*** {*  value: 'jason',*  writable: true, // 可写*  enumerable: true, // 可枚举*  configurable: true // 可配置* }*/复制代码

Object.defineProperty() 定义对象上属性的一些特性


/*** Object.defineProperty* 该方法定义对象上属性的一些特性** 第一个参数,目标对象* 第二个参数,要添加或编辑的属性* 第三个参数,描述*/
var obj2 = {};
Object.defineProperty(obj2, "age", {value: 2,writable: true,configurable: true,enumerable: true
});
console.log(obj2.age); // 2复制代码

writable:对象上的属性是否可以修改。兼容模式下编辑不可写元素会编辑失败,严格模式下会报错。

var obj3 = {};
Object.defineProperty(obj3, "age", {value: 2,writable: false,configurable: true,enumerable: true
});
obj3.age = 23;
console.log(obj3.age); // 2 不可修改复制代码

configurable:对象上的属性是否可以配置

var obj4 = {};
Object.defineProperty(obj4, "age", {value: 22,writable: true,configurable: false, // 不可配置enumerable: true
});/*** 再次定义* 此时会报错*/
// Object.defineProperty(obj4, "age", {
//   value: 22,
//   writable: true,
//   configurable: false,
//   enumerable: true
// });delete obj4.age;
console.log(obj4.age); // 22 属性删除失败,应为属性不可配置复制代码

enumerable: 当 enumerable 为 false 之后,for...in 不会遍历到该对象


/*** enumerable* 当 enumerable 为 false 之后,for...in 不会遍历到该对象*/
var obj5 = {weight: "66kg"
};
Object.defineProperty(obj5, "height", {value: "176cm",writable: true,configurable: true,enumerable: false // 不可枚举
});
let keys = Object.keys(obj5);
console.log(keys); // [ 'weight' ] 属性 height 不可遍历复制代码

prototype,constructor,__proto__ 三者之间的关系

es6 中类的实现 和 es5 比较

es6 中类的实现


class Parent {constructor(name) {this.name = name;}/*** 静态方法,通过 Parent 调用*/static getMe() {console.log("this is super");}/*** 公共方法,在 prototype 上*/getName() {console.log(this.name);}
}复制代码

es5 实现方式


/*** 立即执行闭包* @param {*} Constructor 构造函数* @param {*} protoProps 原型属性* @param {*} staticProps 静态属性* @return {function}*/
var _createClass = (function() {/*** 为属性添加描述符* @param {object} target 目标对象* @param {Array} props 属性*/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);}}/*** @param {Object} Constructor* @param {array} protoProps 原型属性* @param {array} staticProps 静态属性*/return function(Constructor, protoProps, staticProps) {// 原型上属性, 添加到Constructor 的原型上if (protoProps) defineProperties(Constructor.prototype, protoProps);// 类上的属性, 添加到Constructor 类上if (staticProps) defineProperties(Constructor, staticProps);return Constructor;};
})();/*** 检测函数* es6 中 类不能直接调用* @param {*} instance 实例* @param {*} Constructor 构造函数*/
function _classCallCheck(instance, Constructor) {console.log(instance.__proto__);console.log(Constructor.prototype);// instance 是否是 Constructor 的实例console.log(instance.__proto__ === Constructor.prototype);if (!(instance instanceof Constructor)) {throw new TypeError("Cannot call a class as a function");}
}/*** 立即执行闭包* @return {function}*/
var Parent = (function() {function Parent(name) {// 验证是否是实例化类// es6不允许类直接调用,直接调用抛出异常_classCallCheck(this, Parent);this.name = name;}_createClass(Parent,[{key: "getName",value: function getName() {console.log(this.name);}}],[{key: "getMe",value: function getMe() {console.log("this is super");}}]);return Parent;
})();复制代码

es6 中继承的实现 和 es5 比较

es6 中继承的实现


class Child extends Parent {constructor(name, age) {super(name);this.age = age;}sayHello() {console.log(`hello my age is ${this.age}`);}
}复制代码

es5 继承关键字实现


/*** 继承* @param {*} subClass 子类* @param {*} superClass 父类*/
function _inherits(subClass, superClass) {// 类型检测if (typeof superClass !== "function" && superClass !== null) {throw new TypeError("Super expression must either be null or a function, not " +typeof superClass);}/*** Object.create 接受两个参数* 指定原型创建对象* @param {*} 目标原型* @param {*} 添加属性*/subClass.prototype = Object.create(superClass && superClass.prototype, {constructor: {value: subClass, // subClass.prototype.constructor 指向 subClassenumerable: false, // constructor 不可枚举writable: true,configurable: true}});/*** Object.setPrototypeOf 方法* 设置子类的 __proto__ 属性指向父类* @param {*} 子类* @param {*} 父类*/if (superClass) {// 设置子类的__proto__ 让 Child 能访问父类静态属性Object.setPrototypeOf? Object.setPrototypeOf(subClass, superClass): (subClass.__proto__ = superClass);}
}复制代码

es5 中继承的实现


/*** 调用父类的 Constructor 构造函数*/
function _possibleConstructorReturn(self, call) {if (!self) {throw new ReferenceError("this hasn't been initialised - super() hasn't been called");}return call && (typeof call === "object" || typeof call === "function")? call: self;
}/*** @param {*} _Parent 传入父类*/
var Child = (function(_Parent) {_inherits(Child, _Parent);function Child(name, age) {_classCallCheck(this, Child);/*** 调用父类的 Constructor 构造函数*/var _this = _possibleConstructorReturn(this,(Child.__proto__ || Object.getPrototypeOf(Child)).call(this, name) // 调用父类Constructor);_this.age = age;return _this;}_createClass(Child, [{key: "sayHello",value: function sayHello() {console.log("hello my age is " + this.age);}}]);return Child;
})(Parent);复制代码

完结

以上就是 es6 class 和 extends 的 es5 实现方式,以上代码有一些隐藏细节还需要自学,以下是用到的知识点

  • 属性描述符
  • Object.create 引用原型链需要指定 Constructor 属性
  • new 关键字的实现方式 其中需要设置 __proto__ 属性
  • Object.setPrototypeOf 实现原理 让子类的__proto__ = 父类
  • instanceof 判断 子类的__proto__ === 父类
  • 调用子类的 Constructor 也会调用父类的 Constructor
  • 设置子类的 __proto__ = 父类,让子类可以调用父类静态方法

ES6 中 class 和 extends 的es5实现相关推荐

  1. ES5和ES6中对于继承的实现方法

    在ES5继承的实现非常有趣的,由于没有传统面向对象类的概念,Javascript利用原型链的特性来实现继承,这其中有很多的属性指向和需要注意的地方. 原型链的特点和实现已经在之前的一篇整理说过了,就是 ...

  2. es6中的类及es5类的实现

    目录 类的特点 类的特点 1.类只能通过new得到 在es6中类的使用只能是通过new,如果你将它作为一个函数执行,将会报错. //es6的写法 class Child {constructor() ...

  3. ES5与ES6中如何实现继承

    ES5中实现继承 // 使用类或不使用类继承 // 继承 (2个类连起来) function Animal (lengthNumber) {this.lengthNumber = legsNumber ...

  4. ES6中的class是如何实现的?(附Babel编译的ES5代码详解)

    序言 这篇文章主要讲解面试某大厂遇到的一个问题 - ES6中的class语法的实现? ECMAScript 6 实现了class,class是一个语法糖,使得js的编码更清晰.更人性化.风格更接近面向 ...

  5. ES5和ES6中的变量声明提升

    ES5和ES6中的变量声明提升 Example1: a=2; var a; console.log( a ); //结果为2 Example2: console.log( a ); //结果是unde ...

  6. es5与es6中如何处理不确定参数?以及es6中rest parameter的强大之处

    本人学习过程中编写,定有不足之处,如果有错误,请您积极指正:如果有帮助,请不要吝啬您的赞美(点赞),欢迎各位大佬点赞评论. es5中处理不确定参数(arguments) function sum(){ ...

  7. ES5合并数组---ES6中合并数组

    合同数组的方法, 其中,ES6中扩展运算符提供了数组合并的新写法. formatEs6ConcatArr () {let arr1 = ['a', 'b']let arr2 = ['c', 'c']l ...

  8. ES5和ES6中的this指向

    ES5和ES6中的this指向 ES5: 1.在普通函数中,this指向的是函数调用者,默认情况下,this指向的是window 2.在严格模式下,如果没有直接调用者,在函数中,this的值为unde ...

  9. JavaScript-----静态成员在ES5和ES6中的实现

    在学习静态成员在ES5和ES6中的实现之前我们先来了解一下静态成员的定义.  静态成员 :类的所有对象共享的成员(类的成员变量(属性)和成员方法) 看完这个定义我相信大家还是不太了解静态成员是什么意思 ...

最新文章

  1. linux下yum错误:[Errno 14] problem making ssl connection Trying other mirror.
  2. linux查看文件及文件夹的大小
  3. Maven——常用命令详解
  4. 指针、引用以及const限定符、constexpr限定符
  5. Asp.NetCore之组件写法
  6. 朋友圈设置成昨天发的_如何让你的朋友圈骚到脱颖而出?
  7. js获取DIV的位置坐标的三种方法!
  8. pytorch 实现RBF网络
  9. 操作系统 读者-写者问题
  10. 如何在Cesium中加载谷歌离线影像数据
  11. ubuntu下配置vscode和opencv4要点记录
  12. OPCClient远程连接OPC服务器配置手册
  13. 大规模定制家具实施ERP的必要性
  14. JavaScript jQuery修改样式
  15. Jenkins 添加侧边栏按钮链接 Sidebar Link插件
  16. 第一段代码 打开了新世界的大门
  17. Linux,常用shell命令【删除文件或目录】
  18. python小游戏-移动木板
  19. DOS远程桌面连接命令[佚名]
  20. 饥荒联机版Mod开发——modmain(五)

热门文章

  1. Windows2008管理---第12章 终端服务器
  2. 我在德国做SAP CRM One Order redesign工作的心得 1
  3. hashMap 根据已有知识知道的
  4. 如何让隐藏在大数据背后的价值发挥出来?
  5. ViewPager通过自定义适配器MyPagerAdapter实现界面导航(上标题)
  6. 【Android】Service生命周期回顾
  7. c#学习-base和this在构造函数中的应用
  8. vf省计算机考试题库,计算机二级VF上机题库及答案
  9. 使用FragmentTabHost+Fragment+viewpager 实现滑动分页
  10. 不要相信 errno 可靠