在Javascript中实现Ruby优化

时间:2016-01-26 15:18:39

标签: 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之外的方法不受影响。这显然是不允许的。

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

2 个答案:

答案 0 :(得分:1)

我做了tiny library called 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()

进行反思,否则更改对代码库的其他部分是不可见的

答案 1 :(得分:0)

所以我想出了一些有用的东西,虽然它很讨厌。

这是一个小提琴: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.