2019-07-09 Callback

  • JS에서 함수도 객체이기때문에, 함수를 다른 함수의 인자처럼 사용할 경우에는 오직 함수의 정의만 넘기면 된다.
  • 즉 함수의 이름만 넘기면 된다. 함수라고 해서 () 같은 것을 붙여줄 필요가 없다
setInterval(callback, 1000)

콜백함수는 클로저

  • 다른 함수의 인자로 콜백을 전달할 때, 전달받은 함수의 특정시점에 그 콜백함수가 동작한다. 전달 받은 함수가 이미 콜백함수를 내부에서 정의한것처럼.
  • 콜백은 클로저와 같다. 콜백함수는 콜백함수를 포함한 함수 내부의 인자에 접근이 가능하고 심지어 전역함수에도 접근이 가능하다. 즉 기존 함수가 가진 스코프에서 새로운 내부 스코프가 추가 되는 것이다.

콜백함수의 적용 기본 원칙

이름이나 익명의 함수를 사용하라(Use Named OR Anonymous Function as callbacks)

function logStuff (userData) {
if ( typeof userData === "string") {
console.log(userData);
  } else if ( typeof userData === "object") {
	for (var item in userData) {
	console.log(item + ": " + userData[item]);
    }
  }
}

// 2개의 인자를 받아서 마지막에 콜백함수를 호출
function getInput(options, callback) {
	allUserData.push(options);
    callback(options)
}

getInput({name : 'Rich', speciality : 'js'}, logStuff)

콜백함수가 실행 되기전에 함수임을 명확하게 하기 : 콜백함수가 인자로 전달되어 함수 내부에서 실행될 때 전달받은 인자가 함수인지를 명확하게 하자.

function getInput(options, callback) {
	allUserData.push(options);

    if (typeof callback === 'function') {
    	callback(options);
    }
}

this를 사용한 메서드를 콜백으로 사용시 문제

  • 콜백함수가 this객체를 사용하는 메서드인 경우 반드시 this객체의 컨택스트를 보호하도록 콜백함수를 수정해야한다.
  • 예를들어 전역함수에 인자로 콜백함수가 전달된 경우에는 this가 window객체를 가리키게 만들거나 또는 콜백함수를 감싸고 있는 메서드를 가리키게 해야한다.
let clientData = {
  id: 094545,
  fullName: 'Not set',

  setUserName: function(firstName, lastName) {
    console.log(`setUsername에 전달받은 ${firstName}, ${lastName}`);
    this.fullName = firstName + ' ' + lastName;
  }
}

function getUserInput(firstName, lastName, callback) {
  callback(firstName, lastName);
}

getUserInput('Barack', 'Obama', clientData.setUserName);

console.log(clientData.fullName);

console.log(global.fullName);
//result
setUsername에 전달받은 Barack, Obama
this value in clientData [object global]
Not set
Barack Obama
  • clientData.setUserName을 실행하면 예상과 달리 this.name이 세팅이 되지 않는다. 이는 getUserInput메서드가 글로벌 함수이기 때문이다. this가 global(window)이기때문이다.
  • this바인딩이 js의 문제중 하나이다. call과 apply를 통한 this를 보호해야한다. 이 함수들은 this객체를 유지하고 내부 인자들을 함수로 전달하는 역할을 한다.
let clientData = {
  id: 094545,
  fullName: 'Not set',
  setUserName: function(firstName, lastName) {
    console.log(`setUsername에 전달받은 ${firstName}, ${lastName}`);
    console.log(`this value in clientData ${this}`);
    this.fullName = firstName + ' ' + lastName;
  }
}

function getUserInput2(firstName, lastName, callback, callbackObj) {
  callback.apply(callbackObj, [firstName, lastName]);
}
getUserInput2('jimmy', 'kim', clientData.setUserName, clientData);
  • call함수는 항상 첫번째 인자로 this객체를 사용한다. 나머지 인자들은 콤마를 기준으로 구분
  • Apply의 경우 첫번째 인자로 this객체를 사용하고, 마지막 파라미터는 배열로 존재.
  • 위의 형태로 this를 매핑할 수 있다.

this

  • js는 함수도 객체로 취급을 하기 때문에 애매함이 있다.
  • 함수는 실행이 가능한 코드와 연결된 객체라는 개념으로 접근을 하면 좋다.
  • 함수는 객체이므로 확장이 가능하다.
function hello(x) {
  console.log('hello ' + x);
}

hello('first');

hello['woman'] = 'Beautiful lady';

console.log(hello.woman)
//result
hello first
Beautiful lady
var myDog = {
  name: 'Badoogi',
  bark: function() {
    console.log('wa wa aw');
  },

  callDog: function() {
    console.log(this.name + ' Come on');
  }
}

console.log(myDog.callDog());

  • 이 객체 안에서의 this는 myDog라는 객체이다. this가 저 객체만 가리키고 있나? No => this는 this를 둘러싸고 있는 객체만을 가리키는 상태가 아니기 때문이다. !!!
  • this는 this가 포함된 값을 부르는 객체를 가리키도록 변경이 가능하다.
var speech = 'this is a global value';

var william = {
  'speech': 'williamShakespeare said~~~',
  'sayIt': displayQuote
};

function displayQuote() {
  console.log(this.speech + '!!!');
  // 이곳의 this는 정적인 값이 아닌 함수를 호출하는 객체에 따라 다른 this를 가진다.
}

william.sayIt();
displayQuote();

//result
williamShakespeare said~~~!!!
undefined!!!
  • displayQuote()라는 함수를 단독으로 실행시키면, this는 브라우저에서는 window 콘솔에서는 global을 가리키게 된다.
  • 콘솔에서는 결과가 undefined!!!이지만 크롬에서는 this is global value!!로 나온다.

JS함수에서 객체를 가지지 않는 상태의 함수를 부르는 일은 결코 없어야 한다.

console.log('isNaN??' + isNaN(NaN));

function changeNaN() {
  this.isNaN = function() {
    return 'not anymore';
  }
}

changeNaN();

console.log('isNaN??' + isNaN(NaN));

  • 객체를 가지지 않는 changeNaN함수를 호출했더니 this가 global객체를 가리키게 되어 전역변수 자체 값을 바꿔버렸다. 이 문제를 해결하기 위해 call, apply함수를 사용한다. 즉 다른 객체에 의해 함수가 호출되는 것이 아닌 그 함수 자체가 실행이 되게 만드는 것이다.

call(thisObj[, arg1[, arg2[, [, argN]]]])

  • 여기서 thisObj는 선택사항으로 현재의 객체로 사용이 될 객체이다.
Written on July 9, 2019