繁体   English   中英

使用IIFE方法并将变量从一个文件传递到另一个文件

[英]Use an IIFE approach and pass a variable from one file to another

我之前的一个问题是如何在多个.js文件之间组织代码。 现在我有一个问题。

我在d3.js中有一张按国家划分的地图。 当用户双击某个国家/地区时,我想将变量传递给另一个js文件。

这是我的html文件index.hbs

<html lang='en'>
    <head>
        <meta charset='utf-8'>
        <script src='https://d3js.org/d3.v5.js' charset='utf-8'></script>
        <script src='https://d3js.org/topojson.v2.min.js'></script>
        <script src='https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js'></script>

        <link href='/css/all.css' rel='stylesheet'/>
    </head>

    <body>
        <div id='map'></div>

        <script> 
            var viewData = {};  
            viewData.nuts0 = JSON.parse('{{json nuts0}}'.replace(/&quot;/g, '"').replace(/&lt;/, ''));
            viewData.CONFIG = JSON.parse('{{json CONFIG}}'.replace(/&quot;/g, '"').replace(/&lt;/, '')); 
        </script>

        <script src='/script/map.js' rel='script'/><{{!}}/script>
        <script src='/script/other.js' rel='script'/><{{!}}/script>
    </body>
</html>

map.js

var NAME=(function map() {

    var my = {};

    var CONFIG = viewData.CONFIG;
    var nuts0 = viewData.nuts0;

    // paths
    var countries;

    // width and height of svg map container
    var width = CONFIG.bubbleMap.width;
    var height = CONFIG.bubbleMap.height;

    // to check if user clicks or double click
    var dblclick_timer = false;

    // create Hammer projection
    var projectionCurrent = d3.geoHammer() 
        .scale(1) 
        .translate([width/2, height/2]); 

    var projectionBase = d3.geoHammer()
        .scale(1) 
        .translate([width/2, height/2]);

    // creates a new geographic path generator with the default settings. If projection is specified, sets the current projection
    var path = d3.geoPath().projection(projectionCurrent);

    // creates the svg element that contains the map
    var map = d3.select('#map');

    var mapSvg = map.append('svg')
        .attr('id', 'map-svg')
        .attr('width', width)
        .attr('height', height);

    var mapSvgGCountry = mapSvg.append('g').attr('id', 'nuts0');

    countries = topojson.feature(nuts0, nuts0.objects.nuts0);
    projectionCurrent.fitSize([width, height], countries);

    var mapSvgGCountryPath = mapSvgGCountry.selectAll('path')
        .data(countries.features)
        .enter()
        .append('path');

    mapSvgGCountryPath.attr('class', 'country')
        .attr('fill', 'tomato')
        .style('stroke', 'white')
        .style('stroke-width', 1) 
        .attr('d', path)
        .attr('id', function(c) {
            return 'country' + c.properties.nuts_id;
        })
        .on('click', clickOrDoubleCountry);

    function clickOrDoubleCountry(d, i) {
        if(dblclick_timer) { // double click
            clearTimeout(dblclick_timer);
            dblclick_timer = false;
            my.countryDoubleClicked = d.country; // <-- variable to pass
        }
        else { // single click
            dblclick_timer = setTimeout(function() {
                dblclick_timer = false;
            }, 250)
        } 
    } 

    return my;

}());

other.js

(function other(NAME) {
    console.log('my:', NAME.my); // undefined
    console.log('my:', NAME.countryDoubleClicked); // undefined
})(NAME);

我希望能够在other.js文件中读取我在map.js中创建的对象,然后能够从other.js访问my.countryDoubleClicked变量。

这段代码不起作用,我得到TypeError: NAME.my is undefined

有几件事情在发生:

揭示变量

首先你不透露出my变量显示为NAME.mymap.js:

var NAME = (function map() {
    var my = {};
    //...
    return my;
}());

NAME.my NAME设置为my ,而不是将NAME.my设置为my 如果你想这样做,你可以这样做:

var NAME = (function map() {
    var my = {};
    //...
    return {
      my: my
    };
}());

您可以从以下文章中了解有关此技术的更多信息,称为“显示模块模式”: http//jargon.js.org/_glossary/REVEALING_MODULE_PATTERN.md

使用函数稍后运行代码

其次,正如其他人所提到的那样,正如您other.js ,由于other.js的代码会立即运行,因此在用户有机会点击某个国家/地区之前,它会运行该代码。 相反,您需要可以按需运行的代码(在这种情况下,当用户双击某些内容时)。 在JavaScript中,传统上通过分配或传递函数来完成。 为简单起见,我们可以为my.doubleClickHandler分配一些my.doubleClickHandler ,然后在clickOrDoubleCountry调用该函数。 为此,除了将它分配给NAME.my.countryDoubleClicked之外,我已经将该国家作为传递给处理程序的参数,但您可能只需要使用其中一个。

