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.