심화

Y·2020년 8월 7일
0

자바스크립트

목록 보기
11/20

지속적으로 업데이트 중입니다.

함수 객체, 함수 객체 생성


함수 객체


ECMAScript의 함수는 함수로 동작하기 위한 추가적인 기능을 가지고 있는 일반객체의 확장판이다. 함수 객체는 다음과 같은 데이터를 내부에 저장한다.

  • 렉시컬 환경(Lexical Environment) [[Environment]]
  • 함수 코드 [[ECMAScriptCode]]
  • 함수 종류 [[FunctionKind]] (normal,classConstructor,generator,async)
  • 생성자 종류 [[ConstructorKind]] (base,derived)
  • this 참조형태 [[ThisMode]]
  • strict mode 여부 [[Strict]]
  • super 참조 [[HomeObject]]
  • 함수 실행 메서드 [[Call]] , [[Construct]]

함수 실행메서드에서 [[Call]]은 함수가 단순히 호출되었을때 함수 객체 내부에서 호출되고, new 또는 super연산자와 함께 호출되면 [[Counstruct]]가 호출된다.
[[Call]]로 호출된 객체를 callable, [[Counstruct]]로 호출된 객체를 constructor라고 한다. 자바스크립트에서 함수는 두개 모두에 해당될수도있고 아닐 수도 있다. Arrow function으로 생성된 함수는 callable하지만, 자신의 this , super, new.target을 바인딩하지 않아 non-constructor이다.
참고로 Arrow function인 경우 this키워드는 lexical을 참조한다.

함수 생성


자바스크립트에서 함수가 생성될때 다음 6가지의 정보가 사용된다.

  • 함수 생성 방식 - Normal , Arrow, Method
  • 함수의 매개변수 리스트
  • 함수 몸체
  • 스코프 (Lexical Environment)
  • strict mode 여부
  • 함수 객체의 프로토타입 - FunctionPrototype, Generator, AsyncFunctionPrototype 등

즉, 함수 생성 방식을 통해 생성자가 될 수 있는지의 여부를 따지고, strict 여부와 실제 함수 객체의 종류를 구분하고, 함수 객체의 프로토타입을 저장하고(함수자체의 프로토타입), 그 후에 스코프,파라미터,함수 몸체,this참조 방식등의 정보를 저장한다.

함수 생성 방식


function foo() {}; // Normal
const fool () => {};  // Arrow
const person = {
  sayHi() {} // Method
};

함수 생성을 구분하는 이유는 ArrowMethod인 경우 생성자로 동작하지 못하도록 방지하고 , this 참조 방식을 결정하기 위해서다.
ES6 이후 함수를 어떻게 생성하는가에 따라 생성자로 쓰일 수 있는지 없는지가 결정되며, 이는 함수 생성시 함수 할당(FunctionAllocate) 단계에서 결정된다.

함수 호출


ECMAScript은 함수 호출을 Call(F, V, [,argumentsList]) 로 표현한다. Call은 함수 객체 내부 [[Call]] 메서드를 수행하는 동작이며, F는 함수객체, V[[Call]]this,argumentsList는 전달할 인자의 배열이다.

함수 객체의 [[Call]]


함수 객체 내부 메서드인 [[Call]]은 인자로 this값과 argumentList를 받는다.
함수 호출의 과정은 다음과 같다.

  • F.[[FunctionKind]]가 classCounstructor라면 에러를 발생시킨다.
  • callerContext 는 현재 실행중인 실행 컨텍스트(running execution context)
  • calleeContext 에 새로운 실행 컨텍스트를 생성하고 지정한다. (PrepareForOrdinaryCall)
  • 새로만들어진 calleeContext가 현재의 running execution context가 된다.
  • this를 바인딩한다. (OrdinaryCallBindThis) / this가 어떤 객체를 참조할지 결정
  • 함수 코드를 수행하고 result에 결과 저장 (OrdinaryCallEvaluateBody)
  • calleeContext를 실행컨텍스트 스택에서 제거하고, callerContext를 다시 현재 실행컨텍스트로 지정
  • result 반환

OrdinaryCall


위의 함수 호출 과정에서의 단계들에대해 더 알아보자.

PrepareForOrdinaryCall


이 과정은, 결국 실행컨텍스트 스택에 새로운 실행컨텍스트를 생성하고 push하는 과정이다. 새로운 실행컨텍스트가 생성되는 시점 직후에, 렉시컬 환경이 생성된다. 따라서, 어떠한 함수가 호출될 당시의 렉시컬환경을 기억한다.

여기서 렉시컬환경이란, 자바스크립트 코드에서 변수나 함수 등의 식별자를 정의하는데 사용하는 객체이다. 렉시컬환경내부에는 식별자와 참조 값을 기록하는 Environment Recordouter라는 또 다른 렉시컬환경을 참조하는 포인터로 구성된다. outer는 외부 렉시컬환경을 참조하는 포인터이며, 이는 중첩된 코드에서 스코프탐색에 쓰인다.

  • Environment Record는 렉시컬 환경 안에 함수와 변수를 기록한다.
  • Declarative environment recordObject environment record 가 있는데,
    전자는 변수와 함수 선언을 저장하고,이 부분에서 new.target,this,super등에 대한 정보를 갖게 된다. 후자는 전역 코드에 대해서만 적용되며 전역 객체도 기록한다. 각 객체의 속성을 바인딩하기 위해 record에 새로운 엔트리를 형성한다.
  • 렉시컬환경은 this값을 결정하는 일도 수행하며, 전역 실행컨텍스트에서 this는 전역객체이다. this 바인딩은 함수 호출에 따라 결정된다.

