자체 실행 익명 함수 및 프로토 타입


26

자바 스크립트에는 자바 스크립트에서 클래스 / 네임 스페이스를 만들고 관리하기위한 몇 가지 분명한 기술이 있습니다.

어떤 기술이 다른 기술과 비교하여 어떤 상황이 필요한지 궁금합니다. 하나를 골라 앞으로 나아가고 싶습니다.

여러 팀에서 유지 관리 및 공유되는 엔터프라이즈 코드를 작성하고 유지 관리 가능한 자바 스크립트를 작성할 때 모범 사례가 무엇인지 알고 싶습니다.

나는 자체 실행 익명 기능을 선호하는 경향이 있지만 이러한 기술에 대한 커뮤니티 투표가 궁금합니다.

프로토 타입 :

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
}

Self-Executing Anonymous Function은 jQuery 팀에서 사용하는 패턴이라고 언급해야합니다.

업데이트이 질문을했을 때 나는 내가 이해하려고하는 것이 중요하다는 것을 실제로 보지 못했습니다. 실제 문제는 new를 사용하여 객체의 인스턴스를 만들거나 new키워드 의 생성자 / 사용이 필요없는 패턴을 사용할지 여부 입니다.

내 의견으로는 new키워드를 사용하지 않는 패턴을 사용해야하기 때문에 내 자신의 대답을 추가했습니다 .

자세한 내용은 내 답변을 참조하십시오.


1
설명하는 두 가지 기술에 대한 간단한 예를 들어 줄 수 있습니까?
Hey

간단한 샘플로 인해 프로토 타입을 과소 평가하지 마십시오.
Robotsushi

1
자체적으로 실행되지 않습니다 = /
Hey

2
나는 표현을 닫거나 부르는 괄호가 보이지 않습니다 ...
Hey

1
(+1) 네임 스페이스는 많은 개발자에게 간과됩니다.
umlcat

답변:


22

자체 실행 익명 함수는 외부 이벤트 (예 : window.onload)에 연결하지 않고 스크립트 실행을 자동화하는 데 사용됩니다.

이 예제에서는 기본 모듈 패턴을 형성하는 데 사용되며, 기본 목적은 네임 스페이스를 글로벌 환경에 도입하고 네임 스페이스에 "내보내기"또는 첨부되지 않은 내부 속성에 대한 캡슐화 를 제공 하는 것 입니다.

반면에 객체 프로토 타입 수정은 상속 을 설정 하거나 네이티브를 확장 하는 데 사용됩니다 . 이 패턴은 일반적인 방법이나 속성으로 1 : n 객체를 생성하는 데 사용됩니다.

다른 작업 을 수행하기 때문에 하나의 패턴을 다른 패턴 보다 우선적 으로 선택해서는 안됩니다 . 네임 스페이스 측면에서는 자체 실행 기능이 적절한 선택입니다.


7
"익명 함수 자체 실행"은 일반적으로 즉시 호출되는 함수 표현식 (IIFE) 으로 알려져 있습니다.
voithos

나는 그들을 피하고 정직하기 위해 IIFE와 사랑을 얻지 못합니다. 이클립스에서 코드 개요를 디버그하고 깨뜨리는 것은 지저분합니다. 네임 스페이스가 객체에 붙어 있으면 실행 해야하는 경우 그냥 호출하면 캡슐화로 실제로 아무것도 얻지 못합니다.
다니엘 Sokolowski

4

방금 사용하기 시작한 패턴은 다음과 같습니다 (어제까지 변형을 사용했습니다).

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더 이상 사용하는 유일한 시간은 상속을 정의하는 것입니다.


5
이것에는 몇 가지 문제가 있습니다. 새로운 "함수"마다 생성자마다 새로운 함수 객체가 생성됩니다. 또한 상속을 위해 프로토 타입 객체의 복사본을 얻기 위해 생성자를 호출하는 것은 어리둥절합니다. Object.create (ParentClass.prototype) 또는 Object.create를위한 shim을 사용하십시오.function clone(obj){return this typeof 'clone' ? this : new clone(clone.prototype=obj)}
Hey

@GGG : 그렇습니다. 당신의 첫 포인트가 맞습니다. 구현의 각 특정 사용 사례를 고려해야한다고 말하고 싶습니다. prototype당신이 제안 하는 방법에 대한 나의 문제 는 (내가 익숙하지 않은 방법이 없다면) 어쨌든 당신이 속성을 캡슐화하는 능력을 잃어 버리는 것입니다. 모든 것이 공개적으로 열려 있음을 의미합니다 (세계 끝이 아닙니다) 개인 취향).
데미안 브레히트

