Home  >  Article  >  Web Front-end  >  What are the common scenarios in which closures can be exploited in JS? (Picture and text tutorial)

What are the common scenarios in which closures can be exploited in JS? (Picture and text tutorial)

亚连
亚连Original
2018-05-18 16:18:241287browse

Scenario 1: Calling setTimeout using a function reference

A common usage of closures is to provide parameters for a function that is executed before a certain function is executed. For example, in a web environment, it is a very common application for a function to be used as the first parameter of the setTimeout function call.

setTimeout takes the function to be executed (or a piece of javascript code, but this is not what we are going to discuss) as its first parameter, and the next parameter is the time to delay execution. If a piece of code wants to be called via setTimeout, then it needs to pass a reference to the function object as the first parameter. The number of milliseconds to delay as the second parameter, but this function object reference cannot provide parameters for the object that will be delayed.

However, you can call another function that returns a call to an internal function, passing a reference to that internal function object to the setTimeout function. The parameters required when the internal function is executed are passed to it when calling the external function. setTimeout does not need to pass parameters when executing the internal function, because the internal function can still access the parameters provided when the external function is called:

function callLater(paramA, paramB, paramC) {  
            /*使用函数表达式创建并放回一个匿名内部函数的引用*/  
            return (function () {  
                /* 
                这个内部函数将被setTimeout函数执行; 
                并且当它被执行时, 
                它能够访问并操作外部函数传递过来的参数 
                */  
                paramA[paramB] = paramC;  
            });  
        }  
        /* 
        调用这个函数将在它的执行上下文中创建,并最终返回内部函数对象的引用 
        传递过来的参数,内部函数在最终被执行时,将使用外部函数的参数 
        返回的引用被赋予了一个变量 
        */  
        var funcRef = callLater(elStyle, "display", "none");  
        /*调用setTimeout函数,传递内部函数的引用作为第一个参数*/  
        hideMenu = setTimeout(funcRef, 500);

Scenario 2: Associating the function to the instance method of the object

There are many Scenario: You need to allocate a reference to a function object in order to execute the function at some time in the future. Then closures can be very helpful to provide a reference to the function that will be executed. Because the function may not be accessible until execution.

One example is that a JavaScript object is encapsulated to participate in the interaction of a specific DOM element. It has doOnClick, doMouseOver and doMouseOut methods. And want to execute these methods when the corresponding event on the DOM element is triggered. However, any number of JavaScript objects may be created associated with DOM elements, and individual instances have no idea what the code that instantiates them will do with them. Object instances do not know how to refer to themselves "globally" because they do not know which global variable (if any) a reference will be assigned to them.

So, the problem is to execute an event handler function associated with a specific JavaScript object instance, and know which method of that object to call.

The next example uses a simple closure on the associated function of an object instance with element event handling. Event handlers are assigned different object instance methods to call by passing the event object and a reference to the element to be associated.

/* 
        一个给对象实例关联一个事件处理器的普通方法, 
        返回的内部函数被作为事件的处理器, 
        对象实例被作为obj参数,对象上将要被调用的方法名称被作为第二个参数 
        */  
        function associateObjWithEvent(obj, methodName) {  
            /*返回的内部函数被用来作为一个DOM元素的事件处理器*/  
            return (function (e) {  
                /* 
                事件对象在DOM标准的浏览器中将被转换为e参数, 
                如果没有传递参数给事件处理内部函数,将统一处理成IE的事件对象 
                */  
                e = e || window.event;  
                /* 
                事件处理器调用obj对象上的以methodName字符串标识的方法 
                并传递两个对象:通用的事件对象,事件处理器被订阅的元素的引用 
                这里this参数能够使用,因为内部函数已经被执行作为事件处理器所在元素的一个方法 
                */  
                return obj[methodName](e, this);  
            });  
        }  
        /* 
        这个构造器函数,通过将元素的ID作为字符串参数传递进来, 
        来创建将自身关联到DOM元素上的对象, 
        对象实例想在对应的元素触发onclick、onmouseover、onmouseout事件时 
        对应的方法被调用。 
        */  
        function DhtmlObject(elementId) {  
            /* 
            调用一个方法来获得一个DOM元素的引用 
            如果没有找到,则为null 
            */  
            var el = getElementWith(elementId);  
            /* 
            因为if语句块,el变量的值在内部进行了类型转换,变成了boolean类型 
            所以当它指向一个对象,结果就为true,如果为null则为false 
            */  
            if (el) {  
                /* 
                为了给元素指定一个事件处理函数,调用了associateObjWithEvent函数, 
                利用它自己(this关键字)作为被调用方法的对象,并且提供方法名称 
                */  
                el.onclick = associateObjWithEvent(this, "doOnClick");  
                el.onmouseover = associateObjWithEvent(this, "doOnMouseOver");  
                el.onmouseout = associateObjWithEvent(this, "doOnMouseOut");  
            }  
        }  
        DhtmlObject.prototype.doOnClick = function (event, element) {  
            //doOnClick body  
        }  
        DhtmlObject.prototype.doMouseOver = function (event, element) {  
            //doMouseOver body  
        }  
        DhtmlObject.prototype.doMouseOut = function (event, element) {  
            //doMouseOut body  
        }

