자바스크립트의 함수

빠르게 지식을 훑고 지나갈 수 있는 기회가되서 정리하려합니다.


함수


function foo(x, y) {
  return x + y;
}

return 문으로 값을 반환합니다.

return 문이 없는 경우에는 undefined를 반환합니다.

따라서 모든 함수는 값을 반환합니다!

function foo(x, y) {
  return x + y;
}

foo(1,2);
foo(1);
foo();
foo(1,2,3,4);

함수 호출시 자바스크립트는 함수의 인수전달을 느슨하게 처리합니다.

따라서 호출 자체는 “언제든지” 성공합니다.

이러한 “느슨한” 인수 전달 메커니즘은 값 검증의 습관화를 통해 런타임 오류 가능성을 줄여나갈 수 있습니다.

function foo(x, y) {
  if(x === undefined || y === undefined) {
    throw new Error("인수가 잘못되었군요닝겐 후훗");
  }
  
  return x + y;
}


arguments


function foo(x, y) {
  return arguments.length;
}

foo();	//0
foo(1);	//1
foo(1, 2);	//2
foo(1,2,3,4,5);	//5

arguments는 언제나 제공됩니다!

그래서 항상 arguments를 사용할 수 있죠

function add() {
  let sum = 0;
  for(let i = 0; i < arguments.length; i++) {
    sum += (typeof arguments[i] === 'number') ? arguments[i] : 0;
  }
  
  return sum;
}

add();	//0
add(1,2,3);	//6
add('준우', 1, 2);	//3


객체로서의 함수


자바스크립트에서는 원시데이터타입을 제외한 대부분의 것들이 객체입니다.

함수 또한 객체입니다.

따라서 객체의 특성인 속성과 메소드를 가질 수 있습니다!

function empty(x, y) {
  //아무고토 하지 않는 함쑤..
}

console.log(empty.length);	//2

empty함수의 length속성은 함수의 인수갯수를 의미합니다.

함수는 length이외에도 apply, call, bind, prototype, name, toString같은 다양한 속성과 메소드를 가지고 있습니다.


함수의 속성 및 메소드


함수에는 속성을 추가할 수 있습니다.

function foo(x, y) {
  return x + y;
}

foo.age = 100;
console.log(foo.age);	//100

아래의 함수는, 딱 한번만 자신의 할 일을 수행하고 그 이후에는 할 일을 하지 않는 이상한 함수입니다.

function weird(x, y) {
  if(!weird.isCall) {
    weird.isCall = true;
  } else {
    return undefined;
  }
  
  return x * y;
}

weird(1, 2);	//2
weird(3, 4);	//undefined
werid(5, 6);	//undefined

아래의 함수는 자기 자신을 재정의한 후 반환합니다.

function weird(x, y) {
  weird = function() {};
  
  return x * y;
}

weird(1, 2);	//2
weird(1, 2);	//undefined

아래의 함수는 몇 번 호출되었는지 기억하는 함수입니다.

function genius(x) {
  if(!genius.count) {
    genius.count = 1;
  } else {
    genius.count++;
  }
  
  return x * x;
}

for(let i = 1; i < 100; i++) {
  genius(i);
}

console.log(genius.count);	//99


함수에 포함된 함수


function foo() {
  function bar() {
    return 'hi there';
  }
  
  return bar();
}

foo();	//'hi there'


인스턴스 생성


그렇다면 foo함수 내부에 있는 bar함수를 외부에서 호출할 수 있을까요?

foo.bar();	//Uncaught TypeError: foo.bar is not a function

아뇨. 런타임 오류가 발생합니다.

다른 객체지향 언어와는 달리 자바스크립트는 클래스를 제공하지 ㅇ낳습니다.

하지만 클래스와 유사한 역할인 인스턴스를 생성할 수 있습니다.

바로 new입니다.

function Foo(whoAreYou) {
  this.name = whoAreYou;
  this.getName = function() {
    return this.name;
  };
}

const foo = new Foo("parkjunwoo");
foo.getName();	//"parkjunwoo"

이러한 인스턴스는 보통의 함수와 구분하기위해서 이름을 대문자로 시작합니다.


this


function Foo1() {
  const x = 10;
}

function Foo2() {
  this.x = 10;
}

var foo1 = new Foo1();
var foo2 = new Foo2();
foo1.x;	//undefined
foo2.x;	//10

Foo1의 지역변수 x는 인스턴스 생성 시간동안에만 존재하며 반환과함께 사라집니다.