function clickOrDoubleCountry(d, i) {
    if(dblclick_timer) { // double click
        clearTimeout(dblclick_timer);
        dblclick_timer = false;
        my.countryDoubleClicked = d.country; // <-- variable to pass
        if (my.doubleClickHandler) {
            my.doubleClickHandler(d.country);
        }
    }
    // ...
} 

然后在other.js ,您将要运行的函数分配给NAME.my.doubleClickHandler

(function other(NAME) {
    NAME.my.doubleClickHandler = function (country) {
        // now this code runs whenever the user double clicks on something
        console.log('exposed variable', NAME.my.countryDoubleClicked); // should be the country
        console.log('argument', country); // should be the same country
    });
})(NAME);

所以除了上面修改的other.js之外,这是完整修改的map.js

var NAME=(function map() {

    var my = {};

    var CONFIG = viewData.CONFIG;
    var nuts0 = viewData.nuts0;

    // paths
    var countries;

    // width and height of svg map container
    var width = CONFIG.bubbleMap.width;
    var height = CONFIG.bubbleMap.height;

    // to check if user clicks or double click
    var dblclick_timer = false;

    // create Hammer projection
    var projectionCurrent = d3.geoHammer() 
        .scale(1) 
        .translate([width/2, height/2]); 

    var projectionBase = d3.geoHammer()
        .scale(1) 
        .translate([width/2, height/2]);

    // creates a new geographic path generator with the default settings. If projection is specified, sets the current projection
    var path = d3.geoPath().projection(projectionCurrent);

    // creates the svg element that contains the map
    var map = d3.select('#map');

    var mapSvg = map.append('svg')
        .attr('id', 'map-svg')
        .attr('width', width)
        .attr('height', height);

    var mapSvgGCountry = mapSvg.append('g').attr('id', 'nuts0');

    countries = topojson.feature(nuts0, nuts0.objects.nuts0);
    projectionCurrent.fitSize([width, height], countries);

    var mapSvgGCountryPath = mapSvgGCountry.selectAll('path')
        .data(countries.features)
        .enter()
        .append('path');

    mapSvgGCountryPath.attr('class', 'country')
        .attr('fill', 'tomato')
        .style('stroke', 'white')
        .style('stroke-width', 1) 
        .attr('d', path)
        .attr('id', function(c) {
            return 'country' + c.properties.nuts_id;
        })
        .on('click', clickOrDoubleCountry);

    function clickOrDoubleCountry(d, i) {
        if(dblclick_timer) { // double click
            clearTimeout(dblclick_timer);
            dblclick_timer = false;
            my.countryDoubleClicked = d.country; // <-- variable to pass
            if (my.doubleClickHandler) {
                my.doubleClickHandler(d.country);
            }
        }
        else { // single click
            dblclick_timer = setTimeout(function() {
                dblclick_timer = false;
            }, 250)
        } 
    } 

    return {
        my: my
    };

}());

如果您不想将NAME.my用于所有内容并希望直接从NAME访问方法和变量(例如NAME.countryDoubleClicked而不是NAME.my.countryDoubleClicked ),则可以使用原始的return语句return my; ,请记住,没有名为NAME.my变量。

您需要设置显式字段...例如:

let x = (function(){
    let obj = {}; // the "namespace"
    let private_var = 0;
    function foo() {
        return private_var++; // Access private vars freely
    };
    obj.foo = foo; // "publish" the function
    console.log(foo()); // you can use unqualified foo here
    return obj;
})();

// outputs 0 from the console log call inside the "constructor"

console.log(x.private_var); // undefined, it's not published
console.log(x.foo()); // outputs 1, the function was published
console.log(x.foo()); // 2
console.log(x.foo()); // 3

Javascript函数的局部变量或局部函数不会在任何地方隐式发布。 如果要访问它们,则需要设置对象字段。

我更喜欢Revealing Module模式上方的Original Module模式 ,主要是因为松散增强的好处; 简而言之,它允许将模块分解为可以异步加载的部分, 在此处阅读更多内容。

通过window.NAME = window.NAME || {} 如果名称为NAME的自定义命名空间尚不存在,则会在下面的代码中声明window.NAME = window.NAME || {} 模块1向其声明变量my ,模块2变量为变量other ; 模块1在模块2之前或之后运行无关紧要。

当主模块执行时(在模块1和2之后),它可以访问两者中定义的变量。 请注意,可以通过3种不同方式访问它们。

 // Module1.js. (function(NAME) { NAME.my = "foo" // Replace with your map instance. })(window.NAME = window.NAME || {}); // Module2.js. (function(namespace) { namespace.other = "bar" })(window.NAME = window.NAME || {}); // Main module using what is defined in the 2 modules above. (function(namespace) { console.log(NAME.my); console.log(namespace.my) console.log(window.NAME.my); console.log(NAME.other); console.log(namespace.other) console.log(window.NAME.other) })(window.NAME = window.NAME || {}); 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM