Language/Javascript

[Javascript] 자바스크립트 함수 - 매개변수와 인수, this, 프로퍼티

재은초 2023. 6. 20. 16:18
반응형

함수(function)란?

  • 함수란 하나의 특별한 목적의 작업을 수행하도록 설계된 독립적인 블록을 의미하며, 이러한 함수는 필요할 때마다 호출하여 해당 작업을 반복해서 수행할 수 있다.
  • 자바스크립트에서 블록이란 함수나 실행문의 중괄호 {} 로 묶여진 부분을 가르킨다.
  • 자바스크립트에서는 함수도 하나의 타입(datatype)으로 함수를 변수에 대입하거나, 함수에 프로퍼티를 지정하는 것도 가능하며, 함수는 다른 함수 내에 중첩되어 정의될 수도 있다.
function addNum(x, y) {
    return x + y;
}

document.write(addNum(2, 3));

 

함수의 정의

  • 자바스크립트에서 함수의 정의는 function 키워드로 시작되며, ① 함수의 이름(function name) ② 함수의 매개변수(parameter) ③ 중괄호 {} 로 둘러싸인 자바스크립트 실행문의 요소를 가진다.
  • 함수 이름은 함수를 구분하는 식별자(identifier)이며, 매개변수란 함수를 호출할 때 인수(argument)로 전달된 값을 함수 내부에서 사용할 수 있게 해주는 변수다.

함수 선언문로 함수 정의하기

function 함수이름(매개변수1, 매개변수2, ...) {
    함수가 호출되었을 때 실행하고자 하는 실행문;
}
function addNum(x, y) {      // addNum라는 이름의 함수와 x, y의 매개변수 정의
    document.write(x + y);
}

addNum(2, 3);                // addNum() 함수에 인수로 2와 3을 전달하여 호출

함수 표현식로 함수 정의하기

  • 자바스크립트의 함수는 일급 객체이므로 ① 무명의 리터럴로 표현이 가능하고, ② 변수나 자료 구조(객체, 배열…)에 저장할 수 있으며, ③ 함수의 파라미터로 전달할 수 있고, ④ 반환값(return value)으로 사용할 수 있다.
  • 함수의 일급객체 특성을 이용하여 함수 리터럴 방식으로 함수를 정의하고 변수에 할당할 수 있는데 이러한 방식을 함수 표현식(Function expression)이라 한다.
  • 함수 표현식 방식으로 정의한 함수는 함수명을 생략할 수 있다. 이러한 함수를 익명 함수(anonymous function)이라 한다. 함수 표현식에서는 함수명을 생략하는 것이 일반적이다.
  • 함수가 할당된 변수를 사용해 함수를 호출하지 않고 기명 함수의 함수명을 사용해 호출하게 되면 에러가 발생한다. 이는 함수 표현식에서 사용한 함수명은 외부 코드에서 접근 불가능하기 때문이다.
// 기명 함수 표현식(named function expression)
var foo = function multiply(a, b) {
  return a * b;
};

// 익명 함수 표현식(anonymous function expression)
var bar = function(a, b) {
  return a * b;
};

console.log(foo(10, 5)); // 50
console.log(multiply(10, 5)); // Uncaught ReferenceError: multiply is not defined

Function 생성자 함수로 정의하기

  • 함수 선언문과 함수 표현식은 모두 함수 리터럴 방식으로 함수를 정의하는데 이것은 결국 내장 함수 Function 생성자 함수로 함수를 생성하는 것을 단순화시킨 축약법이다.
  • Function 생성자 함수로 함수를 생성하는 방식은 일반적으로 사용하지 않는다.
var square = new Function('number', 'return number * number');
console.log(square(10)); // 100

 

매개변수(parameter)와 인수(argument)

  • 매개변수(parameter)란 함수의 정의에서 전달받은 인수를 함수 내부로 전달하기 위해 사용하는 변수를 의미하며, 인수(argument)란 함수가 호출될 때 함수로 전달해주는 값을 말한다.
  • 자바스크립트에서는 함수를 정의할 때는 매개변수의 타입을 따로 명시하지 않는데, 함수를 호출할 때에도 인수로 전달된 값에 대해서도 따로 타입 검사를 하지 않는다.
  • 함수 호출시 함수 정의보다 적은 수의 인수가 전달되어도 오류를 발생시키지 않는데, 이 경우 전달되지 않은 나머지 매개변수에 자동으로 undefined 값을 설정한다.
