2

私はこのような自己呼び出しの入れ子関数を作成できることを知っています

(function ns(){

    (function Class(){

        alert('ns.Class fired');

    })();

})();​

しかし、それは醜く、完全に正しく見えません。

私のチョップは本来あるべきものではありません。誰かが私の名前空間アプリを構造化するためのより良い方法を教えてくれ、それでも「いくつかの」自己呼び出し関数を利用できることを望んでいます。

// THIS DOESN'T WORK
//     because I haven't called "ns"
function ns(){

    (function Class(){

        alert('fired');

    })();

};​

私の目的のために、これを求めている理由は、jQueryと組み合わせてJSの名前空間を改善するためです。

だから私はこのようなことをしたいと思っています(これは機能します)

var ns = ns || {};
ns.Class = function(){};

ns.Class.Navigation = (function() {

    $('#element').on('click', function() {alert('element clicked');});

})();​

しかし、これがより大きな(jsヘビー)アプリを構築する正しい方法であるかどうかはわかりませんか?!?!

  • これの重さは重すぎませんか?
  • これを達成するためのよりスマートな方法はありますか?
4

4 に答える 4

4

まず最初に、「メイン」IIFEを呼び出すという事実はns、それを名前空間オブジェクトと見なしていることを示唆していますが、これは完全には正しくありません。名前空間は、クロージャスコープの利点が追加されているため、IIFEを使用して作成されることがよくあります。しかし、結局のところ、名前空間はObject(リテラル)の単なる空想の言葉です。
最も人気のあるlib/toolkit /frameworkを取り出してください:jQuery。基本的に、これは1つの巨大なIIFEであり、同じように広大なオブジェクトを構築し、いくつかのメソッドとプロパティが割り当てられます(とにかく関数オブジェクトへの参照)。そのIIFEで作成された他のオブジェクトと変数がいくつかありますが、それらはグローバルオブジェクトにまったく公開されていないか、(非常に)間接的に公開されています。

明確にさせてください:

var myNameSpace = (function()
{
    var invisibleVar = 'can\'t touch this';
    var objectLiteral = {sing: function()
        {
            return invisibleVar;//exposed, albeit indirectly
        },
        property: 'Hammertime'
    };
    var semiExposed;
    objectLiteral.getSemi = function(newVal)
    {
        return semiExposed;//change closure var
    };
    objectLiteral.changeSemi = function(newVal)
    {
        semiExposed = newVal;//change closure var
    };
    objectLiteral.restoreSemi = (function(initVal)
    {
        return function()
        {
            semiExposed = initVal;//restore to value set when main IIFE was executed
            //don't worry about what this references: use the force... of the scope
            return objectLiteral.getSemi();//<-- return init val
        };
    }(semiExposed));//pass initial value to this scope
    var notExposedAtAll = function()
    {//can't be called but inside the main IIFE scope (and subsequent scopes)
        objectLiteral.foo = 'But it adds a public property';
    };
    objectLiteral.changeMe = function()
    {
        notExposedAtAll();//called in default context (either null or global, but it doesn't matter here)
    };
    return objectLiteral;//direct exposure
}());

