一点javascript元编程&可链接的设置器
a bit of javascript metaprogramming & chainable setters
我需要"可链接"的setter,允许您执行以下操作:
cool_shoes = new Shoes().color('glitter').style('platform')
console.log(cool_shoes.color()) // => 'glitter'
但我已经厌倦了一遍又一遍地写同样的getter和setter代码,也就是说:
function Shoes() { this._color = null; this._style = null; }
Shoes.prototype.constructor = Shoes;
Shoes.prototype.color = function(arg) {
if (arguments.length === 0) {
return this._color; // _slot accessor
} else {
this._color = arg; // _slot setter
return this;
};
};
Shoes.prototype.style = function(arg) {
if (arguments.length === 0) {
return this._style; // _slot accessor
} else {
this._style = arg; // _slot setter
return this;
};
};
我的意思是,这是有效的,但当你应该能够做一些类似于的事情时,这是一个很大的重复
function createGetterSetter(getter, setter) {
return function(arg) {
if (arguments.length === 0) {
return getter();
} else {
setter(arg);
return this;
};
};
};
然后像这样使用:
Shoes.prototype.color = createGetterSetter(
function() { return this._color; },
function(arg) { this._color = arg; });
Shoes.prototype.style = createGetterSetter(
function() { return this._style; },
function(arg) { this._style = arg; });
当然,任何努力工作的javascript向导都知道,它不会工作:当调用getter
或setter
时,this
不会绑定到正确的值。
尽管我尽了最大的努力把.bind(this)
洒在所有合适的地方,但我仍然没有让它发挥作用。这应该相对简单,但我缺少什么?
更新
这里有一些富有想象力和优雅的答案,我仍然使用Bryan Chen的答案,因为之前有一个默契的原因:我的setter和getter需要做的不仅仅是引用对象属性。例如,在我的实际应用程序中,我对createGetterSetter
的调用如下:
Command.prototype.messageType = utils.createGetterSetter(
function() { return messageType(this._payload).toString(); },
function(str) { messageType(this._payload).write(str); });
因此,在我的情况下,简单地在对象中获取或设置槽的解决方案是不起作用的。但是,感谢大家的回答!
正如您所知,问题是this
没有在getter
和setter
上设置,为什么不设置呢?
function createGetterSetter(getter, setter) {
return function(arg) {
if (arguments.length === 0) {
return getter.call(this); // pass this to getter
} else {
setter.call(this, arg); // pass this to setter
return this;
};
};
};
function Shoes() { this._color = null; this._style = null; }
Shoes.prototype.constructor = Shoes;
Shoes.prototype.color = createGetterSetter(
function() { return this._color; },
function(arg) { this._color = arg; });
Shoes.prototype.style = createGetterSetter(
function() { return this._style; },
function(arg) { this._style = arg; });
var cool_shoes = new Shoes().color('glitter').style('platform')
document.write(cool_shoes.color())
接受的答案已经很好了,但我将尝试更进一步。有时getter和setter分别需要0和1个以上的参数,所以我把一些更全面的东西放在一起:
function Chain() {
this._methods = {};
}
Chain.prototype.overload = function(key, method) {
if (typeof key !== 'string') {
throw new TypeError('key must be a string.');
}
if (typeof method !== 'function') {
throw new TypeError('method must be a function.');
}
let attr, len = method.length;
if ((attr = (
this._methods.hasOwnProperty(key) && this._methods[key]
) || {}
) && attr[len]) {
throw new RangeError(`method ${key} of length ${len} already exists.`);
}
attr[len] = method;
if (!this._methods.hasOwnProperty(key)) {
this._methods[key] = attr;
this[key] = function (...args) {
let method = this._methods[key][args.length];
if (typeof method !== 'function') {
throw new ReferenceError(`method ${key} of length ${args.length} does not exist.`);
}
let value = method.apply(this, args);
return (typeof value === 'undefined' ? this : value);
}
}
};
function Shoes() { this._color = null; this._style = null;}
Shoes.prototype = new Chain();
Shoes.prototype.overload('color', function() { return this._color; });
Shoes.prototype.overload('color', function(arg) { this._color = arg; });
Shoes.prototype.overload('style', function() { return this._style; });
Shoes.prototype.overload('style', function(arg) { this._style = arg; });
Shoes.prototype.overload('value', function() {
return { color: this._color, style: this._style };
});
Shoes.prototype.overload('value', function(attr) { return this[attr](); });
Shoes.prototype.overload('value', function(attr, arg) { this[attr](arg); });
var cool_shoes = new Shoes().color('glitter').style('platform');
document.write(JSON.stringify(cool_shoes.value()));
基本上,我们将Chain()
构造为Shoes.prototype
,这样我们就可以重载Shoes()
实例的成员方法。
overload()
函数采用key
和method
,确定方法签名中的参数数量,并将其添加到key
给出的方法名称的可用签名中。如果在指定的函数中已经有一个包含大量参数的签名,overload()
将抛出一个错误。如果调用了具有未声明签名的函数,它也会引发错误。
否则,setter将正确地链式(假设它们不返回任何内容),getter将正确返回它们的值。
更新
根据要求,这里有一个Chain()
的版本,它是作为mixin而不是超类提供的:
function chain() {
let _methods = {};
return function overload(key, method) {
if (typeof key !== 'string') {
throw new TypeError('key must be a string.');
}
if (typeof method !== 'function') {
throw new TypeError('method must be a function.');
}
let attr, len = method.length;
if ((attr = (
_methods.hasOwnProperty(key) && _methods[key]
) || {}
) && attr[len]) {
throw new RangeError(`method ${key} of length ${len} already exists.`);
}
attr[len] = method;
if (!_methods.hasOwnProperty(key)) {
_methods[key] = attr;
this[key] = function (...args) {
let method = _methods[key][args.length];
if (typeof method !== 'function') {
throw new ReferenceError(`method ${key} of length ${args.length} does not exist.`);
}
let value = method.apply(this, args);
return (typeof value === 'undefined' ? this : value);
}
}
};
}
用法:
Shoes.prototype.overload = chain();
这个怎么样?
function ToyClass() { this._c = 0; }
ToyClass.prototype.constructor = ToyClass;
function addFn( CLASS, ident, uncurriedMutation ) {
CLASS.prototype[ident] = function( ...args ) {
uncurriedMutation( this ).apply(this, args )
return this
}
}
addFn(ToyClass, 'increase', function( instance ){
return function(){
(instance._c)++
}
})
const testCase = new ToyClass().increase()
console.log(testCase._c) // will be '1'
链接到repl
edit:简化版。感谢@Patrick Roberts
我参加聚会也迟到了,但我觉得这是个有趣的问题。这里有一个解决方案,可以为对象中的所有属性动态创建可链接的getter和setter。您可以很容易地将其限制为仅为以_
开头的属性生成它们,但为了简单起见,这将执行所有这些操作。
你只需要:
Object.keys(new Shoes()).forEach(field => {
let name = field.slice(1); // Remove underscore
Shoes.prototype[name] = function(val) {
if (val) {
this[field] = val;
return this;
}
return this[field];
};
});
完整的例子。
它在Shoes
原型中创建了一个新函数,用作getter和setter。使用cool_shoes.color("red")
会将_color
属性设置为red
,并且可以通过调用不带参数的函数(cool_shoes.color() === "red"
)来检索。
我曾考虑在不使用新的Proxy
对象显式创建所有getter和setter的情况下创建此功能,但无法实现。也许你会有更多的运气!
- 一点javascript元编程&可链接的设置器
- 这样链接时是否可以设置转换
- 导轨 4.宝石will_paginate.更改在 ajax 请求上设置的分页链接
- 当按下链接以在不同页面上的表单中的下拉列表中设置值时
- 如何通过Javascript启用链接按钮最初设置为Enable=false
- 如何根据两个不同的输入动态设置链接的路径
- 单击链接时设置cookie
- 如何将链接状态设置为在用户访问另一个页面之前保持
- 如何为网页上的链接设置“接受页眉”
- 将DIV ID内的所有链接设置为返回false
- 如何防止链接设置其子项样式
- 将当前链接设置为活动
- 如何在离开页面之前为单击的链接设置动画
- 如何使用else函数将登录链接设置为未登录的用户
- on单击命令链接设置与该链接附加的图像的css
- 为动态生成的链接设置单击动作
- 将js文件中的相对链接设置为来自文件源而不是域
- 如何将超链接设置为变量
- 使用链接设置窗口焦点
- 我怎么能在锚标签添加到html的链接设置与javascript