function addNum(x, y, z) {    // x, y, z라는 3개 매개변수를 가지는 함수 정의
    return x + y + z;
}

addNum(1, 2, 3);        // 인수로 1, 2, 3을 전달하여 함수를 호출 -> 6
addNum(1, 2);           // 인수로 1, 2, undefined을 전달하여 함수를 호출 -> NaN
addNum(1);              // 인수로 1, undefined, undefined을 전달하여 함수를 호출 -> NaN
addNum();               // 인수로 undefined 아무것도 전달하지 않고 함수를 호출 -> NaN


/* 전달되지 않은 값을 undefined 대신 따로 값을 설정해 산술 연산 수행하는 방법 */
function addNum(x, y, z) {
    if(x === undefined) // 함수 호출시 x에 해당하는 인수가 전달되지 않은 경우
        x = 0;          // 변수 x의 값을 undefined에서 0으로 변경
    if(y === undefined) // 함수 호출시 y에 해당하는 인수가 전달되지 않은 경우
        y = 0;          // 변수 y의 값을 undefined에서 0으로 변경
    if(z === undefined) // 함수 호출시 z에 해당하는 인수가 전달되지 않은 경우
        z = 0;          // 변수 z의 값을 undefined에서 0으로 변경
    return x + y + z;
}

addNum(1, 2, 3);        // 6
addNum(1, 2);           // 3
addNum(1);              // 1
addNum();               // 0

디폴트 매개변수(default parameter)

  • 디폴트 매개변수란 ECMAScript 6부터 새롭게 정의된 매개변수로, 함수를 호출할 때 명시된 인수를 전달하지 않았을 경우에 사용하게 될 기본값을 의미하며, 자바스크립트에서 매개변수의 기본값은 undefined이다.
function mul(a, b) {
    // 인수가 한 개만 전달되었을 때 나머지 매개변수 값을 undefined 값이 아닌 1로 설정
    b = (typeof b !== 'undefined')  ? b : 1;
    return a * b;
}

mul(3, 4);                   // 12
mul(3);                      // 3


/* 디폴트 매개변수를 이용 */
function mul(a, b = 1) {
    // 인수가 한 개만 전달되면 나머지 매개변수의 값을 언제나 1로 설정
    return a * b;
}

mul(3, 4);                     // 12
mul(3);                        // 3

나머지 매개변수(rest parameter)

  • 나머지 매개변수는 생략 접두사 ... 를 사용하여 특정 위치의 인수부터 마지막 인수까지를 한 번에 지정할 수 있다.
  • arguments 객체를 이용하는 것 말고도 불규칙적으로 전달되는 아규먼트를 다루는 방법으로 Rest Parameter가 있다.
  • ECMAScript 6부터 새롭게 정의된 매개변수로, 파라미터 앞에 마침표 세 개를 붙여주면 여러 개로 전달되는 아규먼트들을 배열로 다룰 수가 있다.
  • arguments객체는 유사 배열이기 때문에 배열의 메소드를 활용할 수 없는 반면, rest parameter는 배열이기 때문에 배열의 메소드를 자유롭게 사용할 수 있다는 장점이 있다.
  • 이름 그대로 앞에 정의된 이름 그대로 앞에 정의된 파라미터에 argument를 먼저 할당하고 나머지 argument를 배열로 묶는 역할을 하기 때문에 일반 파라미터와 함께 사용할 때는 반드시 가장 마지막에 작성해야 한다.
/* 첫 번째 인수에서 두 번째 인수부터 마지막 인수까지를 뺀 후 그 결과를 반환 */
function sub() {
    var firstNum = arguments[0];                  // 첫번째 인수에서
    for(var i = 0; i < arguments.length-1; i++) { // 두번째부터 마지막 까지를
        firstNum -= arguments[i+1];               // 뺌
    }
    return firstNum;
}

sub(10, 2, 3);              // 10 - 2 - 3 = 5
sub(10, 1, 5, 8);           // 10 - 1 - 5 - 8 = -4


/* 나머지 매개변수를 이용하면 sub() 함수를 정의 */
// 첫 번째 인수를 변수 firstNum에 저장하고 나머지 인수들은 배열 restArgs에 저장
function sub(firstNum, ...restArgs) {
    for(var i = 0; i < restArgs.length; i++) {
        firstNum -= restArgs[i];
    }
    return firstNum;
}

sub(10, 2, 3);              // 10 - 2 - 3 = 5
sub(10, 1, 5, 8);           // 10 - 1 - 5 - 8 = -4

 

arguments 객체

  • 자바스크립트 함수 안에는 arguments라는 독특한 객체가 존재하는데, arguments 객체는 함수를 호출할 때 전달한 아규먼트들을 배열의 형태로 모아둔 유사 배열 객체이다.
  • 만약 함수의 정의보다 더 많은 수의 인수가 전달되면 매개변수에 대입되지 못한 인수들은 참조할 방법이 없지만, arguments 객체를 이용하면 함수로 전달된 인수의 총 개수를 확인하거나 각각의 인수에도 바로 접근할 수 있다.
  • arguments 객체는 함수가 호출될 때 전달된 인수를 배열의 형태로 저장하고 있으며, 첫 번째 인수는 arguments[0]에 저장되며 다음 인수는 arguments[1]에 저장되고 인수의 총 개수는 arguments 객체의 length 프로퍼티에 저장된다.
  • arguments 객체는 배열과 비슷할 뿐 실제로 Array 객체는 아니며, 숫자로 된 인덱스와 length 프로퍼티만을 가지고 있을 뿐 모든 것을 배열처럼 다룰 수는 없다.
function addNum() {
    var sum = 0;                                // 합을 저장할 변수 sum 선언

    for(var i = 0; i < arguments.length; i++) { // 전달받은 인수 수만큼 반복
        sum += arguments[i];             // 전달받은 각각의 인수를 sum에 더함
    }
    return sum;
}

addNum(1, 2, 3);                        // 6
addNum(1, 2);                           // 3
addNum(1);                              // 1
addNum();                               // 0
addNum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);  // 55

 

함수 호출 방식에 의해 결정되는 this

  • 자바스크립트의 함수는 호출될 때, 매개변수로 전달되는 인자값 이외에, arguments 객체와 this를 암묵적으로 전달 받는다.
  • Java에서 this는 인스턴스 자신(self)을 가리키는 참조변수로 this가 객체 자신에 대한 참조 값을 가지고 있다. 하지만 Javascript의 경우 Java와 같이 this에 바인딩되는 객체는 한가지가 아니라 해당 함수 호출 방식에 따라 this에 바인딩되는 객체가 동적으로 결정된다.

함수 호출

  • 전역객체(Global Object)는 모든 객체의 유일한 최상위 객체를 의미하며 일반적으로 Browser-side에서는 window, Server-side(Node.js)에서는 global 객체를 의미한다.
  • 기본적으로 this는 전역객체(Global object)에 바인딩된다. 내부함수는 일반 함수, 메소드, 콜백함수 어디에서 선언되었든 관게없이 this는 전역객체를 바인딩한다. 
// in browser console
this === window // true

// in Terminal
node
this === global // true
var value = 1;

var obj = {
  value: 100,
  foo: function() {
    console.log("foo's this: ",  this);  // obj
    console.log("foo's this.value: ",  this.value); // 100
    function bar() {
      console.log("bar's this: ",  this); // window
      console.log("bar's this.value: ", this.value); // 1
    }
    bar();
  }
};

obj.foo();

메소드 호출

  • 함수가 객체의 프로퍼티 값이면 메소드로서 호출된다. 이때 메소드 내부의 this는 해당 메소드를 소유한, 즉 해당 메소드를 호출한 객체에 바인딩된다.
var obj1 = {
  name: 'Lee',
  sayName: function() {
    console.log(this.name);
  }
}

var obj2 = {
  name: 'Kim'
}

obj2.sayName = obj1.sayName;

obj1.sayName();
obj2.sayName();

생성자 함수 호출

  • 자바스크립트의 생성자 함수는 말 그대로 객체를 생성하는 역할을 한다. 하지만 자바와 같은 객체지향 언어의 생성자 함수와는 다르게 그 형식이 정해져 있는 것이 아니라 기존 함수에 new 연산자를 붙여서 호출하면 해당 함수는 생성자 함수로 동작한다.
  • 이는 반대로 생각하면 생성자 함수가 아닌 일반 함수에 new 연산자를 붙여 호출하면 생성자 함수처럼 동작할 수 있다. 따라서 일반적으로 생성자 함수명은 첫문자를 대문자로 기술하여 혼란을 방지하려는 노력을 한다.
// 생성자 함수
function Person(name) {
  this.name = name;
}

var me = new Person('Lee');
console.log(me); // Person&nbsp;{name: "Lee"}

// new 연산자와 함께 생성자 함수를 호출하지 않으면 생성자 함수로 동작하지 않는다.
var you = Person('Kim');
console.log(you); // undefined
  • new 연산자와 함께 생성자 함수를 호출하면
  1. 빈 객체 생성 및 this 바인딩 : 생성자 함수의 코드가 실행되기 전 빈 객체가 생성되며, 이 빈 객체가 생성자 함수가 새로 생성하는 객체다. 이후 생성자 함수 내에서 사용되는 this는 이 빈 객체를 가리킨다. 그리고 생성된 빈 객체는 생성자 함수의 prototype 프로퍼티가 가리키는 객체를 자신의 프로토타입 객체로 설정한다.
  2. this를 통한 프로퍼티 생성 : 생성된 빈 객체에 this를 사용하여 동적으로 프로퍼티나 메소드를 생성할 수 있다. this는 새로 생성된 객체를 가리키므로 this를 통해 생성한 프로퍼티와 메소드는 새로 생성된 객체에 추가된다.
  3. 생성된 객체 반환 : 반환문이 없는 경우, this에 바인딩된 새로 생성한 객체가 반환된다. 명시적으로 this를 반환하여도 결과는 같다.
    반환문이 this가 아닌 다른 객체를 명시적으로 반환하는 경우, this가 아닌 해당 객체가 반환된다. 이때 this를 반환하지 않은 함수는 생성자 함수로서의 역할을 수행하지 못한다. 따라서 생성자 함수는 반환문을 명시적으로 사용하지 않는다.
function Person(name) {
  // 생성자 함수 코드 실행 전 -------- 1
  this.name = name;  // --------- 2
  // 생성된 함수 반환 -------------- 3
}

var me = new Person('Lee');
console.log(me.name);
  • 일반함수와 생성자 함수에 특별한 형식적 차이는 없으며 함수에 new 연산자를 붙여서 호출하면 해당 함수는 생성자 함수로 동작한다.그러나 객체 생성 목적으로 작성한 생성자 함수를 new 없이 호출하거나 일반함수에 new를 붙여 호출하면 오류가 발생할 수 있다.
  • 일반함수와 생성자 함수의 호출 시 this 바인딩 방식이 다른데, 일반 함수를 호출하면 this는 전역객체에 바인딩되지만 new 연산자와 함께 생성자 함수를 호출하면 생성자 함수 내부의 this는 생성자 함수에 의해 암묵적으로 생성된 빈 객체(인스턴스)에 바인딩된다.
// 생성자 함수를 new 없이 호출한 경우
// 함수 내부 this는 전역객체를 가르키므로 전역변수 window에 name 프로퍼티가 바인딩된다
// 또한 암묵적으로 반환하던 this도 반환되지 않으므로 반환문이 없어 undefined를 반환한다
function Person(name) {
  this.name = name;
};

var me = Person('Lee');

console.log(me); // undefined
console.log(window.name); // Lee

apply/call/bind 호출

  • this에 바인딩될 객체는 함수 호출 패턴에 의해 결정되는데, 이는 자바스크립트 엔진이 수행하는 것이다. 이러한 자바스크립트 엔진의 암묵적 this 바인딩 이외에 this를 특정 객체에 명시적으로 바인딩하는 방법도 제공되는데, 이를 가능하게 하는 것이 Function.prototype.apply와 call, bind 메소드다.
  • apply() 메소드를 호출하는 주체는 함수이며 apply() 메소드는 this를 특정 객체에 바인딩할 뿐 본질적인 기능은 함수 호출이라는 것이다.
var Person = function (name) {
  this.name = name;
};

var foo = {};

// apply 메소드는 생성자함수 Person을 호출한다. 이때 this에 객체 foo를 바인딩한다.
// func.apply(함수 내부의 this에 바인딩할 객체, [함수에 전달할 argument의 배열])
Person.apply(foo, ['name']);

console.log(foo); // { name: 'name' }
function Person(name) {
  this.name = name;
}

Person.prototype.doSomething = function (callback) {
  if (typeof callback == 'function') {
    // func.call(함수 내부의 this에 바인딩할 객체, 함수에 전달할 argument)
    callback.call(this);
  }
};