Any instance of DhtmlObject can associate themselves with the DOM elements they are interested in, without having to worry about how these elements will be processed by other code, "polluted" by the global namespace or related to the global namespace. Other instances of DhtmlObject conflict.

Scenario 3: Encapsulating related feature sets

Closures can create additional scopes, which can be used to combine related or dependent code. This way the hazards of code interference can be minimized. Suppose, a function is used to create a string and avoid repeated concatenation operations (such as creating a series of intermediate strings). One idea is to use an array to store parts of the string sequentially, and then use the Array.prototype.join method to output the result (using an empty string as its argument). The array will play the role of the output buffer, but defining it locally will cause it to be created again every time the function is executed. This would be a bit overkill if this array were just assigned as the only variable to every function call.

One solution is to promote the array to a global variable so that it can be used again without having to be created again. But the result is not as simple as you think. In addition, if a global variable is associated with the function that uses the buffer array, there will be a second global attribute (the function itself is also an attribute of the window object) associated with the array, which will make the code lose certain controllability. Because if it is used elsewhere. The creator of this code had to remember the definition of the included function as well as the logic of the array definition. It also makes the code less easy to integrate with other code, because instead of just needing to determine whether the function name is unique in the global namespace, it becomes necessary to determine whether the name of the array associated with the function is unique in the global namespace. .

A closure allows the buffer array to associate (cleanly include) the functions it depends on, while maintaining the buffer array's property names as if they were allocated in the global space, while avoiding name conflicts and code interactions. Danger of interference.

One trick here is to create an additional execution context by executing an inline function expression and have that function expression return an inline function that is used by external code. The buffer array is defined as a local variable in the function expression executed inline. It is only called once, so the array is only created once. But the array is always accessible to functions that depend on it, and can be reused.

The following code creates a function that will return an HTML string, part of which is unchanged, but those unchanged strings need to be interspersed with the variables passed in as parameters.

一个内联执行的函数表达式返回了内部函数对象的一个引用。并且分配了一个全局变量,让它可以被作为一个全局函数来调用。而缓冲数组作为一个局部变量被定义在外部函数表达式中。它没有被扩展到全局命名空间中,并且无论函数什么时候使用它都不需要被再次创建。

/* 
         定义一个全局变量:getImgInPositionedDivHtml 
         被赋予对外部函数表达式一次调用返回的一个内部函数表达式 
         内部函数返回了一个HTML字符串,代表一个绝对定位的DIV 
         包裹这一个IMG元素,而所有的变量值都被作为函数调用的参数 
*/  
        var getImgInPositionedDivHtml = (function () {  
            /* 
            buffAr 数组被定义在外部函数表达式中,作为一个局部变量 
            它只被创建一次。数组的唯一实例对内部函数是可见的, 
            所以它可以被用于每一次的内部函数执行 
            空字符串仅仅被用来作为一个占位符,它将被内部函数的参数代替 
            */  
            var buffAr = [  
                 &#39;<div id="&#39;,  
                &#39;&#39;,   //index 1, DIV ID attribute  
                &#39;" style="position:absolute;top:&#39;,  
                &#39;&#39;,   //index 3, DIV top position  
                &#39;px;left:&#39;,  
                &#39;&#39;,   //index 5, DIV left position  
                &#39;px;width:&#39;,  
                &#39;&#39;,   //index 7, DIV width  
                &#39;px;height:&#39;,  
                &#39;&#39;,   //index 9, DIV height  
                &#39;px;overflow:hidden;\"><img src=\"&#39;,  
                &#39;&#39;,   //index 11, IMG URL  
                &#39;\" width=\"&#39;,  
                &#39;&#39;,   //index 13, IMG width  
                &#39;\" height=\"&#39;,  
                &#39;&#39;,   //index 15, IMG height  
                &#39;\" alt=\"&#39;,  
                &#39;&#39;,   //index 17, IMG alt text  
                &#39;\"><\/div>&#39;  
            ];  
            /* 
            返回一个内部函数对象,他是函数表达式执行返回的结果 
            */  
            return (function (url, id, width, height, top, left, altText) {  
                /* 
                分配各种参数给对应的数组元素 
                */  
                buffAr[1] = id;  
                buffAr[3] = top;  
                buffAr[5] = left;  
                buffAr[13] = (buffAr[7] = width);  
                buffAr[15] = (buffAr[9] = height);  
                buffAr[11] = url;  
                buffAr[17] = altText;  
                /* 
                返回连接每个元素后创建的字符串 
                */  
                return buffAr.join(&#39;&#39;);  
            });  
        })();

如果一个函数依赖另一个或几个函数,但那些其他的函数并不期望与任何其他的代码产生交互。那么这个简单的技巧(使用一个对外公开的函数来扩展那些函数)就可以被用来组织那些函数。

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

JS取得最小公倍数与最大公约数

JS实现数组去重算法

使用JS实现购物车功能步骤详解

The above is the detailed content of What are the common scenarios in which closures can be exploited in JS? (Picture and text tutorial). For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn