본문 바로가기

코드스테이츠(Pre)

자바스크립트_함수, 프로토타입 체이닝

Function Statements

greet() // hi
function greet(){
	console.log('hi');
}

 

Function Expressions

: value를 결과로 갖는 코드

a = 3 // 3
1+2; // 3
a = {greeting:'hi'} // Object{greeting:'hi'}

anonymousGreet();   // TypeError: anonymousGreet is not a function 
var anonymousGreet  // -> 호이스팅!
	= function(){	// object 만들어짐(result)
		console.log('hi');
	}	

 

콜백함수와 비동기작업

 

콜백함수 (주로 이벤트 핸들러에서 사용)

매개변수를 통해 전달되고 전달받은 함수의 내부에서 어느 특정시점에서 실행

var button = document.getElementById('myButton');
button.addEventListener('click', function() {
	console.log('button clicked!');
});

비동기 처리 모델에서 사용된다.

처리가 종료하면 호출될 함수(콜백)을 미리 매개변수에 전달하고 처리가 종료하면 콜백함수를 호출

setTimeout(function () {
	console.log('1초 후 출력된다.');
 	}, 1000);

 

콜백함수는 콜백 큐에 들어가 있다가 해당 이벤트가 발생하면 호출된다. 클로저이므로 콜백 큐에 존재하다가 호출되어도 콜백함수를 전달 받은 함수의 변수에 접근할 수 있다.

function doSomething(){
    var name = 'Lee';
    setTimeout(function() {
		console.log('My name is ' + name);
        }, 100);
   }
   
doSomething();

 

for loop 안에서의 콜백함수 사용

for(var i = 0; i < 10; i++){
	setTimeout(function() {
		console.log(i);
    }, 10);
}
console.log('done');
// done
// 10 10 10.....

 

let 키워드 사용

for(let i = 0; i < 10; i++){
	setTimeout(function() {
    	console.log(i);
    }, 10);
}
// 0 1 2 3 4 ....

 

콜백형태로 실행

repeatConsole = function(i, callback) {
    setTimeout(function() {
    	console.log(i);
        if(i >= 9) {
        	callback();
        } else {
        	repeatConsole(i+1, callback);
        }
    }, 10);
}

repeatConsole(0, function() {
	console.log('done');
});
// 0 1 2 3 ... done

 

'this'

메소드의 내부함수의 this 호출시

var c = {
	name: 'The c object',
    	log: function() {
    	this.name = 'Updated c object';
        console.log(this) 
        var setname = function(newname) {
        	this.name = newname; 
        }
        setname('Updated again! The c object'); // in global
        console.log(this);
    }
}

 

this를 따로 선언

var c = {
	name: 'The c object',
    	log: function() {
    	var self = this;
    	self.name = 'Updated c object';
        console.log(self) 
        
        var setname = function(newname) {
        	self.name = newname;	// scope chain
        }
        setname('Updated again! The c object'); 
        console.log(self); 
    }
}

 

IIFE가 만들어 지는 과정

function(name){
	return 'Hello' + name;
}
// function 키워드를 보면 statement라고 인식 함수이름(Ex.function greet)을 필요 > Error
// function 앞에 () operator를 먼저 사용
// statement를 ()로 묶으면 expression으로 인식
// -> 메모리상에 function이 만들어짐 

(function(name){
	var greeting = 'Inside IIFE:hello';
    console.log(greeting + ' ' +name);
}(tom));	// Inside IIFE: Hello Tom

 

this

var fn = function(one, two) {
	console.log(this, one, two);
};

var r = {r:1}, g = {g:1}, b = {b:1}, y = {y:1};
r.method = fn;

r.method(g,b);//output         > r{}, g{} , b{}
fn(g,b);	//  output     > <global>, g{}, b{}
fn.call(r,g,b);	// 	       > r{}, g{}, b{}
r.method.call(y,g,b);   // > y{}, g{}, b{}
setTimeout(fn, 1000);	// > <global>, undefined, undefined
setTimeout(r.method, 500*2);// > <global>, undefined, undefined
setTimeout(function(){
	r.method(g,b)
    }, 1000);
log(this);
new r.method(g,b);

 

this 키워드는 대부분 중요한 경우 파라미터처럼 동작한다. 

Input Parameters는 함수가 실행될때만 바인딩 된다.

this 'rule' : find call time, look for a dot, look to the left of the dot

 

메소드 실행에서 파라미터로서의 this(가장 자주사용)

r.method(g,b) // {}{}{} (left of the dot) 

value는 dot의 왼쪽에서 오른쪽으로 전달되기 때문에 자동적으로 this의 이름이 주어진다.

 

fn(g,b) // <global>{}{} (dot이 없다면?)                                                                                         

닷 으로 특정하게 this가 바인딩 되지 않았을 경우, global obj를 기본 값으로 바인딩 한다. 
파라미터 위치에 있지만 arguments가 충분하지 않을경우 undefined로 바운드 되는것과 비슷하다
this의 bind한 객체가 함수를 property로 갖고 있지 않을 경우 dot으로 접근이 불가능 하다.
left-of-dot rule은 this와 같은 파라미터를 특정값을 bound시키는 다른 방법이다

 

fn.call(r,g,b) // {} , {} , {}

call method를 사용해서 global obj을 덮어쓴다. call을 사용하면, 인자의 첫번째에 하나의 추가 값을 패스 할 수 있다.
'left of the dot' 규칙을 무시하고 함수가 실행될때 원하는 특정한 this값을 바인딩한다. 특정위치의 파라미터가 arguments로 bound되는 것처럼

 

r.method.call(y,g,b) // {} {}{}

call을 프로퍼티로 접근하는 곳에 사용한다면?
call이 메소드가 접근하는 규칙을 덮어쓰고 yellow obj를 바인딩 한다.

 

 

<--- setTimeout함수를 가상으로 구현한 내부 --->
var setTimeout = function(cb, ms){	// delay 시키는 ms 파라미터, 실행될 함수 존재
    waitSomehow(ms);
    cd();
}

 

setTimeout(fn, 1000) // <global>, undefined, undefined

어떤 value가 함수의 파라미터로 바운드 될지 함수의 호출전에는 알아보기 힘들다. 답을 하기 위해서는 fn 콜백함수의 호출을 확인해야 한다.
setTimeout의 콜백함수에서는 전달될 value를 알기 힘듬, value없이 실행 -> undefined
' find call time, look for a dot, look to the left of the dot'
this의 바인딩은 콜백함수가 실행되는 마지막줄을 봐도 dot이 없기때문에 기본 경우에 해당(global)
(가상버전의 setTimeout도 call과 같은 다른 메소드를 사용하지 않는다.)

setTimeout(r.method, 500 * 2) // <global>, undefined, undefined

