1

我试图让下面的示例代码工作。
编辑(试图更清楚目标是什么):

我不想让函数中的所有函数和变量都可用obj,因为它们在全局变量中可用。这不适用于浏览器。这适用于 Adob​​e ExtendScript,所以我只能使用 EcmaScript 3 和一些 polyfill。该文件由我提供并包含在文件之前。用户可以参考哪些功能可用,并且可以在和中使用它们。这实际上与P5.js所做的非常相似,但我正试图为 InDesign 实现这一点。我们当然可以调用. 目的是摆脱 ,让用户有可能只调用并获得. setupdrawlib.jsuser-script.jssetupdrawobj.foo()obj.fooobj.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 用于库
  • 函数设置(){}
  • 函数绘制(){}
  • obj.go() 作为一切的执行(以后可能会被删除)

我不能告诉用户在 setup 和 draw 中编写更多附加代码。用户的部分应该减少到可以手写​​,不需要使用样板或类似的东西。

#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()

感谢您的所有回答。我将审查它们并返回报告最终决定是什么。他们似乎都以不同的方式解决问题。我目前无法判断哪个是“正确”答案。那些我们似乎最接近我的问题的人得到了我的支持。

4

11 回答 11

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();

我尝试尽可能多地使用您的调用方式(就结构而言)。

这能解决你的问题吗?

于 2016-07-01T15:04:52.033 回答
1

OP:这里的问题是我们有很多函数需要绘制和设置。因此,一个一个地传递它们并不是解决方案。我们可以将它们作为对象传递,但随后我们又回到了第一格。...我会重新考虑这个想法,也许我们必须寻求全球范围。

只是出于好奇,因为我从来不需要 and 的黑魔法evalwith所以我最终选择了它,证明科学怪人的怪物真的可以通过这些工具创建。就是这样,这个可怜的生物……

……维克多……

// 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.
于 2016-07-05T14:23:15.577 回答
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()
})()

于 2016-07-01T15:45:33.893 回答
1

OP: I don't get the idea of the module system. ...

With a (custom) module system one was going to provide a method to ones users via that they were able to register theirs code bases, encapsulated each into a function itself. Since there was control of the "registering", it was up to oneself of which additional state/behavior should be injected into such function based modules. One just has to make clear which arguments would be passed into those modules by ones system.

OP: ... I will have a look into your before prototype. This looks like it could work for us. ...

It won't. But another more straightforward approach will do. It will misuse the idea of a module system and turn things inside out. The OP's requiremnts are far from being ideal. But a possible solution, refactored from the OP's provided latest example code, that - within the given scenario - does what the OP might look for, could look similar to that ...

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
    };

}());

custom code with "lib.js" included.

// 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`.
于 2016-07-04T16:53:55.950 回答
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();

于 2016-07-01T14:15:39.283 回答
1

正如已经提到的,唯一有助于实现更改/更改封闭函数/方法的控制流的目标的模式(因此,一个人不拥有他们的代码库。)是函数组合。

它也被指出,有不同的实现方法。

OP 的更改示例将使用Function.before. 由于bindJavaScript 已经 具有标准化 的 . Function.prototype_ _ _ beforeafteraroundafterThrowingafterFinally

... 尽可能贴近 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的回答......

答:是的,没错……

...对于这个问题...

问:......我是否理解正确,您将决定是否调用foo派生obj.foobah/取决于分别obj.bah在其中实现的逻辑?fn1fn2

...我以前的方法从 更改Function.beforeFunction.around

然而,我个人对这个解决方案并不满意,因为这两种方法都是全局提供的fn1fn2现在需要提前预测修改,在我看来这不是一个干净的方法。

然而,下一个示例接近 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));
于 2016-07-01T15:25:45.843 回答
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();

工作小提琴

于 2016-07-01T13:56:31.150 回答
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(); 
于 2016-07-01T14:54:52.130 回答
0
  1. 从要导入的库中找出要转换为全局变量的函数的所有名称。
  2. 使它们全球化。如果你想做一些花哨的 AST 解析东西来为你找到它们,我想你可以这样做。或者你可以使用控制台并解决它。尽管如此,您实际上希望它是静态的,因为这有助于您推断生成的代码,如果它是动态生成的,那么如果全局名称发生冲突,那么未来对库的升级可能会破坏早期的代码。

但是代码只需要看起来像这样

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)

这是本案所能给出的最佳答案。我知道这很烦人。上面给出的所有选项都会产生比它们解决的更多的问题。真正问问自己为什么我要按照我的要求去做?

于 2016-10-17T03:44:40.313 回答
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
}

于 2016-07-01T14:47:13.927 回答
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
}
于 2016-07-01T14:12:42.260 回答