function foo() {
  console.log(this.name);
}

var p = new Person('Lee');
p.doSomething(foo);  // 'Lee'
function Person(name) {
  this.name = name;
}

Person.prototype.doSomething = function (callback) {
  if (typeof callback == 'function') {
    // callback.call(this);
    // this가 바인딩된 새로운 함수를 호출
    callback.bind(this)();
  }
};

function foo() {
  console.log('#', this.name);
}

var p = new Person('Lee');
p.doSomething(foo);  // 'Lee'

 

반환(return)문

  • 자바스크립트에서 함수는 반환문을 포함할 수 있으며, 반환문을 통해 호출자는 함수에서 실행된 결과를 전달받을 수 있다.
  • 함수는 반환을 생략할 수 있다. 이때 함수는 암묵적으로 undefined를 반환한다.
  • 자바스크립트 해석기는 return 키워드를 만나면 함수의 실행을 중단한 후, 함수를 호출한 코드로 되돌아간다. 만일 return 키워드 이후에 다른 구문이 존재하면 그 구문은 실행되지 않는다.
function multiNum(x, y) {
    return x * y;         // x와 y를 곱한 결과를 반환
}

var num = multiNum(3, 4); // 함수 호출된 후 그 반환값이 변수 num에 저장
document.write(num);

 

함수의 호출

  • 정의된 함수는 프로그램 내에서 호출되어야 비로소 실행된다.
function addNum(x, y) {   // 함수 정의
    return x + y;
}

var sum = addNum(3, 5);   // 함수 addNum() 호출 인수로 3과 5 전달
                          // 함수 호출이 끝난 뒤 반환값을 변수 sum에 대입

 

함수의 유효 범위(function scope)

  • 대부분의 프로그래밍 언어에서는 블록 내에서 정의된 변수를 블록 외부에서는 접근할 수 없으며, 이를 블록 단위 유효 범위라고 한다.
  • 하지만 자바스크립트는 다른 언어와는 달리 함수를 블록 대신 사용하는 함수 단위 유효 범위로, 자바스크립트에서 함수는 자신이 정의된 범위 안에서 정의된 모든 변수와 함수에 접근할 수 있다.
  • 전역 함수는 모든 전역 변수와 전역 함수에 접근할 수 있지만, 다른 함수 내에 정의된 내부 함수는 그 함수의 부모 함수에서 정의된 모든 변수 및 부모 함수가 접근할 수 있는 모든 다른 변수까지도 접근할 수 있다.
var x = 10, y = 20;           // x, y을 전역 변수로 선언

function sub() {              // sub()를 전역 함수로 선언
    return x - y;             // 전역 변수인 x, y에 접근
}

document.write(sub());        // -10


function parentFunc() {       // parentFunc()을 전역 함수로 선언
    var x = 1, y = 2;         // 전역 변수와 같은 이름으로 전역 변수 범위 제한
    function add() {          // add() 함수는 내부 함수로 선언
        return x + y;         // 전역 변수가 아닌 지역 변수 x, y에 접근
    }
    return add();
}

document.write(parentFunc()); // 3

함수 호이스팅(hoisting)

  • 자바스크립트에서 함수의 유효 범위라는 것은 함수 안에서 선언된 모든 변수는 함수 전체에 걸쳐 유효하다는 의미로, 이 유효 범위의 적용이 변수가 선언되기 전에도 똑같이 적용되는 특징을 함수 호스팅(hoisting)이라고 한다.
  • 함수 선언문으로 정의된 함수는 자바스크립트 엔진이 스크립트가 로딩되는 시점에 바로 초기화하고 이를 VO(variable object)에 저장한다. 즉, 함수 선언, 초기화, 할당이 한번에 이루어진다. 그렇기 때문에 함수 선언의 위치와는 상관없이 소스 내 어느 곳에서든지 호출이 가능하다.
  • 자바스크립트에서는 함수 호이스팅이 자동으로 수행되지만, 항상 함수 블록의 첫 부분에 변수를 선언하는 것이 좋다.
var globalNum = 10;          // globalNum을 전역 변수로 선언

function printNum() {
    // globalNum 지역 변수가 선언만 되어 있고 아직 초기화 안 된 상태
    document.write("지역변수 globalNum 선언 전 값 " + globalNum); 
    
    // globalNum을 지역 변수로 선언하고 초기화
    var globalNum = 20;      
    document.write("지역변수 globalNum 선언 후 값 " + globalNum);
}

