我试图让下面的示例代码工作。
编辑(试图更明确目标是什么):
我不想在函数obj
和setup
中使用draw
的所有函数和变量作为全局变量。这不适用于浏览器。这适用于Adobe ExtendScript,因此我只能使用EcmaScript 3和一些polyfill。 lib.js
文件由我提供,并包含在之前 user-script.js
文件。用户可以参考哪些功能可用,并可以在setup
和draw
中使用它们。这实际上与P5.js非常相似,但我正试图为InDesign实现这一目标。我们当然可以打电话给obj.foo()
。我们的目的是摆脱obj.
,让用户只需拨打foo
并获得obj.foo
的结果即可。
这是lib.js.它只是图书馆的一小部分,用于说明我手边的内容。
var obj = {
foo: function() {
console.log('foo');
},
bah: function() {
console.log('bah');
},
foobah:function(arg){
return arg*2;
},
CONST:"Hello World",
go:function(){
// looks in the global scope if there is
// a setup and draw function and should patch them.
if(typeof glob.setup === 'function'){
glob.setup();
},
if(typeof glob.draw === 'function'){
glob.draw();
}
}
};
这可以是user-script.js。我们提供的结构是:
我无法告诉用户在设置和绘图中编写更多其他代码。用户的部分应该减少,他可以手工编写,不需要使用样板或类似的东西。
#include lib.js
function setup() {
foo(); // might be called or not
console.log('in setup');
}
function draw() {
bah();// might be called or not
console.log('in draw');
}
obj.go()
感谢您的所有答案。我将对它们进行审核并回过头来报告最终的决定。所有这些似乎都以不同的方式解决了这个问题。我目前无法分辨出哪个是"对"回答。那些我们似乎离我最近的问题得到了我的支持。
答案 0 :(得分:1)
这就像你要求使用闭包和更高阶函数一样,但是obj在fn1和fn2的范围内,所以不需要注入实际函数你可以从每个函数中调用obj.foo。
var obj = {
foo: function() {
console.log('foo');
},
bah: function() {
console.log('bah');
}
};
function higherOrderF(f,name){
return function(){
name && console.log(name)
return f.apply(null,arguments)
}
}
var fn1 = higherOrderF(obj.foo, 'fn1')
var fn2 = higherOrderF(obj.bah, 'fn2')
fn1();
fn2();
答案 1 :(得分:1)
我认为原型是一种可行的方式。所以,我在我的解决方案中使用Function.prototype
。请检查这是否适合您:
var obj = {
foo: function() {
console.log('food');
},
goo: function() {
console.log('good');
}
}
Function.prototype.add = function(fun) {
if (!this.list) {
this.list = [];
}
this.list.push(fun);
}
function a() {
if (a.list) {
a.list.forEach(function(f) {
f();
})
}
console.log('done at a');
}
function b() {
if (b.list) {
b.list.forEach(function(f) {
f();
})
}
console.log('done at b');
}
function main() {
a.add(obj.foo);
b.add(obj.goo);
a();
b();
console.log('done at main');
}
main();
我试图尽可能多地使用(就结构而言)。
这样可以解决您的问题吗?
答案 2 :(得分:1)
正如已经提到的那样,有助于实现改变/改变封闭函数/方法的控制流的目标的唯一模式(因此,一个人不拥有他们的代码库。)是函数组合。
它也得到了指出,它有不同的实现方法。
OP改变的例子将使用Function.before
的原型实现。因为JavaScript已经具有标准化的bind
,所以我坚信
对于其他一些方法修饰符,Function.prototype
也是正确的位置
像before
,
after
,
around
,
afterThrowing
和afterFinally
。
......尽可能贴近OP的例子:
var
obj = {
foo: function() {
console.log('foo');
},
bah: function() {
console.log('bah');
}
};
function fn1() {
//foo();
console.log('fn1');
}
function fn2() {
//bah();
console.log('fn2');
}
function main() {
//fn1.add(obj.foo); // <= Should add function foo to fn1
//fn2.add(obj.bah); // <= Should add function bah to fn2
window.fn1 = window.fn1.before(obj.foo);
window.fn2 = window.fn2.before(obj.bah);
fn1(); // Should output: foo and then fn1
fn2(); // should output: bah and then fn2
//obj.foo = obj.foo.after(f1);
//obj.bah = obj.bah.after(f2);
}
main();
...... Function.prototype.before
的实施:
(function (Function) {
var
isFunction = function (type) {
return (
(typeof type == "function")
&& (typeof type.call == "function")
&& (typeof type.apply == "function")
);
},
getSanitizedTarget = function (target) {
//return (target == null) ? null : target;
return ((target != null) && target) || null;
};
Function.prototype.before = function (handler, target) { // before
target = getSanitizedTarget(target);
var proceed = this ;
return (isFunction(handler) && isFunction(proceed) && function () {
var args = arguments;
//handler.apply(target, args);
handler.call(target, args);
return proceed.apply(target, args);
}) || proceed;
};
}(Function));
考虑到OP的答案......
A: 是的,没错......
...对这个问题......
问: ...我是否理解您是否要决定是否致电
foo
从obj.foo
或bah
/obj.bah
派生出来的决定分别fn1
fn2
?
......我之前的方法从Function.before
更改为Function.around
。
但是,我个人对此解决方案并不满意,因为全球提供的方法fn1
和fn2
现在需要提前预测修改,这在我看来并不干净方法
尽管如此,下一个例子接近OP正在寻找的内容:
var
obj = {
foo: function() {
console.log('foo');
},
bah: function() {
console.log('bah');
}
};
// - both next function's arguments-signatures
// do already anticipate the modification.
// - this is considered to be a dirty approach,
// please check your architecture or your
// concept of control flow.
function fn1(foo, fn1, args) {
foo(); // does not necessarily need to be called.
console.log('fn1');
}
function fn2(bah, fn2, args) {
bah(); // does not necessarily need to be called.
console.log('fn2');
}
function main() {
//fn1.add(obj.foo); // <= Should add function foo to fn1
//fn2.add(obj.bah); // <= Should add function bah to fn2
window.fn1 = obj.foo.around(window.fn1);
window.fn2 = obj.bah.around(window.fn2);
fn1(); // Should output: foo and then fn1
fn2(); // should output: bah and then fn2
}
main();
...... Function.prototype.around
的实施:
(function (Function) {
var
isFunction = function (type) {
return (
(typeof type == "function")
&& (typeof type.call == "function")
&& (typeof type.apply == "function")
);
},
getSanitizedTarget = function (target) {
//return (target == null) ? null : target;
return ((target != null) && target) || null;
};
Function.prototype.around = function (handler, target) { // around
target = getSanitizedTarget(target);
var proceed = this ;
return (isFunction(handler) && isFunction(proceed) && function () {
return handler.call(target, proceed, handler, arguments);
}) || proceed;
};
}(Function));
答案 3 :(得分:1)
with
是一个简单的解决方案
var obj = {
foo: function() {
console.log('foo');
},
bah: function() {
console.log('bah');
},
proto: null
}
//the frowned upon with statement use with caution!!!
with(obj){
function fn1() {
foo();
console.log('fn1');
}
function fn2() {
bah();
console.log('fn2');
}
function main() {
fn1(); // Should output: foo and then fn1
fn2(); // should output: bah and hen fn2
}
}
main()
//Dependency Injection
//es6 can do this a lot cleaner.
;(function(o){
var foo = o.foo,
bah = o.bah;
function fn1() {
foo();
console.log('fn1');
}
function fn2() {
bah();
console.log('fn2');
}
function main() {
fn1(); // Should output: foo and then fn1
fn2(); // should output: bah and hen fn2
}
main()
}(obj))
//es6 Dependency Injection 0 globals
;(({foo,bah} = obj)=>{
function fn1() {
foo();
console.log('fn1');
}
function fn2() {
bah();
console.log('fn2');
}
function main() {
fn1(); // Should output: foo and then fn1
fn2(); // should output: bah and hen fn2
}
main()
})()
&#13;
答案 4 :(得分:1)
OP:我不明白模块系统。 ...
通过(自定义)模块系统,人们可以通过他们能够注册他们的代码库来为用户提供一种方法, 将每个封装成一个函数本身。 由于存在“注册”的控制,因此应该将其他状态/行为注入到这种基于功能的模块中。 只需要弄清楚哪些参数将被一个系统传递给那些模块。
OP:......我将查看你的
before
原型。这看起来对我们有用。 ...
不会。但另一种更直接的方法是可行的。它会滥用模块系统的想法并将内容彻底改变。 OP的要求远非理想。但是,从OP提供的最新示例代码重构的可能解决方案 - 在给定方案中 - OP可能会寻找什么,看起来可能类似......
lib.js
// lib.js
(function () {
// do not unnecessarily pollute the global namespace.
function foo() {
console.log('foo');
}
function bar() {
console.log('bar');
}
function biz() {
console.log('biz');
}
function baz() {
console.log('baz');
}
function foobar(arg){
return (arg * 2);
}
function executeGlobalCustomCode() {
// does look for the existence of both globally
// scoped methods `setup` and `draw` and, if so,
// calls them with the right arguments.
if (typeof global.setup === 'function') {
global.setup(foo, bar);
}
if (typeof global.draw === 'function') {
global.draw(biz, baz);
}
}
var
global = (function () {
return this;
}).call(null),
CONST = "Hello World";
// do only expose into global namespace what is really needed.
global.util = {
go: executeGlobalCustomCode
};
}());
包含“lib.js”的自定义代码。
// custom code with "lib.js" included.
function setup(foo, bar) {
console.log('within SETUP :: call "foo" and "bar".');
foo(); // might be called or not
bar(); //
}
function draw(biz, baz) {
//console.log('within DRAW :: call "biz" and "baz".');
console.log('within DRAW :: call "baz" only.');
//biz(); // might be called or not
baz(); //
}
util.go(); // does trigger execution of `setup` and `draw`.
答案 5 :(得分:1)
OP:这里的问题是我们在绘制和设置中需要有很多功能。 所以一个接一个地传递它们不是解决方案。我们可以将它们作为对象传递但是我们又回到了第一个方向。 ......我将重新考虑这个想法,也许我们必须走向全球范围。
出于好奇,因为我从未需要eval
和with
的黑魔法,我终于走了
为此,证明弗兰肯斯坦的怪物真的可以通过这些工具创造出来。在那里,这个可怜的生物......
...... Victor ......
// lib.js
(function () {
// do not unnecessarily pollute the global namespace.
var toolkit = {
foo : function foo() {
console.log('foo');
},
bar : function bar() {
console.log('bar');
},
biz : function biz() {
console.log('biz');
},
baz : function baz() {
console.log('baz');
}
};
var globallyExecutablesNameList = [
"setup",
"draw"
];
function badlyEvalPatchAndExecuteGlobalCustomCode() {
// does look for the existence of listed globally
// scoped methods and, if so, patches them with a
// bad "eval" approach (using `new Function`) that
// within this eval uses an `with (context)` which
// is considered to be error prone.
var
fct,
str,
args,
body,
result,
regXFctArgsAndBody = (/function[^(]*\(([^)]*)\)\s*\{(.*)\}/);
globallyExecutablesNameList.forEach(function (fctName) {
fct = global[fctName];
if (typeof fct === "function") {
str = fct.toString().replace((/\n/g), "###__NEW__LINE__###");
result = regXFctArgsAndBody.exec(str);
if (result) {
body = [
"with (this) {", // within new method's `this` context ...
// do stuff, that was derived from ...
result[2].split(/###__NEW__LINE__###/).join("\n"),
// ... a (global) custom function's body.
"}" //
].join("");
args = result[1]
.replace((/###__NEW__LINE__###/g), "")
.replace((/\s+/g), "")
.split(",");
args.push(body);
fct = Function.apply(null, args); // frankenstein's monster.
//console.log("args : ", args);
//console.log("fct.toString() : ", fct.toString());
}
fct.call(toolkit); // provide `toolkit` as this function's context.
}
});
}
var
global = (function () {
return this;
}).call(null);
// do only expose into global namespace what is really needed.
global.lib = {
go: badlyEvalPatchAndExecuteGlobalCustomCode
};
}());
......它是受害者......
// custom code with "lib.js" included.
function setup(/*a, b, c*/) {
console.log("SETUP");
foo();
bar();
}
function draw(/*x, y*/) {
console.log("DRAW");
//biz();
baz();
}
lib.go(); // does look for `setup` + `draw` + processes them by 2 dirty techniques.
答案 6 :(得分:0)
我看到了添加到全局范围的问题,这是使用eval
var obj = {
foo: function() {
console.log('foo');
},
bah: function() {
console.log('bah');
}
};
function fn1() {
foo();
console.log('fn1');
}
function fn2() {
bah();
console.log('fn2');
}
function main() {
function myFunc(fn) {
function foo() {
obj.foo();
}
function bah() {
obj.bah();
}
eval("this.exec = " + fn.toString());
}
var myNewFunc1 = new myFunc(fn1);
myNewFunc1.exec(); //foo, fn1
var myNewFunc2 = new myFunc(fn2);
myNewFunc2.exec(); //bah, fn2
}
main();
工作fiddle
答案 7 :(得分:0)
您可以将函数传递给函数。
var obj = {
foo: function() {
console.log('foo');
},
bah: function() {
console.log('bah');
}
};
function fn1( passedFoo ) {
passedFoo();
console.log('fn1');
}
function fn2( passedBah ) {
passedBah();
console.log('fn2');
}
function main() {
fn1(obj.foo); // Should output: foo and then fn1
fn2(obj.bah); // should output: bah and hen fn2
}
答案 8 :(得分:0)
这是猴子修补的解决方案,它实际上是用原来的函数替换原来的函数,它们仍然调用原始函数...所以它就像你已经注入了代码一样。你仍然可以使用更高阶的函数来帮助组成这些猴子补丁。
var obj = {
foo: function() {
console.log('foo');
},
bah: function() {
console.log('bah');
}
};
function fn1() {
//foo();
console.log('fn1');
}
function fn2() {
//bah();
console.log('fn2');
}
main()
function main() {
//monkey patched functions
var originalFn1=fn1
fn1 = function(){
obj.foo()
return originalFn1.apply(this,arguments)
}
var originalFn2=fn2
fn2 = function(){
obj.bah()
return originalFn2.apply(this,arguments)
}
fn1(); // Should output: foo and then fn1
fn2(); // should output: bah and hen fn2
}
&#13;
答案 9 :(得分:0)
我认为对于添加函数,需要这个全局变量。所以这里我没有参数检查第二次调用函数的未定义条件!
function fn1(f) {
if(typeof f != "undefined") this.fn1.add = f;
this.fn1.add();
console.log('fn1');
}
function fn2(f) {
if(typeof f != "undefined") this.fn2.add = f;
this.fn2.add();
console.log('fn2');
}
function main() {
fn1(obj.foo); // <= Should add function foo to fn1
fn2(obj.bah); // <= Should add function bah to fn2
fn1(); // Should output: foo and then fn1
fn2(); // should output: bah and hen fn2
}
main();
答案 10 :(得分:0)
但代码只需看起来像这样
var foo = obj.foo,
bar = obj.bar;
foo()
bar()
或者,您可以确认将所有函数命名为命名空间并不是一个坏主意,并且即使初学者程序员创建他们想要使用的函数的别名也不是很复杂。它有助于他们理解保持代码组织是一个好主意,并帮助他们理解当函数和对象被分配给刚刚引用的新变量名时,不会复制它们。
var map = reallyLongLibraryNameThatNoOneWantsToType.map
//alternatively
var lib = reallyLongLibraryNameThatNoOneWantsToType
lib.reduce([1,2,3],function(a,b){ return a +b }, 0)
这是本案的最佳答案。我知道这很烦人。上面给出的所有选项都会产生更多问题然后解决。真的问自己为什么我想做我所要求的呢?