value가 전달되지 않았기에 기본 파라미터의 경우 undefined이고, setTimeout은 빨간색을 포함해서 어떤 색의 객체 또한 전달되지 않았기에 어떤 value를 갖는지 확인 할 수 없다.
red 객체가 전달됬을거라 생각하기 쉽지만, cb만 첫번째 인자로 전달됬다.
this의 바인딩을 알기 위해선 어디서 함수가 객체를 참고하는지가 아니라 어디서 실행되는지를 확인해야한다
(It isn't determined where the function is looked up on an object, but by where it's invoked)
위치파라미터의 바인딩의 결과를 알려면 calltime을 확인해라
(so it's not the case that function object stored in r.method somehow gets contaminated by r, such that it will always run with  a this binding of r. Call time of the function containing this always dictates the binding for this.)
fn대신 r.method가 패스되도 setTimeout의 마지막줄은 닷이 포함된 method invocation이 아니고 여전히 free function invocation이다 그러므로 기본 값 global이 들어간다. 

this 바인딩을 구체적으로 하기 위해서는? cd.call(?)을 써야 할까?
비록 setTimeout의 도중에 메소드를 참고하더라도, setTimeout 내부에서는 r Object에 대한 참고는 전혀 없다.
setTimeout이 콜백함수에 접근하기 전까지 참고하는 것은 cb라 이름을 주는 순간이 유일하다.
(*setTimeout doensn't have is an additional reference to whatever object that function happened to be found on in the moments right before it got passed in. So there's no way for .call to specify that object.)

두번째 파라미터인 ms에 대해 보면, math operation(500 * 2)의 결과가 전달된다면 setTimeout 내부에서 1000이 두 수의 곱이란걸 알수 있을까? 아니다. 인풋을 받기 전에 발생한 정보에 대해서는 알 수 가 없다(콜백과 똑같다)
그러므로 콜백에 파라미터(this포함)을 전달할 경우 주의 해서 사용해야 한다.
닷 operator를 통해 전달해도 그 object는 콜백함수에 바인딩 되지 않는다.

setTimeout(function() { r.method() }, 1000); // {} , undefined, undefined

파라미터의 바인딩을 복잡하게 하지않고 콜백함수를 전달하는 방법은 함수 안에 공간을 만들고 메소드 자체를 호출할때 this 바인딩이 가능하다. 똑같은 방식으로 위치파라미터또한 설정가능하다.


log(one); 

파라미터 one은 fn함수안에서만 정의 되었기 때문에 global scope에서는 에러가 발생한다. 

log(this); 

this는 기본적으로 global 객체로 바인딩되어있어서 참고 가능하다.

 

new r.method(g,b); //  ,{} , {}

new키워드를 사용하는 경우 위치파라미터는 영향을 받지않고 그대로 실행된다. this 바인딩의 경우 완전 새로운 객체가 바운딩 된다. new로 나타난 결과의 배경으로서 자동적으로 생성된다.

 

call(), apply(), bind()

 

bind() 

: 함수를 바로 실행시키지 않고, 인자로 들어가는 객체를 함수의 this로 바꿔준다

var person = {
    firstname : 'John',
    lastname : 'Doe',
    getFullName: function() {
        var fullname = this.firstname + ' ' + this.lastname;
        return fullname;
    } 
}

var logName = function(lang1, lang2) {
    console.log('Logged: ' + this.getFullName());
    console.log('Arguments: ' + lang1 + ' ' +. lange2);
    console.log('----------');
} // .bind(person) 해도 아래와 동일한 결과(Logged만 하면 가능, Arguments까지 하면 SyntaxErr(?))

logName(); // undefined error, 현재 this는 logName

//logPersonName 의 this를 person 으로 변경
var logPersonName = logName.bind(person); // NOT EXECUTED!

logName(); // this가 person으로 변경, Logged John Doe
var logPErsonName = logName.bind(person); 
logPersonName('en');// Logged: John Doe 
		// Arguments: en undefined

 

call(), apply()

: this를 변경하고 함수까지 실행한다. 인자의 차이

logName.call(person, 'en', 'es');
console.log('------------');
logName.apply(person, ['es', 'en']);

//Logged: John Doe
//Arguments: en es
//----------------
//Logged: John Doe
//Arguments: es en
//----------------

//IIFE 로도 가능
(function(lang1, lang2) {
    console.log('Logged: ' + this.getFullName());
    console.log('Arguments: ' + lang1 + ' ' +. lange2);
    console.log('----------');
}).apply(person, ['en', 'es']);

//Logged: John Doe
//Arguments: en es
//----------------

 

사용예시

//1. function borrowing
var person2 = {  // getFullName() 없는 객체 생성
    firstname : 'John',
    lastname : 'Doe',
}

// person 의 getFullName() 빌려 올 수 있다
console.log( person.getFullName.apply(person2) );

//2. function currying : copy function with some preset parameters
function multiply(a,b) {
    return a*b;
}

//인자 기본값을 넣은 새로운 함수 생성 효과
var multipleByTwo = multiply.bind(this, 2); 
console.log(multipleByTwo(4)); // 8

var multipleByThree = multiply.bind(this, 3);
console.log(multipleByTwo(4)); // 12

 


  • https://latentflip.com/loupe