printNum();     // undefined 20


/* 자바스크립트 내부에서는 함수 호이스팅에 의해 다음과 같이 코드가 변경되어 처리 */

var globalNum = 10;          // globalNum을 전역 변수로 선언

function printNum() {
    // 함수 호이스팅에 의해 변수 선언 부분이 함수의 맨 처음 부분으로 이동됨
    var globalNum;  
    document.write("지역변수 globalNum 선언 전 값 "+globalNum);

    globalNum = 20;
    document.write("지역 변수 globalNum 선언 후의 값 " + globalNum);

}

printNum();
  • 함수 표현식의 경우 함수 호이스팅이 아니라 변수 호이스팅이 발생하는데, 함수 표현식은 함수 선언문과는 달리 스크립트 로딩 시점에 변수 객체에 함수를 할당하지 않고 runtime에 해석되고 실행되기 때문이다.
  • 함수 호이스팅이 함수 호출 전 반드시 함수를 선언하여야 한다는 규칙을 무시하므로 코드의 구조를 엉성하게 만들 수 있으므로 함수 표현식만을 사용할 것을 권고되어 진다. 또한 함수 선언문으로 함수를 정의하면 사용하기에 쉽지만 대규모 애플리케이션을 개발하는 경우 인터프리터가 너무 많은 코드를 변수 객체(VO)에 저장하므로 애플리케이션의 응답속도는 현저히 떨어질 수 있으므로 주의해야 할 필요가 있다.
var res = square(5); // TypeError: square is not a function

var square = function(number) {
  return number * number;
}

 

함수 객체의 프로퍼티

  • 함수는 객체이므로 함수도 프로퍼티를 가질 수 있는데, 함수는 일반 객체와는 다른 함수만의 프로퍼티를 갖는다.
function square(number) {
  return number * number;
}

square.x = 10;
square.y = 20;

console.log(square.x, square.y);
console.dir(square);

arguments 프로퍼티

  • arguments 객체는 함수 호출 시 전달된 인수(argument)들의 정보를 담고 있는 순회가능한 유사 배열 객체이며 함수 내부에서 지역변수처럼 사용된다. 즉, 함수 외부에서는 사용할 수 없다.
  • arguments 프로퍼티는 현재 일부 브라우저에서 지원하고 있지만 ES3부터 표준에서 더 이상 사용되지 않게 되었다. Function.arguments와 같은 사용 방법은 권장되지 않으며 함수 내부에서 지역변수처럼 사용할 수 있는 arguments 객체를 참조하도록 한다.
  • 매개변수(parameter)는 인수(argument)로 초기화된다. 매개변수의 갯수보다 인수를 적게 전달했을 때 인수가 전달되지 않은 매개변수는 undefined으로 초기화되고, 매개변수의 갯수보다 인수를 더 많이 전달한 경우 초과된 인수는 무시된다.
function multiply(x, y) {
  console.log(arguments);
  return x * y;
}

multiply();        // {}
multiply(1);       // { '0': 1 }
multiply(1, 2);    // { '0': 1, '1': 2 }
multiply(1, 2, 3); // { '0': 1, '1': 2, '2': 3 }
  • 자바스크립트는 함수를 호출할 때 인수들과 함께 암묵적으로 arguments 객체가 함수 내부로 전달된다. arguments 객체는 배열의 형태로 인자값 정보를 담고 있지만 실제 배열이 아닌 유사배열객체이다.
  • 유사배열객체란 length 프로퍼티를 가진 객체를 말한다. 유사배열객체는 배열이 아니므로 배열 메소드를 사용하는 경우 에러가 발생하게 된다. 따라서 배열 메소드를 사용하려면 Function.prototype.call, Function.prototype.apply를 사용하여야 하는 번거로움이 있다.
function sum() {
  if (!arguments.length) return 0;

  // arguments 객체를 배열로 변환
  var array = Array.prototype.slice.call(arguments);
  return array.reduce(function (pre, cur) {
    return pre + cur;
  });
}

// ES6
// function sum(...args) {
//   if (!args.length) return 0;
//   return args.reduce((pre, cur) => pre + cur);
// }

console.log(sum(1, 2, 3, 4, 5)); // 15