callerContext = runningExecutionContext;
calleeContext = new ExecutionContext;
calleeContext.Function = F;


localEnv = NewFunctionEnvironment(F, newTarget);
// 이부분이 렉시컬환경이 만들어지는 과정이다.


calleeContext.LexicalEnvironment = localEnv;
calleeContext.VariableEnvironment = localEnv;

executionContextStack.push(calleeContext);
return calleeContext;

실행컨텍스트 생성과 동시에 렉시컬환경이 생성된 이후, 실행컨텍스트 스택에 들어갈 실행컨텍스트가 반환됨고 스택에 push됨을 인지하면 된다. 이젠 더이상 this바인딩이 실행컨텍스트가 아닌 렉시컬환경의 Environment Record가 담당한다.

// NewFunctionEnvironment(F, newTarget)

env = new LexicalEnvironment;
envRec = new functionEnvironmentRecord;
envRec.[[FunctionObject]] = F;

if (F.[[ThisMode]] === lexical) {
  envRec.[[ThisBindingStatus]] = 'lexical';
} else {
  envRec.[[ThisBindingStatus]] = 'uninitialized';
}

home = F.[[HomeObject]];
envRec.[[HomeObject]] = home;
envRec.[[NewTarget]] = newTarget;

env.EnvironmentRecord = envRec.
env.outer = F.[[Environment]];

return env;

위 코드는 Declarative environment record 중의 functionEnvironment record 부분이다. 여기에 함수 환경으로 this, super, new.target등의 정보를 Environment Record에 함께 초기화했다.

OrdinaryCallBindThis


함수 객체의 [[ThisMode]]에 따른 this값 참조를 결정한다.

  • [[ThisMode]]가 lexical 이면, 다른 처리를 하지 않는다. (arrow function)
  • [[ThisMode]]가 strict면, 인자로 넘어온 thisArgument를 Environment record에 설정한다.
  • [[ThisMode]]가 둘 모두 아니면, global에 있는 [[thisValue]]를 Environment record에 설정한다.

OrdinaryCallEvaluateBody


변수 선언 초기화와 코드수행 및 결과반환 단계이다. 변수 선언과 초기화는 식별자 이름과 값을 매핑한다.

나머지 매개변수와 전개문법


나머지 매개변수

함수의 인자로 들어가는 인자들의 마지막에 ...args 같이 작성하면, args라는 변수에 ...args 앞의 인자들을 제외한 모든 인자들을 모아 배열로 저장하는 기능을 한다.
주의할 점은, 나머지 매개변수는 항상 마지막에 있어야 한다.

전개문법 (spread)


함수의 인자들을 끄집어내는 과정 말고, 그 반대의 과정의 내용이다. 예를들면,

console.log( Math.max([1,2,3]) ); // NaN
console.log( Math.max(1,2,3) ); // 3

배열의 요소들을 함수의 인자로 한번에 넣고 싶을 때 전개문법을 사용하면 된다.
함수를 호출할 때, 다음과 같이 코드를 작성하면 된다.

const arr = [1,2,3];
console.log( Math.max(...arr) ); // 3

서로 다른 두 배열을 합치는것도 가능하며, 합쳐서 인자로 넣는것도 가능하다.
또, iterable 한 값들을 전개하는 것도 가능하다.

let arr1 = [1,2,3];
let arr2 = [4,5,6];

let arr3 = [...arr1,...arr2];
const str1 = "abc";
console.log(arr3) // [1,2,3,4,5,6]
console.log([...str1]) // ["a","b","c"]

[...str1]과 똑같은 기능을 하는 배열 메서드 Array.from()가 있다.
다만, Array.from()메서드는 유사배열객체를 대상으로도 사용이 가능하기 때문에, 보편적으로는 Array.from을 사용한다.

배열,객체 복사


let arr = [1,2,3];
let arrCopy = [...arr];
let obj = { 'a' : 1 , 'b':2 }
let objCopy = {...obj};
console.log(JSON.stringify(obj) === JSON.stringify(objCopy)); // true
console.log( JSON.stringify(arr) === JSON.stringify(arrCopy)); // true

본래의 배열,객체와 복사본의 배열,객체는 서로 같은 내용물을 담고있다. 하지만, 서로 같진 않으므로 복사본을 메서드로 조작하여도 원본의값에는 영향이 없다.

연결 리스트


let list = {
  value : 1,
  next  : {
    value : 2,
    next : {
      value : 3,
      next : {
        value :4
        next : null
      }
    }
  }
}

리스트에서 요소를 삽입, 삭제 할 때는 많은 비용이 들기 때문에 시간이 오래걸린다. 이를 단축하기 위한 방법으로서 연결리스트 자료구조를 사용하는 방법이 있다. arr.push,arr.pop메서드를 사용한다면, 시간이 적게 걸리지만 이는 배열 끝을 조작하는 메서드이기 때문에 앞서 말한 것처럼 특정요소를 조작하기엔 부족하다. 빠르게 삽입 , 삭제를 해야 할 경우 다음과 같은 방식을 이용하면 빠르다.

연결리스트는 다음 2개의 요소를 가진다.

  • value
  • next : 다음 연결리스트 요소를 참조한다. 다음 요소가 없을경우 null을 할당한다.

연결 리스트를 사용하면 전체 리스트를 여러 부분으로 쉽게 나눌 수 있고, 병합도 가능하다.

let secondList = list.next.next;
list.next.next = null; 
list.next.next = secondList;
profile
연세대학교 산업공학과 웹개발 JavaScript

0개의 댓글