function square(x) {
return x * x;
}
var square = function (x) {
return x * x;
};
var square = new Function("x", "return x*x");
var square = (x) => x * x;
함수 선언문으로 정의한 함수는 변수선언문과 마찬가지로 호이스팅(끌어올림)이 가능합니다.
그 외는 모두 변수를 할당한 후에 호출할 수 있습니다.
자바스크립트에서는 함수 안쪽에 또 다른 함수를 선언하는 함수를 사용할 수 있습니다.
function foo() {
function bar() {
console.log("Hello");
}
bar(); // 정상작동
}
bar(); // 에러발생!, bar is not defined
bar함수는 foo함수 내부에서만 사용될 것임을 명시적으로 나타낸다. 이는 코드를 읽어나가는데 도움이 될 수 있습니다.
function outer() {
var title = "hello";
function inner() {
console.log(title);
}
inner();
}
outer();
var s = square(5);
obj.m = function(){...};
obj.m();
var obj = new Objcet();
보통 익명 함수를 실행할 때는 변수에 함수의 참조를 할당한 후 ()를 붙여서 실행합니다.
var f = function(){...};
f();
자바스크립트에는 익명 함수를 정의한 후 곧바로 실행할 수 있는 '즉시 실행 함수'가 있습니다.
(function(){...})();
함수 정의식을 그룹연산자인 () 로 감싸줍니다. 그러면 함수정의식이 평가되고 함수 값으로 바뀝니다.
다음과 같은 방법도 사용 가능합니다.
+function(){...}()
또한 즉시 실행 함수에도 인수를 넘길 수 있습니다.
(function(a,b) {...}(1,2));
앞의 코드는 인수 a와 b에 각각 1과 2를 대입합니다.
즉시실행함수에도 이름을 붙일 수 있습니다. 다만 함수 내부에서만 유효합니다.
(function fact(n) {
if( n=<1) return 1;
return n*fact(n-1);
})(5); // 120 출력
함수의 실행결과를 변수에 할당할 수 있으며 표현식 안에서 사용가능합니다.
var x = (function(){...})();
즉시 실행함수는 전역유효범위를 오염시키지않는 이름공간을 생성할때 사용됩니다(8.7절)
자바스크립트에서는 함수를 호출할 때 인수를 생략할 수 있습니다.
함수 정의식에 작성된 인자의 개수보다 적은 인자를 전달해 함수를 실행하면 생략된 인자는 undefined가 됩니다.
function f(x, y) {
console.log("x=" + x + ", y= " + y);
}
f(2); // x = 2, y = undefined
이러한 성질을 활용해 인수를 생략할 수 있는 함수를 정의할 수 있습니다.
이를 위해서는 인수를 생략했을 때 사용할 초깃값을 설정합니다.
function multiply(a, b) {
b = b || 1; // b의 초깃값을 1로 설정
return a * b;
}
multiply(2, 3); // 6 출력
multiply(2); // 2 출력
인자 b에 값이 들어오면 논리연산자(||)에 따라 true로 평가되어 값을 사용하고, b에 값이 들어오지 않으면 b값이 undefined이므로 false로 평가되어 b값이 1이 됩니다.
가변인자함수 : 매개변수의 최대 갯수가 정해지지 않은 함수
sum(10, 20, 30, ...);
가변인자함수를 이용해 인자의 갯수 제한이 없는 함수를 만들 수 있습니다.
arguments 객체를 사용해 가변인자함수를 만들 수 있습니다.
자바스크립트 함수는 arguments 객체를 가지고 있습니다.
function sum() {
console.log('length: ', arguments.length);
console.log('index 1: ', arguments[1]);
}
sum(10, 20);
// length: 2 출력
// index 1: 20 출력
출처: https://jungpaeng.tistory.com/80 [개발자스러운 블로그]
Arguments 객체는 '유사 배열 객체'로, 10.3절을 참고합니다.
Arguments 객체는 length와 callee 프로퍼티를 갖고 있습니다.
argument.length: 인수 개수
argument.callee: 현재 실행중인 함수의 참조
ES5 strict 모드에서 arguments.callee()의 사용이 금지되었습니다.
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Functions/arguments/calleeJS 초기에는 함수표현식이 불가능해 재귀함수표현이 불가능해 도입되었지만 문제가 많이 발생.
함수가 자기 자신을 호출하는 행위를 재귀 호출 이라고 하며, 이러한 호출을 수행하는 함수를 재귀 함수 라고 합니다.
재귀함수를 정의할때는 두가지를 유의해야 합니다.
function factorial(x) {
if (x < 0) return;
if (x === 0) return 1;
return x * factorial(x - 1);
}
factorial(3);
// 6
함수가 자신을 호출하면 무한한 호출로 이어지기 때문에 중간에 멈출 수 있도록 만들어야 합니다.
위의 예제코드에서는 factorial(x)에서 factorial(x-1)을 호출합니다. 그때마다 인수 x값을 1씩 줄여나갑니다. 하지만 그다음 문장에서 x값이 0일 때 재귀호출을 멈춥니다.
function factorial(x) {
// 종료 조건
if (x < 0) return;
// 기반 조건
if (x === 0) return 1;
// 재귀
return x * factorial(x - 1);
}
factorial(3);
// 6
재귀는 중첩된 함수 호출입니다. 모든 중첩된 함수에서 가장 내부에 중첩된 함수가 가장 먼저 반환됩니다.
factorial(0) 는 1
factorial(1) 는 1 * factorial(0), 또는 1*1
factorial(2) 는 2 * factorial(1), 또는 2*1*1
factorial(3) 는 3 factorial(2), 또는 3*2*1*1
return 1 * 1 * 2 * 3
// 6
재귀함수는 멈출때까지 호출을 계속합니다. 각각의 호출된 재귀 함수는 메모리의 다른 영역을 사용하기 때문에 while문이나 for 문으로 대체하는 것이 이해도 쉽고 메모리 공간도 적게 차지합니다.
자바스크립트 엔진은 실행 가능한 코드(전역코드, 함수코드, eval코드)를 만나면 코드를 평가하고 실행 문맥으로 만들어줍니다.
전역 코드: Window 아래에 정의된 함수
함수 코드: 문자 그대로 함수
eval 코드: eval 함수(별도의 동적환경에서 작동하기에 설명 제외)
// 1. 실행 문맥의 구조
Execution Context = {
// 1-1. 렉시컬 환경 컴포넌트
Lexical Environment Component : {
// A. 환경 레코드
Environment Record : {
// ㄱ. 선언적 환경 레코드
Declarative Environment Record
// ㄴ. 객체 환경 레코드
Object Environment Record
},
// B. 외부 렉시컬 환경 참조
Outer Lexical Environment Reference
},
// 1-2. 변수 환경 컴포넌트
Variable Environment Component
// 1-3. 디스 바인딩 컴포넌트
This Binding Component
}
실행문맥은 실행가능한 코드가 실행되고 관리되는 영역으로, 컴포넌트 여러개가 정보를 나누어 관리합니다.
렉시컬 환경 컴포넌트 & 변수 환경 컴포넌트
디스 바인딩 컴포넌트
환경 레코드와 외부 렉시컬 환경 참조 컴포넌트로 구성되어 있습니다.
환경 레코드: 유효 범위 안의 식별자와 결과값이 묶여 기록되는 곳
외부 렉시컬 환경 컴포넌트: 중첩된 함수의 바깥 코드에 정의된 변수를 읽거나 쓰기 위해 저장되는 곳
웹브라우저에 내장된 자바스크립트는 새로운 웹페이지를 읽어들인 후
렉시컬환경타입의 전역환경을 생성합니다. 그리고 전역객체를 생성한 후 전역환경의 환경레코드에 전역객체의 참조를 대입합니다.
실행문맥은 스택 이라는 구조로 관리됩니다.
이미지
자바스크립트는 싱글 스레드 방식입니다.
호출 스택에 쌓인 실행문맥(함수 또는 코드)을 위에서부터 차례차례 실행
실행 문맥 생성&호출스택에 push -> 렉시컬환경컴포넌트 생성
렉시컬환경컴포넌트에서 환경레코드 생성->함수 안팎의 환경을 기록:
실행문맥에 있는 디스 바인딩 컴포넌트에 그 함수를 호출한 객체의 참조를 저장하고 이것으로 this를 결정. this는 호출상황에 따라 가리키는 객체가 변화
함수 안의 코드가 순서대로 실행
함수가 종료되면 실행문맥과 함께 그 안의 렉시컬환경컴포넌트는 사라짐
but 함수의 참조가 환경 레코드에 유지되는 경우 렉시컬환경컴포넌트가 유지 => 클로저에서 확인
외부함수가 소멸된 이후에도 내부함수가 외부함수에 접근할 수 있습니다.
function outer() {
var title = "hello";
return function () {
console.log(title);
};
}
inner = outer();
inner();
클로저란?
함수와 그 함수가 선언됐을 때의 렉시컬 환경(Lexical environment)과의 조합이다.
function outerFunc() {
var x = 10;
var innerFunc = function () {
console.log(x);
};
innerFunc();
}
outerFunc(); // 10
스코프는 함수를 호출할 때가 아니라 함수를 어디에 선언하였는지에 따라 결정됩니다. 이를 렉시컬 스코핑(Lexical scoping)라 합니다.
위 예제의 함수 innerFunc는 함수 outerFunc의 내부에서 선언되었기 때문에 함수 innerFunc의 상위 스코프는 함수 outerFunc입니다.
innerFunc 함수 스코프(함수 자신의 스코프를 가리키는 활성 객체) 내에서 변수 x를 검색한다. 검색이 실패하였다.
innerFunc 함수를 포함하는 외부 함수 outerFunc의 스코프(함수 outerFunc의 스코프를 가리키는 함수 outerFunc의 활성 객체)에서 변수 x를 검색한다. 검색이 성공하였다.
function outerFunc() {
var x = 10;
var innerFunc = function () {
console.log(x);
};
return innerFunc;
}
//함수 outerFunc를 호출하면 내부 함수 innerFunc가 반환된다.
//그리고 함수 outerFunc의 실행 컨텍스트는 소멸한다.
var inner = outerFunc();
inner(); // 10
이처럼 자신을 포함한 외부함수보다 내부함수가 더 오래 유지되는 경우, 외부 함수 밖에서 내부함수가 호출되더라도 외부함수의 지역변수에 접근할 수 있는 경우를 클로저 라고 합니다.
다시 위에서 말한 정의를 보면,
함수와 그 함수가 선언됐을 때의 렉시컬 환경(Lexical environment)과의 조합이다.
여기서 말하는 함수란 내부함수를 의미하며, "그 함수가 선언됐을 때의 렉시컬 환경" 이란 내부 함수가 선언됐을 때의 스코프를 의미합니다.
즉,
클로저는 반환된 내부함수가 자신이 선언됐을 때의 환경(Lexical environment)인 스코프를 기억하여 자신이 선언됐을 때의 환경(스코프) 밖에서 호출되어도 그 환경(스코프)에 접근할 수 있는 함수를 말합니다. 이를 조금 더 간단히 말하면 클로저는 자신이 생성될 때의 환경(Lexical environment)을 기억하는 함수라고 할 수 있습니다.
다른 분들이 한 정리:
https://github.com/mojaeya/js-study-sunday/blob/main/1.%20Study/5%EC%A3%BC%EC%B0%A8/mojaeya.md
https://github.com/mojaeya/js-study-sunday/blob/main/1.%20Study/5%EC%A3%BC%EC%B0%A8/jaewY.md