Foo2의 인스턴스 foo2에서는 x를 참조할 수 있는데, 그 이유는 x가 인스턴스의 실행 컨텍스트를 가리키는 this에 생성되었기 때문입니다.

컨텍스트에 작성된 값들은 컨텍스트의 라이프 사이클동안 유지됩니다.

따라서, Foo2의 인스턴스인 foo2가 메모리에서 제거되기전까지 x값은 유지됩니다!


메소드


메소드도 똑같은 원리로 this에 추가되어야 인스턴스에서 사용할 수 있습니다.

function Foo(name, age) {
  this.name = name;
  this.age = age;
  this.getName = function() {
    return this.name;
  }
  function getAge() {
    return this.age;
  }
}

var foo = new Foo('junwoo', 100);
foo.getName();	//'junwoo'
foo.getAge();	//Uncaught TypeError: foo.getAge is not a function

this에 추가된 함수 getName은 인스턴스에서 호출하고 참조할 수 있습니다.

하지만 this에 추가되지않은 Foo함수 내부의 getAge함수는 인스턴스에서 참조할 수 없습니다.

지역변수와 마찬가지로 생성시점에 함수 안쪽에서만 존재하며 반환과함께 사라지기 때문입니다.


prototype


함수에는 prototype이라는 특수한 객체가 있습니다.

prototype은 속성이나 메소드를 추가할 수 있는데, 함수의 모든 인스턴스에 즉시 반영됩니다!

function Foo(name) {
  this.name = name;
}

var foo1 = new Foo('junwoo');
var foo2 = new Foo('paul');

foo1.name;	//'junwoo'
foo2.name;	//'paul'

foo1.age = 100;
foo1.age;	//100
foo2.age;	//undefined

Foo.prototype.live = 'dongtan';
foo1.live;	//'dongtan'
foo2.live;	//'dongtan'

foo1.age처럼 객체의 인스턴스에 동적으로 추가된 속성은, 해당 인스턴스에서만 존재해서 foo2에서는 불러올 수 없었습니다.

하지만 prototype으로 추가된 속성은 이미 만들어진 인스턴스 모두에 실시간으로 반영되었다는 것을 알 수 있습니다.


call


const person = {
  age: 100,
  addAge: function(num) {
    this.age += num;
  }
};

person.addAge(5);
console.log(person.age);	//105

this에 관한 포스팅에서 공부해서 조금 익숙합니다.

객체인 person의 메소드인 addAge가 호출되었습니다.

addAge의 this는 dot 앞의 person을 가리키겠죠.

call은 이걸 조금 비틀어버릴 수 있습니다.

const person = {
  age: 100,
  addAge: function(num) {
    this.age += num;
  }
};

const otherPerson = {
  age: 1
};

person.addAge.call(otherPerson, 1);
console.log(person.age);	//100
console.log(otherPerson.age);	//2

이것도 같은 포스팅에서 공부했습니다!

call메소드로 addAge함수를 호출했습니다.

첫번째 인수는 this가 아닌, otherPerson객체를 넘겨주었습니다.

person객체의 age가 변경되지않고 otherPerson의 age가 변경되었습니다.


익명함수


var foo = function(x) {
  return x * x;
}

foo(5);	//25

이 함수는 익명함수입니다.

단지, 익명함수를 참조하고 있는 변수 foo를 이용해서 foo라는 함수를 호출하는 것과 똑같은 방식으로 익명함수를 호출하고 있는 것입니다.

익명함수라는 것을 어떻게 증명할까요?


클로저


function multiple() {
  var x = 5;
  
  return function(num) {
    return x * num;
  }
}

var test = multiple();

test(5);	//25
test(10);	//50

multiple함수는 익명함수를 반환합니다.

반환된 익명함수는 상위스코프인 multiple함수의 변수 x를 참조합니다.

그런데.. 함수가 반환되면 해당 함수의 지역변수들은 모두 소멸됩니다.

그렇다면 test에 multiple함수를 실행한 결과값인 익명함수를 담을때, multple함수 내부의 변수들은 모두 소멸되었을텐데, 내부의 익명함수는 어떻게 변수 x를 처리할까요?

이것을 클로저라고 합니다!


콜백함수


var count = 1;

function sayHello() {
  console.log("count... ", count);
  count++;
}

setTimeout(sayHello, 2000);
sayHello();	//"count... 1" 출력 후 2초뒤 "count... 2"출력

이렇게 setTimeout에서 sayHello함수를 받은 것처럼, 인수로 받은 함수를 콜백함수라고 합니다.


Reference

https://medium.com/ibare-story/e252506f8525