ホームページ  >  記事  >  ウェブフロントエンド  >  JS でクロージャが悪用される一般的なシナリオは何ですか? (画像とテキストのチュートリアル)

JS でクロージャが悪用される一般的なシナリオは何ですか? (画像とテキストのチュートリアル)

亚连
亚连オリジナル
2018-05-18 16:18:241354ブラウズ

シナリオ 1: 関数参照を使用した SetTimeout 呼び出し

クロージャーの一般的な使用法は、特定の関数が実行される前に実行される関数にパラメーターを提供することです。たとえば、Web 環境では、関数が setTimeout 関数呼び出しの最初のパラメーターとして使用されるのが非常に一般的なアプリケーションです。

setTimeout は、実行される関数 (または JavaScript コードの一部、ただしこれについては説明しません) を最初のパラメーターとして受け取り、次のパラメーターは実行を遅延する時間です。コードの一部を setTimeout 経由で呼び出したい場合は、最初のパラメータとして関数オブジェクトへの参照を渡す必要があります。 2 番目のパラメーターとして遅延するミリ秒数を指定しますが、この関数オブジェクト参照は遅延されるオブジェクトのパラメーターを提供できません。

ただし、内部関数への呼び出しを返す別の関数を呼び出し、その内部関数オブジェクトへの参照を setTimeout 関数に渡すことは可能です。内部関数の実行時に必要なパラメータは、外部関数の呼び出し時に渡されます。 setTimeout は、内部関数の実行時にパラメーターを渡す必要はありません。内部関数は、外部関数の呼び出し時に提供されたパラメーターに引き続きアクセスできるためです:

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

シナリオ 2: 関数をオブジェクトのインスタンス メソッドに関連付けます

このようなシナリオの多くは、将来のある時点で関数を実行できるように、オブジェクトへの関数 A 参照を割り当てる必要があります。その場合、クロージャは、実行される関数への参照を提供するのに非常に役立ちます。実行するまで関数にアクセスできない可能性があるためです。

1 つの例は、特定の DOM 要素の対話に参加するために JavaScript オブジェクトがカプセル化されることです。 doOnClick、doMouseOver、doMouseOut メソッドがあります。そして、DOM 要素上の対応するイベントがトリガーされたときにこれらのメソッドを実行したいと考えています。ただし、DOM 要素に関連付けられた JavaScript オブジェクトはいくつでも作成できますが、個々のインスタンスは、それらをインスタンス化するコードがそれらのオブジェクトに対して何を行うのかわかりません。オブジェクト インスタンスは、どのグローバル変数 (存在する場合) に参照が割り当てられるかわからないため、自分自身を「グローバルに」参照する方法がわかりません。

つまり、問題は、特定の JavaScript オブジェクト インスタンスに関連付けられたイベント ハンドラー関数を実行し、そのオブジェクトのどのメソッドを呼び出すかを知ることです。

次の例では、要素イベント処理を備えたオブジェクト インスタンスの関連関数に対して単純なクロージャを使用します。イベント ハンドラーには、イベント オブジェクトと関連付けられる要素への参照を渡すことによって呼び出すさまざまなオブジェクト インスタンス メソッドが割り当てられます。

/* 
        一个给对象实例关联一个事件处理器的普通方法, 
        返回的内部函数被作为事件的处理器, 
        对象实例被作为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  
        }

DhtmlObject のインスタンスは、関心のある DOM 要素に関連付けることができます。これらの要素が他のコードによってどのように処理され、グローバル名前空間や他の DhtmlObject の競合によって「汚染」されるかを心配する必要はありません。

シナリオ 3: 関連する関数セットのカプセル化

クロージャーは追加のスコープを作成でき、これを使用して関連コードまたは依存コードを結合できます。このようにして、コード干渉の危険を最小限に抑えることができます。関数を使用して文字列を作成し、繰り返しの連結操作 (一連の中間文字列の作成など) を回避するとします。 1 つのアイデアは、配列を使用して文字列の一部を順番に格納し、Array.prototype.join メソッドを使用して結果を出力することです (空の文字列を引数として使用します)。配列は出力バッファの役割を果たしますが、ローカルに定義すると、関数が実行されるたびに配列が再度作成されます。この配列がすべての関数呼び出しに唯一の変数として割り当てられるだけであれば、これは少しやりすぎになります。

1 つの解決策は、配列をグローバル変数に昇格させて、再度作成しなくても再度使用できるようにすることです。しかし、結果は思ったほど単純ではありません。さらに、グローバル変数がバッファ配列を使用する関数に関連付けられている場合、2 番目のグローバル属性 (関数自体もウィンドウ オブジェクトの属性です) が関連付けられます。配列を使用すると、コードの制御性が失われます。他の場所で使用される場合だからです。このコードの作成者は、配列の定義ロジックだけでなく、組み込まれている関数の定義も覚えておく必要がありました。また、関数名がグローバル名前空間内で一意であるかどうかを判断するだけでなく、関数に関連付けられた配列の名前がグローバル名前空間内で一意であるかどうかを判断する必要があるため、コードを他のコードと統合するのが難しくなります。グローバル名前空間。

クロージャーを使用すると、バッファー配列は依存する関数を関連付け (クリーンに含めること)、同時に名前の競合のリスクを回避しながら、バッファー配列のプロパティ名をグローバル空間に割り当てられているかのように維持できます。コード相互作用の干渉。

ここでの 1 つのトリックは、インライン関数式を実行して追加の実行コンテキストを作成し、その関数式が外部コードで使用されるインライン関数を返すようにすることです。バッファ配列は、インラインで実行される関数式のローカル変数として定義されます。これは 1 回だけ呼び出されるため、配列は 1 回だけ作成されます。ただし、配列は、それに依存する関数から常にアクセスでき、再利用できます。

次のコードは、一部変更されていない HTML 文字列を返す関数を作成しますが、これらの変更されていない文字列には、パラメーターとして渡される変数を散在させる必要があります。

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

/* 
         定义一个全局变量: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实现购物车功能步骤详解

以上がJS でクロージャが悪用される一般的なシナリオは何ですか? (画像とテキストのチュートリアル)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。