또한 jsperf ( jsperf.com/object-create-vs-constructor-vs-object-literal/12 )에서 수행 된 다음 작업을 본 후 거의 매일 추가 사본의 메모리 비용보다 성능을 향상시킵니다 (다시, 특정 사용 사례에 매우 주관적 임).
데미안 브레히트

이제 ECMA-262의 일부만 진행되었으므로 보이지 않는 것들이 많이 있습니다. 또한 Crockford의 말씀을 복음으로 받아들이지 않습니다. 예, 그는 하나입니다. 이 분야의 전문가 중 하나 (물론 가장 중요한 전문가 중 한 명)이지만 항상 항상 100 % 올바른 것은 아닙니다. 설득력있는 주장에 대해 모순되는 의견을 가진 다른 전문가들이 있습니다 (그중 하나가 아닙니다.).
데미안 브레히트

2
내가 일하고 있는 것에 관심 이있을 것입니다 .
Hey

3

프로토 타입 은 더 깨끗하고 표준 상속 패턴을 따르기 때문에 사용 합니다. 자체 호출 기능은 브라우저 개발 또는 코드가 실행되는 위치를 모르는 상황에 적합하지만 그렇지 않으면 소음입니다.

예:

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());

1
자체 실행 익명 함수는 클래스에 개인 함수에 액세스 할 수 있도록하는 데 매우 유용합니다.
Zee

1

자체 실행 기능을 사용하지만 약간의 차이가 있습니다.

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
이런 NFE를 사용하는 것이 나쁜 생각이라고 생각하는 유일한 사람입니까?
Hey

1

라이브 예

(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 미만으로 유지하려는 경우 클로저를 사용하여 상태를 저장하는 것을 불필요하게 피하십시오.


코드 메이트에 오타가 몇 개 있습니다. 클로저에 대한 귀하의 주장이 자동으로 과도한 메모리 사용을 유발하는 것에 대해 긍정적이지 않습니다. 나는 그것이 얼마나 많이 사용하는지와 범위 문제를 얼마나 잘 다루고 있는지에 달려 있다고 생각합니다. 예를 들어 (글로벌 콘솔을 사용하는 것이 좋은 예입니다. 위의 코드는 콘솔에 변수로 전달하면 훨씬 효율적입니다).
Ed James

@ EdWoodcock 나는 코드가 버그가 있다고 생각하고 리팩토링하고 수정했습니다.
Raynos

@EdWoodcock 지역 console과 세계 의 효율성 차이 console는 마이크로 최적화입니다. 그렇게 할 가치가 console있지만 최소화 할 수는 있지만 다른 문제입니다
Raynos

@EdWoodcock 클로저는 객체가 중복되면 느립니다. 느린 것은 함수 내에서 필요하지 않은 함수를 만드는 것입니다. 클로저는 또한 객체에 직접 상태를 저장하는 것과 비교하여 상태를 저장하기위한 메모리 오버 헤드가 적습니다.
Raynos

네, 당신의 대답은 일에 대해 합리적인 방법이기 때문에 지적하고 싶었습니다 (내가 선택한 방식은 아니지만). (나는 편집을했지만 여전히이 특정 SE 사이트에있는 사람들의 에티켓에 대해 확신하지 못합니다).
Ed James

0

업데이트 이제 JavaScript에 대해 훨씬 잘 이해하고 있으며 문제를 올바르게 해결할 수 있다고 생각합니다. 나는 그것이 잘못 표현되었지만 매우 중요한 자바 스크립트 주제라고 생각합니다.

자체 실행 익명 함수 패턴은 함수 외부에서이를 사용하지 않는 경우 새 키워드를 사용해야하는 패턴이 아닙니다. 나는 new를 사용하는 것이 오래된 기술이라는 생각에 동의한다. 대신에 new를 사용하지 않는 패턴을 사용하려고 노력해야한다.

자체 실행 익명 기능은이 기준을 충족합니다.

자바 스크립트에는 코딩 스타일이 너무 많기 때문에이 질문에 대한 대답은 주관적입니다. 그러나 내 연구와 경험을 바탕으로 자체 실행 익명 함수를 사용하여 API를 정의하고 가능할 때마다 새로운 것을 사용하지 않는 것이 좋습니다.


1
새 키워드에 대해 나쁜 점은 무엇입니까? 궁금해.
Ally

0

여기 IIFE SelfExecutingFunctionMyclasswith 를 확장하기 위해 어떻게해야합니까 Myclass.anotherFunction()?

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"

1
프로그래머는 여행 개념적인 질문에 대한 답변을 통해 설명 할 것으로 예상됩니다. 설명 대신 코드 덤프를 던지는 것은 IDE에서 화이트 보드로 코드를 복사하는 것과 같습니다. 친숙해 보이고 때로는 이해할 수 있지만 이상하게 느껴집니다. 화이트 보드에는 컴파일러가 없습니다
gnat
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.