ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • ν•¨μˆ˜.prototype.bind(κ°’) vs ()⇒{ν•¨μˆ˜(κ°’)} (+this와 ν™”μ‚΄ν‘œν•¨μˆ˜)
    πŸ‹ JavaScript 2023. 2. 20. 15:52

    πŸ‹ 곡뢀 λ°°κ²½


    λ‹€λ₯Έ λΆ„μ˜ λ¦¬μ•‘νŠΈ μ½”λ“œλ₯Ό 읽던 쀑, μƒνƒœκ°’κ³Ό μ„Έν„°ν•¨μˆ˜λ₯Ό ν”„λ‘­μœΌλ‘œ λ°›μ•„μ˜€λŠ” μ½”λ“œλ₯Ό μ§ λ‹€κ³  ν–ˆμ„ λ•Œ, ν™”μ‚΄ν‘œ ν•¨μˆ˜λ‘œ 값을 바인딩 μ‹œν‚€λŠ” (2)번 μ½”λ“œμ™€ λ™μΌν•˜κ²Œ μž‘λ™ν•˜λŠ” (1) 번 μ½”λ“œλ₯Ό λ°œκ²¬ν–ˆλ‹€.

    function μ»΄ν¬λ„ŒνŠΈ({name, setName}){
    	return <button onclick={setName.bind(null, "솑강")}></button>
    } // -- (1)
    
    function μ»΄ν¬λ„ŒνŠΈ({name, setName}){
    	return <button onclick={()=>{setName("솑강")}}></button>
    } // --- (2)
    

    μŠ€νƒμ˜€λ²„ν”Œλ‘œμš° μ§ˆλ¬Έλ“€μ„ λ³΄λ‹ˆ κ½€λ‚˜ 6-7년도 전에 ν”ν•˜κ²Œ μ“°λ˜ μ½”λ“œμ˜€κ³  ν™”μ‚΄ν‘œ ν•¨μˆ˜κ°€ λ“±μž₯ν•œ μ§€κΈˆ, 빈번히 μ‚¬μš©λ˜μ§€ μ•ŠλŠ” 것 κ°™μ§€λ§Œ…

    두 μ½”λ“œκ°€ λŒ€μ²΄ μ™œ λ™μΌν•œ 것인지 μ΄ν•΄ν•˜λŠ” κ³Όμ •μ—μ„œ 쑰각쑰각 ν©μ–΄μ Έμžˆλ˜ this-bind κ°œλ…λ“€μ΄ ν•œλ°λ‘œ λͺ¨μ΄λŠ” κ²½ν—˜μ„ ν–ˆλ‹€.

    this, ν™”μ‚΄ν‘œ ν•¨μˆ˜, bind(call, apply)에 λŒ€ν•œ κ°œλ…μ΄ λͺ…ν™•ν•˜μ§€ μ•Šλ‹€λ©΄ ν•˜λ‹¨ ‘πŸ‹ this λž€?’ μ„Ήμ…˜λΆ€ν„° 읽어보길 λ°”λž€λ‹€.

     

     

    πŸ‹ μ½”λ“œ μ„€λͺ…


    onClick μ΄λ²€νŠΈμ— μ „λ‹¬ν•˜λŠ” 이벀트 ν•Έλ“€λŸ¬ ν•¨μˆ˜λŠ” 일반 ν•¨μˆ˜λ‘œμ¨ this κ°’μ—λŠ” window 객체가 ν• λ‹Ήλœλ‹€.

    <button
          onClick={function () {
            console.log(this); // Window 객체
          }}
        ></button>
    

    useState (ꡬ쑰 μ°Έκ³ ) μ—­μ‹œ μƒνƒœκ°’κ³Ό 일반 μ„Έν„° ν•¨μˆ˜λ₯Ό λ°˜ν™˜ν•˜λŠ” μ½”λ“œκ°€ λ‚΄λΆ€μ μœΌλ‘œ κ΅¬ν˜„λ˜μ–΄μžˆκΈ°μ— thisκ°’μœΌλ‘œ window 객체가 바인딩 된 μƒνƒœμΌ 것이닀.

    ν•˜μ§€λ§Œ... 계속 풀리지 μ•ŠλŠ” 의문점…

    λ©”μ„œλ“œμ— λ‹€λ₯Έ 객체λ₯Ό κ³ μ •μ‹œμΌœμ£Όκ³  싢을 λ•Œ bindλ₯Ό μ‚¬μš©ν•˜κ³  μΌλ°˜ν•¨μˆ˜λŠ” 엄격λͺ¨λ“œμΌ λ•Œ undefined둜 μ•„λ¬΄λŸ° 객체가 바인딩 λ˜μ§€ μ•Šμ„ 텐데, μ™œ ν•˜ν•„ null을 바인딩 μ‹œμΌœμ£ΌλŠ” 걸까? 

    그에 λŒ€ν•œ 닡변은 “κΌ­ null일 ν•„μš”κ°€ μ—†λ‹€”이닀.

    var fn = function (a, b) {
      console.log(a, b);
    };
    
    var f1 = fn.bind(null, 'x', 'y');
    var f2 = fn.bind({random:"랜덀 객체"}, 'x', 'y');
    
    f1(); // x y
    f2(); // x y
    

    μœ„ 예제처럼 thisλ₯Ό λ‚΄λΆ€μ μœΌλ‘œ μ‚¬μš©ν•˜μ§€ μ•ŠλŠ” ν•¨μˆ˜μ˜ 경우 μ–΄λ– ν•œ 객체λ₯Ό 바인딩 μ‹œμΌœμ£Όλ˜ λ™μΌν•œ κ²°κ³Όλ₯Ό μ–»κ²Œλœλ‹€.

    이 μ½”λ“œμ—μ„œ bindκ°€ μ‚¬μš©λœ μ΄ˆμ μ€ “μƒˆλ‘œμš΄ ν•¨μˆ˜λ₯Ό λ°˜ν™˜”ν•˜κ³ , “항상 같은 인자λ₯Ό λ„˜κ²¨μ£Όλ„λ‘”ν•˜λ„λ‘ ν•˜λŠ” 데에 μžˆλ‹€.

    이 λ§₯λ½μ—μ„œ λ³΄μ•˜μ„ λ•Œ, 항상 같은 κ²°κ³Ό, 즉 setName μ„Έν„° ν•¨μˆ˜κ°€ name μƒνƒœκ°’μ„ “솑강”으둜 μ—…λ°μ΄νŠΈμ‹œμΌœμ£ΌλŠ” κ²°κ³Όλ₯Ό μ‹€ν–‰ν•΄μ£Όλ©΄ λ˜λŠ” μ½”λ“œλ‘œμ„œ (1), (2)κ°€ μ‚¬μš©λ˜μ—ˆλ‹€κ³  보여진닀.

     

     

    πŸ‹ ν—ˆλ¬΄ν•˜μ§€λ§Œ κ·Έλž˜λ„ ν•œ 쀄 의견


    뒀에 μ΄μ–΄μ§ˆ λ‚΄μš©λ“€μ„ κ³΅λΆ€ν•œ ν›„, μ΄ν•΄ν•œ μ½”λ“œλ₯Ό λ°”νƒ•μœΌλ‘œ 생긴 ν•œ 쀄 의견으둜,

    (1)λ²ˆλ³΄λ‹€ (2)번이 더 쒋은 μ½”λ“œμΈ 것 κ°™λ‹€.

    μœ„ μ½”λ“œμ—μ„  λ‹¨μˆœνžˆ “HOC ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•΄ 이후 μΈμžλ“€μ„ λ„˜κΈ°κΈ° μœ„ν•œ μš©λ„”둜 μ‚¬μš©λ˜μ—ˆλ‹€.

    ν•˜μ§€λ§Œ bindλ₯Ό μ‚¬μš©ν•  λ•Œ κ°œλ°œμžλ“€μ΄ κΈ°λŒ€ν•˜λŠ” 뢀뢄은 λ°”λ‘œ, 객체가 “λ©”μ„œλ“œ”λ‘œμ„œ λ°”μΈλ”©λ˜μ–΄μ•Ό ν•˜λŠ” 경우라고 μƒκ°ν•˜κΈ° λ•Œλ¬Έμ΄λ‹€.

     

     

    πŸ‹ this λž€?


    객체에 μ €μž₯된 정보λ₯Ό μ ‘κ·Όν•  수 있게 ν•΄μ£ΌλŠ” 닀리라고 μƒκ°ν•œλ‹€.

    thisλŠ” λŸ°νƒ€μž„μ— κ²°μ •λ˜κ³  μ‹€ν–‰λ˜λŠ” μ½˜ν…μŠ€νŠΈμ— 따라 ν• λ‹Ήλ˜λŠ” 값이 달라진닀.

    λ”°λΌμ„œ λ™μž‘ 방식을 잘 이해해야 μ‹€μˆ˜λ₯Ό ν”Όν•˜κ³  효과λ₯Ό 쏙쏙 뽑아 μ‚¬μš©ν•  수 μžˆλ‹€.

    μ•„λž˜ 예제처럼 λΉ„ 엄격 λͺ¨λ“œμ—μ„œλŠ” μ „μ—­ μŠ€μ½”ν”„μ˜ main ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•œ κ°μ²΄λŠ” window 객체가 ν• λ‹Ήλ˜κ³ , 엄격 λͺ¨λ“œμ—μ„œλŠ” undefined이 ν• λ‹Ήλœλ‹€.

    main() ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•œ this값은, 이 λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•œ 객체인 ‘μš”λ‹ˆ’κ°€ λœλ‹€.

    이렇듯 ν•¨μˆ˜λ₯Ό μ§μ ‘μ μœΌλ‘œ ν˜ΈμΆœν•œ 객체가 λ°”λ‘œ this이고, μŠ€μ½”ν”„μ— 따라 λ°”μΈλ”©λ˜λŠ” thisκ°€ λ‹¬λΌμ§ˆ 수 있음. (이둜 인해 λ°œμƒν•  λ¬Έμ œλŠ” λ’€μ—μ„œ μ–ΈκΈ‰ν•©λ‹ˆλ‹€).

    function main(){
    	console.log(this); // thisμ—λŠ” μ•„λž˜ mainν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•œ 객체인 window 객체가 λ‹΄κ²¨μžˆμŒ
    }
    
    main(); // window.main()와 동일.
    
    const μš”λ‹ˆ = {
    	name: "μš”λ‹ˆ",
    	main: function(){
    		console.log(this); // thisμ—λŠ” ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•œ "μš”λ‹ˆ" 객체가 λ‹΄κΉ€
    	}
    }
    
    μš”λ‹ˆ.main();
    
    const λΉ…μš”λ‹ˆ = {
    	name: "λΉ…μš”λ‹ˆ",
    	smallYeonny: { // 였브젝트(λΉ…μš”λ‹ˆ) μ•ˆμ— 였브젝트(슀λͺ°μš”λ‹ˆ)κ°€ μžˆλŠ” 경우 
    		name: "슀λͺ°μš”λ‹ˆ",
    		function main(){
    			console.log(this); // thisμ—λŠ” μ§μ ‘μ μœΌλ‘œ ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•œ "smallYeonny" 객체가 λ‹΄κΉ€
    		}
    	}
    }
    
    λΉ…μš”λ‹ˆ.smallYeonny.main();
    

    μ•„λž˜μ²˜λŸΌ μ΄λ²€νŠΈλ¦¬μŠ€λ„ˆ ν•¨μˆ˜μ—μ„œλ„ this κ°œλ…μ΄ μž¬λ“±μž₯ν•©λ‹ˆλ‹€.

    const button = document.getElementById("btn");
    
    button.addEventListener("click", function(event){
    	console.log(event.target === this); // μš” 이벀트 ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•œ 객체 button이 좜λ ₯λ©λ‹ˆλ‹€ 
    })
    

     

    πŸ‹ this의 문제점?

    닀이내믹 this 바인딩 이슈 JS의 thisλŠ” μ•„λž˜ 상황에 따라 λ‹€λ₯Έ 값이 바인딩될 수 μžˆμŠ΅λ‹ˆλ‹€.

    1. μ „μ—­ κ³΅κ°„μ˜ this: μ „μ—­ 객체
    2. ν˜ΈμΆœν•œ μΈμŠ€ν„΄μŠ€μ˜ λ©”μ„œλ“œ λ‚΄λΆ€μ˜ this: λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•œ 객체
    3. ν˜ΈμΆœν•œ 일반 ν•¨μˆ˜ λ‚΄λΆ€μ˜ this: μ§€μ •λ˜μ§€ μ•ŠμŒ → μ§€μ •λ˜μ§€ μ•ŠλŠ” 값을 μƒμœ„ μŠ€μ½”ν”„μ—μ„œ μ°ΎμœΌλ©΄μ„œ μ΅œμƒμœ„ ν™˜κ²½μ— λ„λ‹¬ν•˜λ©΄ μ „μ—­ 객체인 thisλ₯Ό λ§ˆμ§€λ§‰μœΌλ‘œ 바인딩함.

     

    μœ„ μ˜ˆμ‹œμ²˜λŸΌ this값은 λ™μ μœΌλ‘œ λ°”λ€Œκ³ , 이λ₯Ό μ›ν•˜μ§€ μ•Šκ³  νŠΉμ • λ©”μ†Œλ“œλ₯Ό ν˜ΈμΆœν•  λ•Œ νŠΉμ •ν•œ this 객체λ₯Ό λ¬Άμ–΄μ£Όκ³  싢은 κ²½μš°λ„ μžˆμ„ 것이닀.

    이 문제λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ 2κ°€μ§€ 방법이 μ œμ•ˆλ˜μ—ˆκ³ , μ‹œκ°„ 순으둜 방법 1 → 방법 2 (ES6)κ°€ λ“±μž₯ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

     

    방법 1. bind, call, apply ν•¨μˆ˜λ₯Ό ν™œμš©ν•œ νŠΉμ • 객체둜 바인딩

     

    bind

    const μš”λ‹ˆ = {
    	name: "μš”λ‹ˆ",
    	main: function(){
    		console.log(this); 
    	}.bind(μš”λ‹ˆ);
    }
    

     

    μ΄λ•Œ bindλΌλŠ” HOC ν•¨μˆ˜λ₯Ό ν™œμš©ν•˜μ—¬ mainλ©”μ„œλ“œμ— “μš”λ‹ˆ” 객체λ₯Ό 묢어놓은 μƒˆλ‘œμš΄ λ©”μ„œλ“œλ₯Ό λ§Œλ“€ 수 μžˆλ‹€.

    call

    function welcome(){
    	console.log(this, x,y);
    }
    
    const μš”λ‹ˆ = {
    	name: "μš”λ‹ˆ",
    }
    
    welcome.call(μš”λ‹ˆ, 3,4) 
    

    bind와 μœ μ‚¬ν•œ κΈ°λŠ₯을 ν•˜λŠ” ν•¨μˆ˜λ‘œ apply, call이 μžˆμŠ΅λ‹ˆλ‹€. μ²«μ§Έ μΈμžλŠ” this에 λ°”μΈλ”©λ˜κ³ , 이후 μΈμžλ“€μ€ λ§€κ°œλ³€μˆ˜λ‘œ μ „λ‹¬λ˜μ–΄ μ¦‰μ‹œ μ‹€ν–‰λ˜λŠ” λ©”μ„œλ“œμ΄λ‹€.

    apply

    function welcome(){
    	console.log(this, x,y);
    }
    
    const μš”λ‹ˆ = {
    	name: "μš”λ‹ˆ",
    }
    
    welcome.apply(μš”λ‹ˆ, [3,4]) 
    

    call ν•¨μˆ˜μ™€ λ™μΌν•˜κ²Œ μ²«μ§Έ μΈμžλŠ” this에 λ°”μΈλ”©λ˜μ§€λ§Œ, μ΄ν›„ μΈμžλ“€μ΄ 리슀트 ν˜•νƒœλ‘œ μ „λ‹¬λ˜μ–΄ μ¦‰μ‹œ μ‹€ν–‰λ˜λŠ” λ©”μ„œλ“œμ΄λ‹€.

     

    방법 2. ν™”μ‚΄ν‘œ ν•¨μˆ˜ ν™œμš©

    ν™”μ‚΄ν‘œ ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜λ©΄, ν•¨μˆ˜κ°€ 선언될 μ‹œμ μ—μ„œ μžμ‹ μ˜ μ‹€ν–‰ μ½˜ν…μŠ€νŠΈμ— thisκ°€ μ‘΄μž¬ν•˜μ§€ μ•Šμ•„ λ°”λ‘œ μƒμœ„ μŠ€μ½”ν”„μ˜ this둜 바인딩됨.

    λ”°λΌμ„œ bind ν•˜λ˜ ν•¨μˆ˜λ₯Ό ν™”μ‚΄ν‘œ ν•¨μˆ˜λ‘œλ§Œ λŒ€μ²΄ν•΄ μ£Όλ©΄ 문제 ν•΄κ²° κ°€λŠ₯.

    const 고정객체 = {
    	name: "휴먼",
    }
    
    const μš”λ‹ˆ = {
    	name: "μš”λ‹ˆ",
    	main: () => {
    		console.log(this); // μš”λ‹ˆ 객체 
    	};
    }
    

    κ·Έλ ‡λ‹€λ©΄ ν™”μ‚΄ν‘œ ν•¨μˆ˜λ₯Ό μ–Έμ œ μ“°λŠ” 게 μ’‹μ„κΉŒ?

    λ³„κ°œμ˜ thisκ°€ μƒμ„±λ˜λŠ” 건 μ›ν•˜μ§€ μ•Šκ³ , μ™ΈλΆ€ μ½˜ν…μŠ€νŠΈμ— μžˆλŠ” thisλ₯Ό μ‚¬μš©ν•˜κ³  싢은 경우 ν™”μ‚΄ν‘œ ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•  수 μžˆλ‹€.

     

     

     

    ν•˜μ§€λ§Œ...!!

    상황에 따라 this에 ν• λ‹Ήλ˜λŠ” 값이 λ‹¬λΌμ§ˆ 수 μžˆκΈ°μ—, μ–΄λ–€ 상황에 따라 λ‹€λ₯Έ 값이 λ°”μΈλ”©λ˜λŠ”μ§€ μ΄ν•΄ν•¨μœΌλ‘œμ¨ thisλ₯Ό 더 잘 μ“Έ 수 μžˆλ‹€. 

     

    1. 동적 바인딩 ⚠️

    일반 ν•¨μˆ˜μ˜ 경우 λ©”μ„œλ“œλ‘œ ν˜ΈμΆœλ˜λŠλƒ / μ•„λ‹ˆλ©΄ ν•¨μˆ˜ 자체둜 ν˜ΈμΆœλ˜λŠλƒμ— 따라 λ™μ μœΌλ‘œ thisκ°€ λ°”μΈλ”©λ©λ‹ˆλ‹€.

     

    1.1. 일반 ν•¨μˆ˜λ‘œ 호좜

    const actor = {
    	name: "솑강",
    	callName: () => console.log(this.name); 
    }
    
    actor.callName() 
    // μžμ‹ μ„ ν˜ΈμΆœν•œ actor 객체가 μ•„λ‹ˆλΌ,
    // Window. ν•¨μˆ˜μŠ€μ½”ν”„λ³΄λ‹€ ν•œ 단계 μƒμœ„μŠ€μ½”ν”„μΈ μ „μ—­μŠ€μ½”ν”„κ°€ this둜 바인딩.
    

    1.2. λ©”μ†Œλ“œλ‘œ 호좜

    var nameCard = {
      name: "솑강",
      addClickEvent: function () {
        this.element = document.getElementById(this.name);
    
        var logName = function () {
          console.log("Hello! ", this);
        };
    
        // this.element.addEventListener("click", logName); // this.element 좜λ ₯
    		this.element.addEventListener("click", logName.bind(this)) // ν•΄λ‹Ή μ‹€ν–‰λ¬Έμ˜ this(λ©”μ†Œλ“œ 호좜 객체)둜 μž¬λ°”μΈλ”© μ‹œμΌœμ•Όν•¨
      }
    };
    
    nameCard.addClickEvent();
    

    1.3. ν™”μ‚΄ν‘œ ν•¨μˆ˜ μ‚¬μš©

    const nameCard = {
      name: "솑강",
      addClickEvent: function () {
        this.element = document.getElementById(this.name);
    
        var logName = () => { // ν•¨μˆ˜κ°€ μ„ μ–Έλœ μ‹œμ μ˜ μƒμœ„ μŠ€μ½”ν”„μ˜ this둜 μžλ™ 바인딩 
          console.log("Hello! ", this.name);
        };
    
        this.element.addEventListener("click", logName);
      }
    };
    
    nameCard.addClickEvent();
    

     

     

    참고자료:

    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind

    https://ko.javascript.info/arrow-functions-basics

Designed by Tistory.