js- 不用 extends 实现扩展原生对象

本贴最后更新于 365 天前,其中的信息可能已经时移俗易

Why not Extends?

虽然方法比较简陋,但是效果是不错的,理论性能也在我知识范围里最大优化了。至于你问我为什么不用 extends,我只能说

  1. 我喜欢 function 作为构造函数
  2. class 兼容性有点拉

什么破原型链

最近在做在线二进制编辑器,快做完了(80%)感兴趣可以蹲一波;然后遇到了 dataview 和历史记录的孽缘,剪不断理还乱——我必须扩展 dataview 来让 setUint8 这类的函数推送历史记录来正常使用 ctrl+z 功能。正常写法是直接在历史类里绑定 dataview 然后修改 dataview 的 setUint8 函数,但是有 2 个问题-

  1. 当二进制文件长度变化,dataview 也要随之变化,你在历史里绑定的 dataview 指针就丢了,没法找到更改后的 dataview。再更改一遍也行,但你看第二个问题
  2. 绑定一个 dataview 就要把所有 set 相关的 function 重写一遍,全部写进了 dataview 的对象里,不仅控制台里看着心痒而且每次绑定都要写 10 来个函数的话理论性能也不过关。(高标准严要求 确信)

最好的想法应该是有一个 prototype 代劳,把修改后的方法放在一个 prototype 里,然后每次 dataview 更新直接连接到这个 prototype 上就不用浪费太多内存在绑定函数上了。想一想,用 extends 的思路怎么做?无非就是

 class MyDataView extends DataView{
  constructor(buffer) {
    super(buffer);
  };
  setUint8(i,v) {};
  setInt8(i,v) {};
} 

打开控制台 new 一个看看

图片.png

看到继承链是 this -> MyDataView -> DataView

这真的能不用 extends 吗

不用 extends 的话是不是就要考虑 DataView.prototype.constructor.call 了?可惜作为高贵的原生对象,并不能用对象来作为这个 this 值,否则 TypeError: calling a builtin DataView constructor without new is forbidden

不用 callextends 的话想要实现这样奇奇怪怪的继承链,而且还要求返回值是个 DataView (用来传给 DataView 原型上方法的 this),显然学习成本是巨大而多余的,更何况这样的需求在百度很难找到第二个,因此我把原型链改良一下:this -> MyDataView ,在 this 中创建一个 DataView,在 setUint8 时执行完自己的代码直接把 this 里的 DataView 传给 DataView.prototype.setUint8.call(dataview, ...arguments)

你可以理解为 DataView 的实例 new DataView(buffer) 是王子,住在王宫(DataView.prototype)里,可以自由进出王宫。但你只是个杂鱼百姓,想要进入王宫拿点米 DataView.prototype.getUint8 吃。你可以抓一个人伪装成王子 var dataview = new DataView(buffer) 潜入王宫 DataView.prototype.setUint8.call(dataview, ...arguments),然后就能正常拿到你的结果。

真有这么简单我就信了

论性能而说,这些函数都绑定在 prototype 上,不存在一堆 匿名函数声明 乱挤内存,所以理论性能应该是足够优化了(对 js 内存知识储量到此为止了,有错误请提出)。所以实际代码写法如下

function HistoryDataView(buffer, history) {

  this.buffer = buffer;
  this.history = history;
  this.dataview = new DataView(buffer);

}

["Uint8", "Int8", "Uint16", "Int16", "Uint32", "Int32", "BigInt64", "BigUint64", "Float32", "Float64"].forEach((type)=> 
HistoryDataView.prototype["set"+type] = function() {

  var [i, v, l] = arguments;
  this.history.push(...); // 这里写你自己的代码
  DataView.prototype["set"+type].call(this.dataview, i, v, l);

});

new 一个看看效果

图片.png

正常运行。

这么厉害,那我试试另一个

同理啊,可以试一试扩展一个自己的 Date

function MyDate() {

  this.date = new Date(...arguments);

}
MyDate.prototype.toChineseDay = function () {

  return ["日", "一", "二", "三", "四", "五", "六"][this.date.getDay()]

}

图片.png

看看表,今天是不是星期四?

  • JavaScript

    JavaScript 一种动态类型、弱类型、基于原型的直译式脚本语言,内置支持类型。它的解释器被称为 JavaScript 引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在 HTML 网页上使用,用来给 HTML 网页增加动态功能。

    710 引用 • 1173 回帖 • 171 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • bylx
    作者

    D 佬给个原创 100 鼓励鼓励我 😄