caller 프로퍼티

  • caller 프로퍼티는 자신을 호출한 함수를 의미한다.
function foo(func) {
  var res = func();
  return res;
}

function bar() {
  return 'caller : ' + bar.caller;
}

console.log(foo(bar)); // caller : function foo(func) {...}
console.log(bar());    // null (browser에서의 실행 결과)

length 프로퍼티

  • length 프로퍼티는 함수 정의 시 작성된 매개변수 갯수를 의미한다.
  • arguments.length의 값과는 다를 수 있으므로 주의하여야 한다. arguments.length는 함수 호출시 인자의 갯수이다.
function foo() {}
console.log(foo.length); // 0

function bar(x) {
  return x;
}
console.log(bar.length); // 1

function baz(x, y) {
  return x * y;
}
console.log(baz.length); // 2

name 프로퍼티

  • 함수명을 나타낸다. 기명함수의 경우 함수명을 값으로 갖고 익명함수의 경우 빈문자열을 값으로 갖는다.
// 기명 함수 표현식(named function expression)
var namedFunc = function multiply(a, b) {
  return a * b;
};

// 익명 함수 표현식(anonymous function expression)
var anonymousFunc = function(a, b) {
  return a * b;
};

console.log(namedFunc.name);     // multiply
console.log(anonymousFunc.name); // ''

__proto__ 접근자 프로퍼티

  • 모든 객체는 [[Prototype]]이라는 내부 슬롯이 있는데 이는 다른 객체에 공유 프로퍼티를 제공하는 프로토타입 객체를 가리킨다.
  • __proto__ 프로퍼티는 [[Prototype]] 내부 슬롯이 가리키는 프로토타입 객체에 접근하기 위해 사용하는 접근자 프로퍼티이다. 내부 슬롯에는 직접 접근할 수 없고 간접적인 접근 방법을 제공하는 경우에 한하여 접근할 수 있는데, [[Prototype]] 내부 슬롯에도 직접 접근할 수 없으며 __proto__ 접근자 프로퍼티를 통해 간접적으로 프로토타입 객체에 접근할 수 있다.
  • __proto__ 프로퍼티는 객체가 직접 소유하는 프로퍼티가 아니라 모든 객체의 프로토타입 객체인 Object.prototype 객체의 프로퍼티이다. 모든 객체는 상속을 통해 __proto__ 접근자 프로퍼티는 사용할 수 있다.
  • 함수도 객체이므로 __proto__ 접근자 프로퍼티를 통해 프로토타입 객체에 접근할 수 있다.
// __proto__ 접근자 프로퍼티를 통해 자신의 프로토타입 객체에 접근할 수 있다.
// 객체 리터럴로 셍성한 객체의 프로토타입 객체는 Object.prototype이다.
console.log({}.__proto__ === Object.prototype); // true

// 객체는 __proto__ 프로퍼티를 소유하지 않는다.
console.log(Object.getOwnPropertyDescriptor({}, '__proto__'));
// undefined

// __proto__ 프로퍼티는 모든 객체의 프로토타입 객체인 Object.prototype의 접근자 프로퍼티이다.
console.log(Object.getOwnPropertyDescriptor(Object.prototype, '__proto__'));
// {get: ƒ, set: ƒ, enumerable: false, configurable: true}

// 모든 객체는 Object.prototype의 접근자 프로퍼티 __proto__를 상속받아 사용할 수 있다.
console.log({}.__proto__ === Object.prototype); // true

// 함수 객체의 프로토타입 객체는 Function.prototype이다.
console.log((function() {}).__proto__ === Function.prototype); // true

prototype 프로퍼티

  • prototype 프로퍼티는 함수 객체만이 소유하는 프로퍼티로, 일반 객체에는 prototype 프로퍼티가 없다.
  • prototype 프로퍼티는 함수가 객체를 생성하는 생성자 함수로 사용될 때, 생성자 함수가 생성한 인스턴스의 프로토타입 객체를 가리킨다.
// 함수 객체는 prototype 프로퍼티를 소유한다.
console.log(Object.getOwnPropertyDescriptor(function() {}, 'prototype'));
// {value: {…}, writable: true, enumerable: false, configurable: false}

// 일반 객체는 prototype 프로퍼티를 소유하지 않는다.
console.log(Object.getOwnPropertyDescriptor({}, 'prototype'));
// undefined

 

Reference

반응형