【问题标题】:writing a Javascript function outside of scope to fire inside the scope在范围之外编写一个 Javascript 函数以在范围内触发
【发布时间】:2013-06-06 21:28:42
【问题描述】:

我在很多项目中使用 D3 可视化库,发现自己为每个项目复制和粘贴了大量样板代码。例如,大多数项目都是这样开始的:

var margin = {top: 20, right: 10, bottom: 30, left: 60},
    width = 960,
    height = 500;

var svg = d3.select(container_id).append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom);

在这种代码之后,每个项目都会出现分歧。 D3 的部分乐趣在于,您可以为每个新项目进行一些专门的、创造性的编码。

我想为样板代码编写一个轻量级的包装器,这样我每次都可以跳到有趣的部分,这样做我意识到我不太明白如何正确地制作一个复杂的、可重用的 Javascript 对象。这是我开始的:

var d3mill = function() {
    var margin = {top: 20, right: 10, bottom: 30, left: 60},
        width = 960,
        height = 500;

    var svg = d3.select(container_id).append("svg")
       .attr("width", width + margin.left + margin.right)
       .attr("height", height + margin.top + margin.bottom);

    return {
        svg: function() { return svg; },
        call: function(f) { f(); }
    };
};

我想我希望能够做到这一点:

 var d3m = d3mill();
 var test = function() {
     console.log(svg);
 };
 d3.call(test);

我认为(希望)通过call() 传递函数会导致函数在d3mill 实例的闭包内触发,从而定义svg

以上面svg() 函数的方式将闭包中的每个变量都暴露给外界将是巨大的浪费时间。允许外部函数在这里运行的正确方法是什么?

【问题讨论】:

标签: javascript object d3.js closures


【解决方案1】:

如果您将代码更改为:

return {
    svg: function() { return svg; },
    call: function(f) { f.call(this); }
};

那么它应该正确地将test中的上下文设置为d3m

在该函数中,您应该能够访问 this.svg() 以获取 SVG 对象,但您将能够直接访问“私有”词法范围变量 svg,即:

var d3m = d3mill();
var test = function() {
    console.log(this.svg());   // OK
    console.log(svg);          // not OK - undefined variable
};
d3m.call(test);

您也可以在调用时将svg 参数传递给f

return {
    svg: function() { return svg; },
    call: function(f) { return f.call(this, svg); }  // also added "return", just in case
};

有用法:

var d3m = d3mill();
var test = function(svg) {
    console.log(svg);          // now OK - it's a parameter
};
d3m.call(test);

【讨论】:

  • 这是否比调用 d3m.svg() 更进一步?希望我也不需要一个“margin()”函数,并且对于我在样板文件中需要的每个其他有用变量都需要一个函数。但我想这就是闭包的定义......
  • Re:第二个建议,将 svg 作为参数传递——这给了我“未定义”,我认为是因为函数内作用域胜过对象作用域?
  • @ChrisWilson 嗯,这有点奇怪。我没有看到在f.call(this, svg) 中传递的变量被任何具有相同名称的变量的范围所隐藏。
  • 我的错误,在第一部分没有看到 (this, svg)。是的,这行得通!虽然它不允许我在闭包中使用我想要的任何变量...
  • 这和“function call() { f(svg); }”基本一样吧?
【解决方案2】:

您也可以用作构造函数。

var D3Mill = (function() {
    var defaults = {
        margin: { top: 20, right: 10, bottom: 30, left: 60 },
        width: 960,
        height: 500
    };

    function num(i, def) {
        return ("number" === typeof i) ? i : def;
    }

    function D3Mill(container_id, opts) {
        opts = opts || {};
        // Use opts.xxx or default.xxx if no opts provided
        // Expose all values as this.xxx
        var margin = this.margin = (opts.margin || defaults.margin);
        var width  = this.width  = num(opts.width,  defaults.width);
        var height = this.height = num(opts.height, defaults.height);
        this.svg = d3.select(container_id).append("svg")
           .attr("width", width + margin.left + margin.right)
           .attr("height", height + margin.top + margin.bottom);
    }

    D3Mill.prototype.perform = function(f) { return f.call(this); };

    return D3Mill;
}());

var d3m = new D3Mill("my_container_id");
// or
var opts = {
    width: 1,
    height: 1,
    margin: { ... }
};
var d3m = new D3Mill("my_container_id", opts);

var test = function() {
    console.log(this.svg, this.margin, this.width, this.height);
};
d3m.perform(test);

【讨论】:

  • 我真的很喜欢这个解决方案(虽然我从@Alnitak 学到了很多东西!)。
【解决方案3】:

以下内容还可以让您访问您想要在测试中使用的变量。

var d3mill = function() {
    this.margin = {top: 20, right: 10, bottom: 30, left: 60},
        width = 960,
        height = 500;

    this.svg = d3.select(container_id).append("svg")
       .attr("width", width + margin.left + margin.right)
       .attr("height", height + margin.top + margin.bottom);

};


var d3m = new d3mill();
var test = function() {
  console.log(this.svg);
};
test.call(d3m);

【讨论】:

  • 它仍然只能访问this属性,而不是使用var 声明的任何变量。
  • 好的。我应该将其包含在答案中。但是,我认为这是一件好事:-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-11-26
  • 1970-01-01
  • 1970-01-01
  • 2013-12-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多