在 Javascript 中实现 Ruby 改进



Ruby的改进允许你在词法范围内临时"升级"对象。 我正在尝试在javascript中实现类似的想法。 这里有一些工作代码,几乎可以做到我想要的:

function dateExtension() {
  return {
    day: function() { return this.getDate() }
  }
}
function refine(ctor, mixin) {
  return function() {
    var ret = new (Function.prototype.bind.apply(ctor, arguments));
    return Object.assign(ret, mixin);
  }
}
function test() {
  // Cant overwrite native Date function,
  // so have to rename it.
  var XDate = refine(Date, dateExtension());
  var d = new XDate();
  console.log(d.day()); // prints the day of the month to console
}
test();  

我真正想做的是:

function test() {
  var Date = refine(Date, dateExtension());
  var d = new Date();
  console.log(d.day());  // Uncaught TypeError: Bind must be called on a function
}

这个想法是使本地var Date覆盖内置的Date,仅在test()体内。 因此,在test()内,它将获得day()的新方法,但在test() Date之外将不受影响。 这显然是不允许的。

是否有一些解决方法可以使这个想法起作用?

我制作了一个名为chill-patch的小库来做到这一点。它使您能够将函数用作方法,并安全地修补原型。

下面是如何使用它的示例:

const chillPatch = require('chill-patch')
const lastFunc = arr => arr[arr.length - 1]
const array = [1, 2, 3]
// safely add a method to `Array` 
const last = chillPatch(Array, lastFunc, 'last')
// call the new method! 
array[last]() //=> 3 

如果你想自己滚动,整个源代码如下:

'use strict'
const chillPatch = (klass, func, optionalDescription) => {
  const symbol = Symbol(optionalDescription)
  klass.prototype[symbol] = function(){
    const args = Array.prototype.slice.call(arguments)
    args.unshift(this)
    return func.apply(null, args)
  }
  return symbol
};
module.exports = chillPatch

我看到你在评论中说你不想修改原型,但你的理由可能是修改原型很危险。但是,上述方法使用Symbol补丁,这是完全安全的。除非有人对代码库的其他部分进行反射,否则更改将不可见Object.getOwnPropertySymbols()

所以我想出了一些有效的东西,尽管它相当笨拙。

这里有一个小提琴:https://jsfiddle.net/40cty4qa/

这是代码

function lg() { console.log.apply(console, arguments) }
function extend(o, mixin) {
  for (var k in mixin) o.prototype[k] = mixin[k]
}
function unextend(o, mixin) {
  for (var k in mixin) delete o.prototype[k];
}
Function.prototype.refine = function(ctor, mixin) {
  var self = this;
  return function() {
    extend(ctor, mixin);
    var ret = self.apply(this, arguments);
    unextend(ctor, mixin);
    return ret;
  }
}
function dateExtension() {
  return {
    day: function() { return this.getDate() }
  }
}
function refine(ctor, mixin) {
  return function() {
    var ret = new (Function.prototype.bind.apply(ctor, arguments));
    return Object.assign(ret, mixin);
  }
}
function test() {
  var d = new Date();
  lg(d.day());
}
test = test.refine(Date, dateExtension());
test();  // This works, since were inside a refinement
var d = new Date();
lg(d.day());  // This doesnt work, as desired.

最新更新