일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- indexOf()
- @Moditying @Query
- 객체의키값만 찾기
- lastIndexOf()
- sql 문자열 패턴 검색
- 프론트엔드 스쿨
- 리엑트블로거
- 코딩 어?
- 스프링 데이타 JPA
- ubuntu타임존
- Robo3T 글씨체 변경
- 가상컴퓨터마법사
- 시퀄 문법
- search()
- js 문자열을 문자배열로
- ...점점점문법
- 배열을 객체로
- 깃 토큰 만료
- 5.3.8 Modifying Queries
- 레디스 확인
- ${변수}
- 문자열 인터폴레이션
- 객체의 밸류값만 찾기
- Robo3T 폰트변경
- sql like연산자
- Robo3T 폰트 키우기
- 객체를 배열로
- findIndex()
- 우분투 시간 변경
- Robo3T 글씨키우기
- Today
- Total
코딩기록
JS)모던 자바스크립트 Deep Dive 12장 함수 본문
12장 함수
12-1. 함수란?
- 함수는 일련의 과정을 문(statement)으로 구현하고 코드 블록으로 감싸서 하나의 실행 단위로 정의한 것.
- 함수 내부로 입력을 전달받는 변수: 매개변수(parameter), 입력: 인수(argument인자), 출력: 반환값(return value)
- 함수는 값이며, 여러 개 존재할 수 있으므로 특정 함수를 구별하기 위해 식별자인 함수 이름을 사용.
- 함수 정의(function definition)를 통해 생성.
- 함수 호출(function call/invoke): 인수를 매개변수를 통해 함수에 전달하면서 함수의 실행을 명시적으로 지시함.
- 함수를 호출하면 코드 블록에 담길 문들이 일괄적으로 실행되고, 실행 결과, 즉 반환값을 반환함.
-
// 함수 호출var result = add(2, 5);// 함수 add에 인수 2, 5를 전달하면서 호출하면 반환값 7을 반환.console.log(result); // 7
12-2. 함수를 사용하는 이유
- 실행 시점을 개발자가 결정할 수 있고 몇 번이든 재사용이 가능, 필요할 때 여러 번 호출 가능.
- 동일한 작업을 반복적으로 수행할 시 미리 정의된 함수를 재사용해 코드의 재사용이라는 측면에서 유용.
- 함수를 사용하지 않고 코드를 중복해서 작성하면 그 코드를 수정할 때 중복된 횟수만큼 코드를 수정해야 하고 수정에 걸리는 시간이 증가하고 실수할 가능성도 높아짐.
- 코드의 중복을 억제하고 재사용성을 높이는 함수는 유지보수의 편의성을 높이고 실수를 줄여 코드의 신뢰성을 높이는 효과가 있음.
- 함수는 객체 타입의 값이라 식별자를 붙일 수 있음. 적절한 함수 이름은 내부 코드를 이해하지 않고도 함수의 역할을 파악할 수 있어 코드의 가독성을 향상함.
- ⭐코드는 개발자를 위한 문서이기도 해서 사람이 이해할 수 있는 코드, 즉 가독성이 좋은 코드가 좋은 코드임.
12-3. 함수 리터럴
- 자바스크립트의 함수는 객체 타입의 값.(타 언어와 구분되는 자바스크립트의 중요한 특징)
- 18장 함수와 일급객체에서 자세히...
- 함수 리터럴은 function 키워드, 함수 이름, 매개변수 목록, 함수 몸체로 구성.
-
// 변수에 함수 리터럴을 할당var f = function add(x, y) {return x + y;};
- 함수 리터럴의 구성 요소
- 리터럴은 값을 생성하기 위한 표기법, 따라서 함수 리터럴도 평가되어 값을 생성하며, 이 값은 객체. 즉, 함수는 객체.
- 일반 객체와는 달리 함수 객체는 호출 가능.
- 함수 객체만의 고유한 프로퍼티를 가짐.
12-4. 함수 정의
- 함수 정의란 함수를 호출하기 이전에 인수를 전달받을 매개변수와 실행할 문들, 그리고 반환할 값을 지정하는 것.
- 정의된 함수는 자바스크립트 엔진에 의해 평가되면 식별자가 암묵적으로 생성되고 함수 객체가 할당됨.
1). 함수 선언문
- 함수 선언문을 통한 함수 정의
-
// 함수 선언문function add(x, y) {return x + y;};// 함수 참조// console.dir은 console.log와 달리 함수 객체의 프로퍼티까지 출력.// 단, Node.js 환경에서는 console.log와 같은 결과가 출력.console.dir(add); // [Function: add]console.log(add(2,5)); // 7
- 함수 선언문은 함수 리터럴과 형태가 동일하나 함수 이름은 생략 불가.
- 함수 선언문은 표현식이 아닌 문.
- 크롬 개발자 도구의 콘솔에서 완료 값 undefined가 출력됨.
- 표현식이 아닌 문은 변수에 할당할 수 없다, 함수 선언문은 변수에 할당할 수 없지만 아래 예제는 선언문이 변수에 할당되는 것처럼 보임.
-
// 함수 선언문은 표현식이 아닌 문으로 변수에 할당할 수 없음.// 하지만 함수 선언문이 변수에 할당되는 것처럼 보임.var add = function add(x, y) {return x + y;};// 함수 호출console.log(add(2, 5)); // 7
- 자바스크립트 엔진은 함수 이름이 있는 함수 리터럴을 단독으로 사용(값으로 평가되어야 하는 문맥에서 함수 리터럴을 사용하지 않는 경우, 다시 말해 함수 리터럴을 피연산자로 사용하지 않는 경우)하면 함수 선언문으로 해석하고,
- 함수 리터럴이 값으로 평가되어야 하는 문맥, 예를 들어 함수 리터럴을 변수에 할당하거나 피연산자로 사용하면 함수 리터럴 표현식으로 해석.
-
// 기명 함수 리터럴을 단독으로 사용하면 함수 선언문으로 해석.// 함수 선언문에서는 함수 이름 생략 불가.function foo() { console.log('foo'); }foo(); // foo// 함수 리터럴을 피연산자로 사용하면 함수 선언문이 아니라 함수 리터럴 표현식으로 해석.// 함수 리터럴에서는 함수 이름을 생략할 수 있음.(function bar() { console.log('bar'); });bar(); // ReferenceError: bar is not defined
-
✅ bar() 함수를 호출할 수 없는❌ 이유
- 그룹 연산자 () 내에 있는 함수 리터럴(bar)은 함수 선언문으로 해석되지 않고 함수 리터럴 표현식으로 해석됨.
- 그룹 연산자의 피연산자는 값으로 평가될 수 있는 표현식이어야 하므로 표현식이 아닌 문인 함수 선언문은 피연산자로 사용할 수 없음.
- 기명 함수 리터럴은 코드의 문맥에 따라 함수 선언문 또는 함수 리터럴 표현식을 해석됨.
- 함수이름은 함수 몸체 내에서만 참조할 수 있는 식별자.
- 이는 함수 몸체 외부에선 함수 이름으로 함수를 참조할 수 없으므로 함수 몸체 외부에서는 함수 이름으로 함수를 호출할 수 없다는 뜻. 즉, 함수를 가리키는 식별자가 없다는 것과 마찬가지. 결론적으로 bar() 함수는 호출 불가.
✅foo() 함수를 호출할 수 있는⭕ 이유
-
// 기명 함수 리터럴을 단독으로 사용하면 함수 선언문으로 해석.// 함수 선언문에서는 함수 이름 생략 불가.function foo() { console.log('foo'); }foo(); // foo
- foo는 함수 몸체 내부에서만 유효한 식별자인 함수 이름이므로 foo로 함수를 호출할 수 없어야 함.
- foo라는 이름으로 함수를 호출하려면 foo는 함수 이름이 아니라 함수 객체를 가리키는 식별자여야 하지만 foo를 선언한 적도 없고 할당한 적도 없는데 위 예제에서 foo 함수를 호출함.
- 이유는 foo는 자바스크립트 엔진이 암묵적으로 생성한 식별자이기 때문.
- 자바스크립트 엔진은 함수 선언문을 해석해 함수 객체를 생성함.
- 함수 이름은 함수 몸체 내부에서만 유효한 식별자이므로 함수 이름과는 별도로 생성된 함수 객체를 가리키는 식별자가 필요함. 식별자가 없으면 생성된 함수 객체를 참조할 수 없으므로 호출할 수도 없음.
- 따라서 자바스크립트 엔진은 생성된 함수를 호출하기 위해 함수 이름과 동일한 이름의 식별자를 암묵적으로 생성하고, 거기에 함수 객체를 할당함.
- 이유는 foo는 자바스크립트 엔진이 암묵적으로 생성한 식별자이기 때문.
- 의사 코드로 적힌 함수 표현식 ⬆⬆
- 함수는 함수 이름으로 호출하는 것이 아니라 함수 객체를 가리키는 식별자로 호출.
- 즉, 함수 선언문으로 생성한 함수를 호출한 것은 함수 이름 add가 아니라 자바스크립트 엔진이 암묵적으로 생성한 식별자 add임.
- 자바스크립트 엔진이 함수 선언문을 함수 표현식으로 변환해 함수 객체를 생성한다고 생각할 수 있지만 함수 선언문과 함수 표현식이 정확히 동일하게 동작하는 것은 아님.
2). 함수 표현식
- 자바스크립트의 함수는 값처럼 변수에 할당할 수도 있고 프로퍼티 값이 될 수도 있으며 배열의 요소가 될 수도 있음.
- 이처럼 값의 성질을 갖는 객체를 일급 객체라 함.
- 함수 리터럴로 생성한 함수 객체를 변수에 할당할 수 있는데 이러한 함수 정의 방식을 함수 표현식이라 함.
- 함수 선언문은 '표현식이 아닌 문', 함수 표현식은 '표현식인 문'.
- 유사하게 동작하는 것처럼 보이지만 정확히 동일하게 동작하진 않음.
====함수 이름은 재귀를 하기 위해 필요함===
function foo로 호출하면 레퍼런스에러가 나오는 이유는 함수는 스코프 내에서만 함수를 호출할 수 있기 때문.
예제 12-11처럼 함수를 만드는 건 좋은 방법이 아님.
함수이름도 식별자임.
**호이스팅 변수 선언문이 런타임 이전에 실행된다.-
변수 호이스팅은 절대 쓰면 안 됨.
함수 선언문으로 만들 때 - 함수 호이스팅은 써도 되는 분위기. 밑에 함수 깔아 놓고 위에서 부름, 가독성이 좋다고 판단되면 써도 됨.
면접 때 호이스팅 물어보는 이유- 안 써야 하는 거라서,
변수를 선언하기 전에 참조하지 말라고.
호이스팅은 안 좋은 문법이라서 안 쓰려고 물어봄
함수 표현식으로 함수 만들 때 변수 호이스팅이 발생. -함수 표현식 많이 안 씀 ,
화살표 함수는 함수 표현식의 일종이라 함수 표현식을 써야 하면 화살표 함수를 쓰는 것을 권장.
문법 배우는 이유
원시값과 객체가 다르게 동작하는 이유.
그때의 상황과 지금의 상황이 같을까?
그때는 컴퓨터 성능이 안 좋았음.
알고리즘: 인플레이스- 함수로 인수로 뭔가를 받을 때 변수로 만들어진 메모리 공간을 아끼려고
인수로 리턴값을 담는 인수를 줌.
요즘은 메모리 값이 쌈, 그래서 인건비를 줄이는 게 더 이득.
그래서 현대는 객체도 원시값처럼 쓰려고 함
알고리즘 교과서 20년 전, 지금 똑같음.
함수로 매개변수로 객체를 전달받았을 때
변수에 담긴
원본 객체를 변경시키지 않고 쓰기 위해서 카피가 따라다닌다.
그래서, 얕은 복사, 깊은 복사를 씀.
배열에서 영향을 줌.
배열도 객체.
문법을 배움과 동시에 어떤 코드가 읽기 좋은 코드냐에 대한 얘기를 함.
써야 하는 문법과 쓰지 말아야 하는 문법을 구분하는 문법을 갖춤으로써.
이 책을 통해서
자바스크립트 핵심가이드 더글라스 클라코드
delete 쓰지 말라는 이유는 객체 바꾸지 말라는 의미에서.
3). 함수 생성 시점과 함수 호이스팅
✅함수 호이스팅과 변수 호이스팅의 차이
- var 키워드를 사용한 변수를 선언문과 함수 선언문은 런타임 이전에 자바스크립트 엔진에 의해 먼저 실행되어 식별자를 생성한다는 점은 동일.
- var 키워드로 선언된 변수
- undefined로 초기화
- 함수 선언문을 통해 암묵적으로 생성된 식별자
- 함수 객체로 초기화 (함수 호이스팅에 의해 호출이 가능)
- var 키워드로 선언된 변수
- 따라서 var 키워드를 사용한 변수 선언문 이전에 변수를 참조하면 변수 호이스팅에 의해 undefined로 평가되지만 함수 선언문으로 정의한 함수를 함수 선언문 이전에 호출하면 함수 호이스팅에 의해 호출이 가능.
- 함수 표현식은 변수에 할당되는 값이 함수 리터럴인 문
- 변수 할당문의 값은 할당문이 실행되는 시점, 즉 런타임에 평가되므로 함수 표현식의 함수 리터럴도 할당문이 실행되는 시점에 평가되어 함수 객체가 됨.
- 따라서 함수 표현식으로 함수를 정의하면 함수 호이스팅이 발생하는 것이 아니라 변수 호이스팅이 발생.
- 함수 표현식 이전에 함수 참조 시 undefined로 평가되어(undefined를 호출하는 것과 같음) 타입에러 TypeError 발생.
- 함수 표현식으로 정의한 함수는 반드시 함수 표현식 이후에 참조 또는 호출해야 함.
- 하지만 이런 규칙을 무시해 함수 선언문 대신 함수 표현식을 권장하기도 함(더글라스 크락포-JSON창안)
- 4). Function 생성자 함수
- 자바스크립트가 기본 제공하는 빌트인 함수.
- Function함수에 매개변수 목록과 함수 몸체를 문자열로 전달하면서 new 연산자와 함께 호출하면 함수 객체를 생성해서 반환.(new 없이 호출해도 동일) [예제 12-13]
-
7장 생성자 함수에 의한 객체 생성에서 자세히... -
- Function 생성자 함수로 함수를 생성하는 방식은 일반적이지 않으며 바람직하지도 않음.
- Function 생성자 함수로 생성한 함수는 클로저를 생성하지 않고 함수 선언문이나 함수 표현식으로 생성한 함수와 다르게 동작함. [예제 12-14]
5). 화살표 함수
- ES6에서 도입.
- function 키워드 대신 화살표=>를 사용해 좀 더 간략한 방법으로 함수를 선언.(내부 동작까지 간략화)
- 화살표 함수는 항상 익명 함수로 정의.
-
// 화살표 함수const add = (x, y) => x + y;console.log(add(2, 5)); //7
- 생성자 함수로 사용할 수 없으며, 기존 함수가 this 바인딩 방식이 다름.
- prototype 프로퍼티가 없고 arguments 객체를 생성하지 않음.
- 26.3절 화살표 함수에서 자세히...
12-5. 함수 호출
- 함수를 가리키는 식별자와 한 쌍의 소괄호인() 함수 호출 연산자로 호출.
- 함수 호출하면 현재의 실행 흐름을 중단하고 호출된 함수로 실행 흐름을 옮김.
- 이때 매개변수에 인수가 순서대로 할당되고 함수 몸체의 문들이 실행됨.
1). 매개변수와 인수
- 함수를 실행하기 위해 필요한 값을 함수 외부에서 함수 내부로 전달할 필요가 있는 경우, 매개변수(parameter인자)를 통해 인수(argument)를 전달.
-
// 함수 선언문function add(x, y) {return x + y;}// 함수 호출// 인수 1과 2가 매개변수 x와 y에 순서대로 할당되고 함수 몸체의 문들이 실행.var result = add(l, 2);
- 인수
- 값으로 평가될 수 있는 표현식이어야 함.
- 함수를 호출할 때 지정하며, 개수와 타입에 제한이 없음.
- 매개변수
- 함수를 정의할 때 선언하며, 함수 몸체 내부에서 변수와 동일하게 취급.
- 즉, 함수가 호출되면 함수 몸체 내에서 암묵적으로 매개변수가 생성되고 일반 변수와 마찬가지로 undefined로 초기화된 이후 인수가 순서대로 할당.
- 함수가 호출될 때 매개변수의 단계 그림 12-9
- 매개변수의 스코프(유효 범위)는 함수 내부. 내부에서만 참조 가능.
- 13장 스코프에서 자세히...
- 인수
- 함수는 매개변수의 개수와 인수의 개수가 일치하는지 체크하지 않음.
- 인수가 부족해서 인수가 할당되지 않은 매개변수의 값은 undefined이고 에러가 발생하지 않음. NaN반환.
-
function add(x, y) {return x + y;}console.log(add(2)); // NaNconsole.log(add(2, 5, 10)); // 7
- 매개변수보다 인수가 더 많은 경우 초과된 인수는 무시됨. 그냥 버려지는 것이 아니라 모든 인수는 암묵적으로 arguments 객체의 프로퍼티로 보관됨.
- arguments 객체는 함수 정의 시, 매개변수 개수를 확정할 수 없는 가변 인자 함수 구현 시 유용함
- 18.2.1절 arguments 프로퍼티에서 자세히...
덧) 스터디에서 나온 이런저런 내용 240717수-
* Q: js에서 프로퍼티 정의는?
속성 x 자산.
속성은 어트리뷰트가 옳다. html 어트리뷰트, js 프로퍼티 구분해서 사용하길 권장.
프로퍼티 - 어떤 객체가 가지고 있는 자산.
객체지향에 추상화, 캡슐화가 있음.
캡슐화- 관련 있는 데이터를 모으는 이유는?
데이타군을 어딘가로 보낼 때, 함수 내부로 혹은 리턴할 때 필요함.
정보은닉(js-클로저), 캡슐화되어 있는 프로퍼티, 메서드를 외부에 공개할 것이냐 말 것이냐.
Js는 모든 프로퍼티가 공개되어 있다.
클래스에 프라이빗이 추가됨.
클래스 기반 객체지향, js객체지향-프로토타입객체지향
*함수를 가지고 있는 프로퍼티는 메서드.
객체를 생성할 때 메서드를 만들어야 하냐? => 만들지 않는 게 좋다.
그러면 객체는 순수한 데이터로만 이루어진 readOnly(리드온니)가 최적의 상태.
메서드를 쓰는 순간 상속을 구현해야 한다. 그 이유는 프로토타입에서 배울 예정.
객체 리터럴로 만든 객체에 리터럴을 추가하면 그 객체는 한 번만 사용한다는 걸 뜻 함.
그럴 경우 메서드는 오버 엔지니어링임.
*일급 객체는 '완전한 객체다'라는 의미. 이 급, 삼 급이 따로 있지 않음.
*그림 12-9의 인수는 호출 연산자- 호출대상이 되는 객체는 콜러블(호출할 수 있다).
add(2, 5);에서 add는 함수로 평가되는 식별자여야 함.
함수에서 매개변수가 젤 중한데 매개변수는 함수 몸체와 함수 외부를 연결.
인수는 함수 외부에 있었던 것.
*사용자들이 에러를 보는 것보다 개발자들이 보는 게 나아서 최근 추세는 개발자들이 적극적으로 에러를 발생시킨다. 여러 단계의 방어막으로 에러를 막아야 함. 정적테스트, 유닛테스트, 통합테스트 등. 3,4단계로.
2). 인수 확인
- 자바스크립트 함수는 매개변수와 인수 개수가 일치하는지 확인하지 않음.
- 자바스크립트는 동적 타입 언어이기 때문에 매개변수의 타입을 사전에 지정할 수 없음.
- 따라서 자바스크립트의 경우 함수를 정의할 때 적정한 인수가 전달되었는지 확인할 필요가 있음.
-
}console.log(add(2)); // NaNconsole.log(add('a', 'b')); // 'ab'
- return x + y;
- function add(x, y) {
**변경된 코드**
- 위 코드처럼 함수 내부에서 적절한 인수가 전달되었는지 확인하더라도 부적절한 호출을 사전에 방지할 수는 없고 에러는 런타임에 발생.
- 타입스트립트와 같은 정적 타입을 도입해서 컴파일 시점에 부적저한 호출을 방지할 수 있게 하는 것도 하나의 방법.
- ES5에서 도입된 매개변수 기본값을 사용
- 함수 내에서 수행하던 인수 체크 및 초기화를 간소화할 수 있음.
- 매개변수 기본값은 매개변수에 인수를 전달하지 않았을 경우와 undefined를 전달하는 경우에만 유효.
3). 매개변수의 최대 개수
- ECMAScript 사양에서는 매개변수의 최대 개수에 대해 명시적으로 제한하고 있지 않음.
- 매개변수는 순서에 의미가 있음.
- 함수의 매개변수의 이상적인 개수는 0개이며 적을수록 좋음.
- 매개변수가 많다는 것은 함수가 여러 가지 일을 한다는 증거이므로 바람직하지 않음.
- 이상적인 함수는 한 가지 일만 해야 하며 가급적 작게 만들어야 함.
- 매개변수는 최대 3개 이상을 넘지 않는 것을 권장.
- 그 이상 필요시 하나의 매개변수를 선언하고 객체로 인수를 전달하는 것이 유리.
- 객체를 인수로 사용하는 경우
- 프로퍼티 키만 정확히 지정하면 매개변수의 순서를 신경 쓰지 않아도 됨.
- 명시적으로 인수의 의미를 설명하는 프로퍼티 키를 사용해 코드의 가독성이 좋아지고 실수도 줄어듦.
- 주의할 것
- 함수 외부에서 함수 내부로 전달한 객체를 함수 내부에서 변경하면 함수 외부의 객체가 변경되는 부수 효과가 발생함
- 12.6절 참조에 의한 전달과 외부 상태의 변경에서 자세히...
4). 반환문
- 함수는 return 키워드와 표현식(반환값)으로 이뤄진 반환문을 사용해 실행 결과를 함수 외부로 반환(return)할 수 있음.
- 함수 호출 표현식은 return 키워드가 반환한 표현식의 평가 결과, 즉 반환값으로 평가된다.
- 반환문의 2가지 역할
- 반환문은 함수의 실행을 중단하고 함수 몸체를 빠져나감.
- 따라서 반환문 이후에 다른 문이 존재하면 그 문은 실행되지 않고 무시.
-
- 반환문은 return 키워드 뒤에 오는 표현식을 평가해 반환.
- return 키워드 뒤에 반환값으로 사용할 표현식을 명시적으로 지정하지 않으면 undefined가 반환됨.
- 반환문은 생략 가능. 함수는 함수 몸체의 마지막문까지 실행한 후 암묵적을 로 undefined를 반환.
- return 키워드와 반환값으로 사용할 표현식 사이에 줄 바꿈이 있으면 세미콜론 자동 삽입 기능(ASI)에 의해 세미콜론이 추가됨.
- 반환문은 함수 몸체 내부에서만 사용 가능하며 전역에서 사용 시 문법 에러가 발생.
- ❗❗Node.js
- 모듈 시스템에 의해 파일별로 독립적인 파일 스코프를 가짐.
- 파일의 가장 바깥 영역에 반환문을 사용해도 에러 발생하지 않음.
- 반환문은 함수의 실행을 중단하고 함수 몸체를 빠져나감.
12-6. 참조에 의한 전달과 외부 상태의 변경
원본을 훼손하지 않는 방향으로 코딩해야 함.
- 원시 값은 값에 의한 전달, 객체는 참조에 의한 전달 방식으로 나뉘듯,
- 매개변수도 함수 몸체 내부에서 변수와 동일하게 취급되므로 매개변수 또한 타입에 따라 값에 의한 전달, 참조에 의한 전달 방식을 그대로 따름.
- 원시 타입 인수를 전달받은 매개변수
- 원시 값은 변경 불가능한 값(im-mutable value)이므로 직접 변경할 수 없기 때문에 재할당을 통해 할당된 원시 값을 새로운 원시 값으로 교체.
- 값 자체가 복사되어 매개변수에 전달되기 때문에 함수 몸체에서 그 값을 변경(재할당을 통한 교체) 해도 원본은 훼손되지 않음. 원본이 변경되는 부수 효과 없음.
- 객체 타입 인수를 전달받은 매개변수
- 객체는 변경 가능한 값(mutable value)이므로 직접 변경할 수 있기 때문에 재할당 없이 직접 할당된 객체를 변경.
- 함수 외부에서 함수 몸체 내부로 전달함 참조 값에 의해 원본 객체가 변경되는 부수 효과 발생.
- 이처럼 함수가 외부 상태를 변경하면 상태 변화를 추적하기 어려워지는데 이는 코드의 복잡성을 증가시키고 가동성을 해치는 원인이 됨.
- 객체를 불변 객체로 만들어 사용하면 문제 해결 가능.
- 객체 복사본을 새롭게 생성하는 비용은 들지만 객체를 마치 원시 값처럼 변경 불가능한 값으로 동작하게 만듦.
- 이를 통해 객체의 상태 변경을 원천봉쇄하고 객체의 상태 변경이 필요한 경우에는 객체의 방어적 복사를 통해 원본 객체를 완전히 복제, 즉 깊은 복사를 통해 새로운 객체를 생성하고 재할당을 통해 교체함.
- 이를 통해 외부 상태가 변경되는 부수 효과를 없앨 수 있음.
- 이를 통해 객체의 상태 변경을 원천봉쇄하고 객체의 상태 변경이 필요한 경우에는 객체의 방어적 복사를 통해 원본 객체를 완전히 복제, 즉 깊은 복사를 통해 새로운 객체를 생성하고 재할당을 통해 교체함.
- 객체 복사본을 새롭게 생성하는 비용은 들지만 객체를 마치 원시 값처럼 변경 불가능한 값으로 동작하게 만듦.
- 객체를 불변 객체로 만들어 사용하면 문제 해결 가능.
- 원시 타입 인수를 전달받은 매개변수
12-7. 다양한 함수의 형태
1). 즉시 실행 함수
- 즉시 실행 함수도 일반 함수처럼 값을 반환할 수 있고 인수를 전달할 수도 있음.
-
// 즉시 실행 함수도 일반 함수처럼 값을 반환할 수 있음.var res = (function foo() {var a = 3;var b = 5;return a * b;}());console.log(res); // 15foo();// 즉시 실행 함수도 일반 함수처럼 인수를 전달할 수 있음.res = (function (a, b) {return a * b;}(3, 5));console.log(res); // 15
- 즉시 실행 함수 내에 코드를 모아 두면, 변수나 함수 이름 충돌 방지.
- 14.3절 전역 변수의 사용을 억제하는 방법에서 자세히...
- 함수 정의와 동시에 즉시 호출되는 함수를 즉시 실행 함수라고 함.
- 즉시 실행 함수는 단 한 번만 호출되며 다시 호출할 수 없음.
- 익명 함수가 일반적이나 기명 즉시 실행 함수도 사용할 수 있음.
-
// 기명 즉시 실행 함수(function foo() {var a = 3;var b = 5;return a * b;}());foo();
- 그룹 연산자 (...) 내의 기명 함수는 함수 선언문이 아니라 함수 리터럴로 평가되며, 함수 이름은 함수 몸체에서만 참조할 수 있는 식별자이므로 즉시 실행 함수를 다시 호출할 수는 없음.
- 반드시 그룹 연산자 ()로 감싸야함. 그렇지 않으면 SyntaxError 발생.
- 그룹 연산자로 함수를 묶는 이유는 먼저 함수 리터럴을 평가해서 함수 객체를 생성하기 위해서임.
- 먼저 함수 리터럴을 평가해서 함수 객체를 생성하는 다른 방법 4가지.
- 먼저 함수 리터럴을 평가해서 함수 객체를 생성하는 다른 방법 4가지.
2). 재귀 함수
- 함수가 자기 자신을 호출하는 것.
- 반복되는 처리를 반복문 없이 구현.
-
// 10 ~ 0까지 출력function countdown(n) {if (n < 0) return;console.log(n);countdown(n - 1); // 재귀 호출}countdown(10);
-
// 팩토리얼(계승)은 1부터 자신까지의 모든 양의 정수의 곱.// n! = 1 * 2 *... * (n-1) * nfunction factorial(n) {// 탈출 조건: n이 1 이하일 때 재귀 호출을 멈춤.if (n <= 1) return 1;// 재귀 호출return n * factorial(n - 1); // 재귀 호출}console.log(factorial(5)); // 5! = 5 * 4 * 3 * 2 * 1 = 120
- factorial 함수 내부에서 자신을 호출하는 식별자 factorial은 함수 이름.
- 함수 외부에서 함수를 호출할 때는 반드시 함수를 가리키는 식별자로 해야 함.
-
- 재귀 호출을 멈출 수 있는 탈출 조건이 반드시 있어야 함, 없으면 무한 재귀 호출되어 스택 오버플로 에러가 발생.
- 따라서 재귀함수를 사용하는 편이 직관적으로 이해하기 쉬울 때만 한정적을 사용하는 것이 바람직.
- 대부분의 재귀함수는 for문이나 while문으로 구현 가능.
3). 중첩 함수
- 함수 내부에 정의된 함수 또는 내부 함수라 함.
- 중첩 함수를 포함하는 함수는 외부 함수. 외부 함수 내부에서만 호출 가능.
- 자신을 포함하는 외부 함수를 돕는 헬퍼 함수 역할.
- 스코프와 클로저에 관련 있음.
- ES6 이전: 코드의 최상위 또는 다른 함수 내부에서만 정의.
- ES6 이후: 함수 정의는 문이 위치할 수 있는 문맥이라면 어디든 가능.
- 단, 호이스팅으로 인한 혼란이 발생할 수 있으므로 if문, for문 등의 코드 블록에서 함수 선언문을 통해 함수를 정의하는 것은 바람직하지 않음.
4). 콜백 함수
-
[예제 12-51]// 외부에서 전달받은 f를 n만큼 반복 호출.function repeat(n, f) {for (var i = 0; i < n; i++) {f(i); // i를 전달하면서 f를 호출}}var logAll = function (i) {console.log(i);};// 반복 호출할 함수를 인수로 전달.repeat(5, logAll); // 0 1 2 3 4var logOdds = function (i) {if (i % 2) console.log(i);};// 반복 호출할 함수를 인수로 전달.repeat(5, logOdds); // 1 3
- 콜백함수는 함수의 매개변수를 통해 다른 함수의 내부로 전달되는 함수.
- 고차함수란 매개변수를 통해 함수의 외부에서 콜백 함수를 전달받은 함수, 반환값으로 함수를 반환하는 함수.
- 콜백함수는 고차 함수에 전달되어 헬퍼 함수의 역할을 함.
- 함수 외부에서 고차 함수 내부로 주입하기 때문에 자유롭게 교체할 수 있음.
- 고차 함수는 콜백 함수를 자신의 일부분으로 합성.
- 매개변수를 통해 전달받은 콜백 함수의 호출 시점을 결정해서 호출.
- 27.9절 배열 고차 함수에서 자세히...
- 콜백 함수는 고차 함수에 의해 호출되며 이때 고차 함수는 필요에 따라 콜백 함수에 인수를 전달할 수 있음.
- 이때 콜백 함수로서 전달된 함수 리터럴은 고차 함수가 호출될 때마다 평가되어 함수 객체를 생성.
- 따라서 콜백 함수를 다른 곳에서도 호출할 필요가 있거나, 콜백 함수를 전달받는 함수가 자주 호출된다면 함수 외부에서 콜백 함수를 정의한 후 함수 참조를 고차 함수에 전달하는 편이 효율적.
5). 순수 함수와 비순수 함수
- 순수함수
- 어떤 외부 상태에 의존하지도 않고 외부 상태를 변경하지도 않는, 즉 부수 효과가 없는 함수.
- 동일한 인수가 전달되면 언제나 동일한 값을 반환.
- 일반적으로 최소 하나 이상의 인수를 전달받음.
- 인수를 전달받지 않은 순수 함수는 언제나 동일한 값을 반환하므로 결국 상수와 마찬가지.
- 인수를 변경하지 않는 것이 기본. 순순 함수는 인수의 불변성을 유지함.
- 오직 매개변수를 통해 함수 내부로 전달된 인수에게만 의존해 값을 생성해 반환.
- 만약 외부상태에는 의존하지 않고 함수 내부 상태에만 의존한다 해도 그 내부 상태가 호출될 때마다 변화하는 값(예: 현재 시간)이라면 순수 함수가 아님.
- 함수의 어떤 외부 상태에도 의존하지 않으며 외부 상태를 변경하지도 않는 함수.
- 비순수 함수
- 외부 상태에 의존하거나 외부 상태를 변경하는, 즉 부수 효과가 있는 함수.
- 위 예제와 같이 인수를 전달받지 않고 함수 내부에서 외부 상태를 직접 참조하면 외부 상태에 의존하게 되어 반환값이 변할 수 있고, 외부 상태도 변경할 수 있으므로 비순수 함수가 됨.
- 함수 내부에서 외부 상태를 직접 참조하지 않더라도 매개변수를 통해 객체를 전달받으면 비순수 함수가 됨.
- 함수가 외부 상태를 변경하면 상태 변화를 추적하기 어려워짐.
함수형 프로그래밍은 결국 순수 함수를 통해 부수 효과를 최대한 억제해 오류를 피하고 프로그램의 안정성을 높이려는 노력의 일환이라 할 수 있음. 자바스크립트는 멀티 패러다임 언어이므로 객체지향 프로그래밍뿐만 아니라 함수형 프로그래밍을 적극적으로 활용하고 있음.
참고
도서 - 모던 자바스크립트 Deep Dive -이웅모
이웅모 강사님 홈피 - https://poiemaweb.com/js-prototype
이웅모 강사님 유튜브 - https://www.youtube.com/watch?v=0AjTZG6bGq8
✅ 덧, 부분은 스터디 내용을 기억에 의존해서 쓴 글이라 틀린 부분이 있다면 댓글 부탁드립니다.- 뽀짝코딩 주인장-
'프론트 > 모던 자바스크립트 Deep Dive 책 스터디' 카테고리의 다른 글
JS)모던 자바스크립트 Deep Dive 14장 전역 변수의 문제점 (0) | 2024.07.18 |
---|---|
JS)모던 자바스크립트 Deep Dive 13장 스코프 (0) | 2024.07.18 |
JS)모던 자바스크립트 Deep Dive 11장 원시값과 객체의 비교 (0) | 2024.07.15 |
JS)모던 자바스크립트 Deep Dive 8장 ~ 10장/ 8장 제어문, 9장 타입 변환과 단축 평가, 10장 객체 리터럴 (1) | 2024.07.14 |
JS)모던 자바스크립트 Deep Dive 5장 ~ 7장/ 5장 표현식과 문, 6장 데이터 타입, 7장 연산자 (1) | 2024.07.09 |