web-dev-qa-db-ja.com

自己実行匿名関数とプロトタイプ

JavaScriptには、JavaScriptでクラス/名前空間を作成および管理するための、いくつかの明らかに目立つテクニックがあります。

ある手法と他の手法のどちらを使用する必要があるのか​​、知りたいです。一つ選んでこれからも頑張りたいです。

複数のチームで保守および共有されるエンタープライズコードを作成していますが、保守可能なJavaScriptを作成する際のベストプラクティスを知りたいのですが?

私は自己実行匿名機能を好む傾向がありますが、私はこれらの手法に対するコミュニティの投票が何であるかに興味があります。

プロトタイプ:

function obj()
{
}

obj.prototype.test = function() { alert('Hello?'); };
var obj2 = new obj();
obj2.test();

自己完結型匿名関数:

//Self-Executing Anonymous Function 
(function( skillet, $, undefined ) {
    //Private Property
    var isHot = true;

    //Public Property
    skillet.ingredient = "Bacon Strips";

    //Public Method
    skillet.fry = function() {
        var oliveOil;

        addItem( "\t\n Butter \n\t" );
        addItem( oliveOil );
        console.log( "Frying " + skillet.ingredient );
    };

    //Private Method
    function addItem( item ) {
        if ( item !== undefined ) {
            console.log( "Adding " + $.trim(item) );
        }
    }     
}( window.skillet = window.skillet || {}, jQuery ));   
//Public Properties      
console.log( skillet.ingredient ); //Bacon Strips  

//Public Methods 
skillet.fry(); //Adding Butter & Fraying Bacon Strips 

//Adding a Public Property 
skillet.quantity = "12"; console.log( skillet.quantity ); //12   

//Adding New Functionality to the Skillet 
(function( skillet, $, undefined ) {
    //Private Property
    var amountOfGrease = "1 Cup";

    //Public Method
    skillet.toString = function() {
        console.log( skillet.quantity + " " + 
                     skillet.ingredient + " & " + 
                     amountOfGrease + " of Grease" );
        console.log( isHot ? "Hot" : "Cold" );
     };     

}( window.skillet = window.skillet || {}, jQuery ));
//end of skillet definition


try {
    //12 Bacon Strips & 1 Cup of Grease
    skillet.toString(); //Throws Exception 
} catch( e ) {
    console.log( e.message ); //isHot is not defined
}

自己実行の匿名関数はjQueryチームが使用するパターンであることを私は言及する必要があると思います。

更新この質問をしたとき、自分が理解しようとしていることの重要性が本当にわかりませんでした。手元にある実際の問題は、オブジェクトのインスタンスを作成するためにnewを使用するか、コンストラクター/ newキーワードの使用を必要としないパターンを使用するかどうかです。

私の意見では、newキーワードを使用しないパターンを使用する必要があるため、自分の回答を追加しました。

詳細については、私の回答を参照してください。

26
Robotsushi

更新 javascriptについての理解が深まり、質問に適切に対応できると感じています。私はそれは言葉遣いが悪いが、対処する非常に重要なjavascriptトピックだったと思います。

自己実行匿名関数パターンは、関数の外でこれを使用しない場合に新しいキーワードを使用する必要があるパターンではありません。 newの使用は古い手法であるという考えに同意します。代わりに、newの使用を回避するパターンの使用に努めるべきです。

自己実行匿名関数はこの基準を満たしています。

この質問への答えは主観的です。JavaScriptには非常に多くのコーディングスタイルがあるためです。ただし、私の調査と経験に基づいて、APIを定義し、可能な場合は常に新しいAPIを使用しないように、自己実行匿名関数を使用することをお勧めします。

0
Robotsushi

自己実行匿名関数は、外部イベント(つまりwindow.onload)にフックすることなくスクリプトの実行を自動化するために使用されます。

この例では、クラシックモジュールパターンを形成するために使用されます。その主な目的は、グローバル環境に名前空間を導入し、そうでない内部プロパティにencapsulationを提供することです名前空間に「エクスポート」またはアタッチされます。

一方、オブジェクトプロトタイプの変更は、継承の確立(またはネイティブの拡張)に使用されます。このパターンは、一般的なメソッドまたはプロパティを持つ1:nオブジェクトを生成するために使用されます。

preferenceで1つのパターンを選択しないでください。他のパターンは異なるタスクを実行するためです。ネームスペースに関しては、自己実行機能が適切な選択です。

22
sunwukung

これが私が使用し始めたパターンです(昨日までそのバリエーションを使用してきました):

function MyClass() {
    // attributes
    var privateVar = null;

    // function implementations
    function myPublicFunction() {
    }

    function myPrivateFunction() {
    }

    // public declarations
    this.myPublicFunction = myPublicFunction;
}

MyClass.prototype = new ParentClass(); // if required