これは、すべてのツールキット/ライブラリの基本プリンシパルの一部を使用し、実際にはすべての適切なJSスクリプトが共有します。関数をファーストクラスオブジェクトとして使用し、それらを式として使用して一時スコープを作成するなど...)
IMO、IIFEの適切なケースなります:スコープを使用すると、オブジェクトを変数に割り当てるための十分な時間が与えられるため、メソッドの作成方法(IIFEの有無に関係なくthisに関係なく、いつでもどの参照を使用するかを気にする必要はありません。変数。
いくつかの基本的なデータ非表示を実装することもできます。この例では、の初期値がsemiExposedIIFEに渡され、そのスコープ内に保持されます。何もこれを台無しにすることはできません(まあ、それは現時点では完全に真実ではありません)ので、あなたはいつでもできます任意のプロパティの初期値に戻します。

ただし、IIFEを使用すると、コードが大きくなるにつれてコードが読みにくくなる可能性があることを認めます。また、IIFEを使いすぎない理由を完全に理解しています。あなたは調べることができますbind、それはあなたが多くのIIFEを減らすのを助けるでしょう、しかし欠点があります。たとえば、一部のpplはまだIE8を使用していますが、これはをサポートしていませんbind
しかし、他のオプションは次のようになります。単純なIIFEファクトリ関数を作成します。

function giveScope(varsFromScope,toFunction)
{
    return function()
    {
        var passArguments = Array.prototype.slice.apply(arguments,[0]);//get args from call
        passArguments.push({scope:varsFromScope});
        toFunction.apply(this,passArguments);
    };
}
var pseudoClosure = giveScope({scopeContext: this, something:'else'},function(arg1,arg2)
    {
        //function body here
        arguments[arguments.length - 1].currentContext;//<== "closure scope"
        this;//called context
    });

そうすれば、オブジェクトを渡す単純な関数呼び出しに置き換えることで、いくつかのIIFEを取り除くことができます。簡単で、Xブラウザと互換性があります。

最後に、最初のスニペットは、イベントの委任で使用する傾向があるものです。

var target = e.target || e.srcElement;
var parentDiv = (function(targetRef)
{
    while(targetRef.tagName.toLowerCase() !== 'div')
    {
        targetRef = targetRef.parentNode;
    }
    return targetRef;
}(target));

そうすれば、同じスコープで別の検証可能ファイルを作成する必要はありません。divが見つかったときにtargetRefが割り当てられ、targetRefがGCさparentDivれて終了します。したがって、その変数を作成する必要はありません。範囲内にとどまります。

今はかなり遅くなっていて、私があまり意味をなさないかどうかはわかりません。結論は次のとおりです。IIFEは嫌いかもしれませんが、IIFEなしでは実際にはできません。
かっこがたくさんある場合は、かっこを使用する必要がないことを知って喜んでもらえるかもしれませ。JSエンジンに関数宣言を式として解釈させる演算子は、次のようになります。

(function()
{
}());
//can be written as:
!function()
{
}();
//or
~function()
{
}();
//or when assigning the return value, you don't even need anything at all:
var foo = function()
{
    return 'bar';
}();
console.log(foo);//logs bar

おそらくあなたは別の表記法を好みますか?しかし正直なところ、あなたは構文が気に入らないかもしれませんが、私はあなたがそれと一緒に暮らすか、coffeescriptか何かに切り替える必要があるのではないかと心配しています。

于 2012-12-04T17:04:17.520 に答える
1

あなたが何を目指しているのか、私にはよくわかりません。あなたはこのようなものを探していますか?:

var ns = ns || {};
ns.Class = {
    Navigation: (function() {
        return $('#element').on('click', function() {alert('element clicked');});
    })()
};

ここにハンドラーがアタッチされておりns.Class.Navigation、のjQueryラッパーへの参照#elementです。その参照が必要ない場合は、すぐに呼び出される関数式を呼び出した結果(それ以外の場合は空)に何かを割り当てるのはなぜですか? ns.Class関数ではありませんが、実際に関数として使用しているようには見えませんでした。

これはあなたが探していることをしますか?

于 2012-12-04T16:47:45.770 に答える
0
$(function ns() {
    "use strict";
    (function Class(){
        alert('ns.Class fired');
    }());
});

nsはjqueryオブジェクトに渡され、呼び出しは関数parens内で発生します-crockfordテスト(jslint)に合格します。

于 2012-12-04T16:36:38.030 に答える
0
const outerName = (function innerFunc() {
  // Private stuff executed once on creation.
  const createdOnce = document.createElement('textarea');

  return function returnedFunc(someArg1, someArg2) {
    console.log('Hi!');
  };
}());
于 2017-07-25T10:54:34.793 に答える