これに関するいくつかの考え:

  1. あなたは何も得るべきではありません(anonymous)デバッガースタック内のトレースは、すべてが指定されたものとしてトレースします(無名関数はありません)。
  2. それは私が今まで見た中で最もきれいなパターンです
  3. 宣言に実装を結合することなく、公開されたAPIを簡単にグループ化できます(つまり、スクロールすることなく、パブリッククラスインターフェイスを簡単に操作できます)。

私がprototypeを使用するのは、本当に継承を定義するときだけです。

4
Demian Brecht

私はprototypesを使用しています。これは、それらがよりクリーンで、標準の継承パターンに従っているためです。自己呼び出し機能は、ブラウザの開発や、コードがどこで実行されているのかわからない場合に最適ですが、それ以外の場合は単なるノイズです。

例:

var me;

function MyObject () {
    this.name = "Something";
}

MyObject.prototype.speak = function speak () {
    return "Hello, my name is " + this.name;
};

me = new MyObject();
me.name = "Joshua";
alert(me.speak());
3
Josh K

私は自己実行機能を使いますが、少し違いがあります:

MyClass = (function() {
     var methodOne = function () {};
     var methodTwo = function () {};
     var privateProperty = "private";
     var publicProperty = "public";

     return function MyClass() {
         this.methodOne = methodOne;
         this.methodTwo = methodTwo;
         this.publicProperty = publicProperty;
     };
})();

返されたグローバル変数を任意の入力パラメーター(jQueryなど)から分離しているため、このアプローチがはるかにクリーンであることがわかった場合(これを書き込んだ方法は、voidを返し、C#でrefパラメーターを使用することと同じです。ポインターをポインターに渡し、それをC++で再割り当てする)。次に、クラスに追加のメソッドまたはプロパティをアタッチする場合、プロトタイプの継承を使用します(jQueryの$ .extendメソッドの例ですが、独自のextend()をロールするのは簡単です)。

var additionalClassMethods = (function () {
    var additionalMethod = function () { alert('Test Method'); };
    return { additionalMethod: additionalMethod };
})();

$.extend(MyClass.prototype, additionalClassMethods);

var m = new MyClass();
m.additionalMethod(); // Pops out "Test Method"

このようにして、追加されたメソッドと元のメソッドを明確に区別します。

1
Ed James

実例

(function _anonymouswrapper(undefined) {

    var Skillet = {
        constructor: function (options) {
            options && extend(this, options);
            return this; 
        },
        ingredient: "Bacon Strips",
        _isHot: true,
        fry: function fry(oliveOil) {
            this._addItem("\t\n Butter \n\t");
            this._addItem(oliveOil);
            this._addItem(this.ingredient);
            console.log("Frying " + this.ingredient);
        },
        _addItem: function addItem(item) {
            console.log("Adding " + item.toString().trim());
        }
    };

    var skillet = Object.create(Skillet).constructor();

    console.log(skillet.ingredient);
    skillet.fry("olive oil");

    var PrintableSkillet = extend(Object.create(Skillet), {
        constructor: function constructor(options) {
            options && extend(this, options);
            return this;
        },
        _amountOfGrease: "1 Cup",
        quantity: 12,
        toString: function toString() {
            console.log(this.quantity + " " +
                        this.ingredient + " & " +
                        this._amountOfGrease + " of Grease");
            console.log(this._isHot ? "Hot" : "Cold");
        }
    });

    var skillet = Object.create(PrintableSkillet).constructor();

    skillet.toString();

    function extend(target, source) {
        Object.getOwnPropertyNames(source).forEach(function (name) {
            var pd = Object.getOwnPropertyDescriptor(source, name);
            Object.defineProperty(target, name, pd);
        });
        return target;
    }
}());

IIFEを使用して、コードの「モジュールスコープ」をエミュレートできます。その後、通常どおりオブジェクトを使用できます。

クロージャーを使用してプライベート状態を「エミュレート」しないでください。クロージャーを使用すると、メモリのペナルティが大きくなります。

エンタープライズアプリケーションを作成していて、メモリ使用量を1GB未満に維持したい場合は、不必要にクロージャを使用して状態を保存しないでください。

1
Raynos

これが、IIFEMyclass.anotherFunction()で拡張するために、このSelfExecutingFunctionMyclassをどのように使用するかです。

MyClass = (function() {
     var methodOne = function () {};
     var methodTwo = function () {};
     var privateProperty = "private";
     var publicProperty = "public";

    //Returning the anonymous object {} all the methods and properties are reflected in Myclass constructor that you want public those no included became hidden through closure; 
     return {
         methodOne = methodOne;
         methodTwo = methodTwo;
         publicProperty = publicProperty;
     };
})();

@@@@@@@@@@@@@@@@@@@@@@@@
//then define another IIFE SEF to add var function=anothermethod(){};
(function(obj){
return obj.anotherfunction=function(){console.log("Added to Myclass.anotherfunction");};
})(Myclass);

//the last bit : (function(obj){})(Myclass); obj === Myclass obj is an alias to pass the Myclass to IIFE

var myclass = new Myclass();
myclass.anotherfunction(); //"Added to Myclass.anotherfunction"
0
oneLINE