프론트 공부
모던 자바스크립트 Deep Dive 12~14장 공부
KimMercury
2023. 3. 14. 00:31
12장 함수
수학은 자바스크립트에서 가장 중요한 핵심 기능이다.
- 프로그래밍에서의 함수의 개념은 수학적 개념과 같다.
- 수학적 함수는 "입력" 을 받아 "출력"을 내보내는 일련의 과정이다.
- 프로그래밍 언어의 함수는 일련의 과정을 문으로 구현하고 코드블록으로 감싸서 하나의 실행 단위로 정의한 것
- 함수 내부에 전달 받는 변수를 매개변수, 입력은 인수, 출력을 반환값 이라 한다.
- 함수는 값이며, 여러개가 존재할 수 있어 특정 함수를 구별하기 위해 식별자인 함수 이름을 사용할 수 있다.
함수를 사용하는 이유
- 함수는 여러번 호출이 가능하다.
- 실행 시점을 개발자가 결정 가능하다.
- 중복 코드를 제거하면 사람의 실수가 줄어들어 코드의 신뢰성이 증가한다.
- 유지보수가 편의성이 중가한다.
함수 리터럴
- 자바스크립트의 함수는 객체 타입의 값이다.
- 함수 리터럴은 function 키워드, 함수 이름, 매개 변수 목록, 함수 몸체로 구성된다.
함수 리터럴 구성 요소
- 익명 함수: 함수 리터럴 방식으로 만들어진 이름 없는 함수
- 함수를 재사용 하는 경우에는 함수 선언문, 함수 표현식 같은 일반 함수를 사용해야 한다.
- 함수의 이름이 선언 되어있지 않아 재사용이 어렵다!!!
익명함수 형태
function() {
console.log("hello!");
}
var test = function( a )
{
console.log("hello");
};
function hello(){ // 익명함수 사용 X
alert("안녕하세요. 환영합니다.");
}
$("#btn").click(hello);
$("#btn").click(function(){ // 익명함수 사용 O
alert("안녕하세요. 환영합니다.");
});
- 호이스팅(함수 선언 보다 함수 호출 윗 줄에 위치 해도 실행되는 기능)이 적용되지 않는다.
//익명 함수 호출
orange( );
//익명 함수 선언
var orange = function( ) {
document.write("This is an orange.");
};
// result : 오류
함수 정의
함수 선언문
- 함수 선언문은 함수 리터럴 형태와 동일하다. 그러나 함수 선언문은 이름 생략이 불가능하다.
- 함수 선언문은 표현식이 아니라 문이다.
- 표현식:
- 단일 값을 생성한다
- 값으로 평가될 수 있는 문
- 문
- 프로그램을 구성하는 기본 단위
- 표현식:
함수 선언문
function add(x,y){
return x + y
}
함수 표현식
- 자바스크립트의 함수에는 값처럼 변수에 할당이 가능하다.
- 그 값은 프로퍼티, 배열의 요소 가 될 수 있다.
- 이러한 값의 성질을 갖는 객체를 일급객체 라고 한다.
- 일급 객체라는 것은 함수를 값처럼 자유롭게 사용이 가능하다는 것을 의미한다.
함수 표현식
var add = function(x,y){
return x + y
}
함수 생성 시점 및 호이스팅
console.dir(add); //add(x,y)
console.dir(sub); // undefined
console.log(add(2,5));// 7
console.log(sub(2,5));// Error
함수 선언문
function add(x, y){
return x + y;
}
함수 표현식
var sub =function(x, y){
return x + y;
}
- 함수 선언문에서는 호이스팅이 일어난다.
- 함수 표현식에서는 호이스팅이 일어나지 않는다.
- sub()을 실행하면 var sub을 선언하며 함수를 담는다.
- var는 호이스팅의 영향을 받으므로 위로 끌어올려진다.
- 따라서 var sub 가 가장 먼저 실행된다.(변수에 아무 값이 없으므로 undefined가 찍힌다.)
- 그 후 sub()이 호출되면 위에 선언한 sub가 호출되므로 변수를 호출하는 격이 된다.(error : sub is not function)
Function 생성자 함수
var add = new function('x','y','return x + y');
console.log(add(2,5))
- Function 생성자 함수는 일반적으로 사용을 권장 하지 않는다.
- Function 생성자 함수는 클로저를 생성하지 않는다.
화살표 함수
- ES6에서 도입된 함수이다.
- function 키워드 대신에 =>을 사용하여 좀 더 간략한 방법으로 함수를 선언할 수 있다.
- 화살표 함수는 항상 익명 함수로 정한다.
- 매겨변수가 없을 경우 괄호 필수
- 본문이 return[식 or 값] 뿐일 경우 {}, return 생략 가능
- 위에서 return 할 값이 객체인 경우 괄호 필수
화살표 함수
const add = (x,y) => x + y
const add = x => ({ x }); // 인자를 하나만 받는다면 괄호() 생략 가능
const add = (x,y) = a + b; // return 값만 있는 경우 중괄호({}), return 생략 가능
함수 호출
- 함수는 함수를 가리키는 식별자와 한 쌍의 소괄호인 함수 호출 연산자로 호출된다.
- 함수를 실행하기 위해 필요한 값을 함수 외부에서 함수 내부로 전달할 필요가 있는 경우, 매개변수를 통해 인수를 전달한다.
- 매개변수는 함수 몸체 내부에서만 참조할 수 있다.
- 매개변수의 스코프는 함수 내부이다.
- 함수는 매개변수의 개수와 인수의 개수가 일치하는지 체크하지 않는다.
- 매개변수보다 인자가 더 많은 경우 초과된 인수는 무시된다.
반환문
- 함수는 return 키워드와 반환값을 사용햐 실행 결과를 외부로 반환할 수 있다.
- 반환문은 함수의 실행을 중단하고 함수를 탈출한다.(반환문 아래의 다른 문들은 동작하지 않는다.)
- return 키워드 뒤에 오는 표현식을 평가해 반환한다.(return 키워드 뒤에 아무것도 없으면 undefine을 반환한다.)
- 반환문은 함수의 몸체 내부에서만 사용이 가능하다(밖에서 사용시 Error 발생)
참조에 의한 전달과 외부 상태의 변화
- 매개변수도 함수 몸체 내부에서 변수로 취급되므로 값에 의한 전달, 참조에 대한 전달 방식이 그대로 작동한다.
다양한 함수의 형태
재귀함수
- 함수가 자기 자신을 호출하는 함수이다.
- 재귀 함수는 반복되는 처리를 위해 사용된다.
- 재귀 함수는 탈출 조건이 필요하다(스택 오버플로가 발생할 수 있다.)
재귀 함수
function test(a){
if(a<=0) return;
console.log(a);
test(a - 1)
}
test(5)
결과: 5,4,3,2,1
중첩 함수
- 함수 내부에 정의된 함수를 중첩 함수 또는 내부 함수라고 부른다.
- 내부 함수를 포함하는 함수를 외부 함수라고 부른다.
- 내부 함수는 외부 함수 내부에서만 호출이 가능하다.
- 일반적으로 내부함수는 외부 함수를 돕는 헬퍼 함수로 사용된다
function out(){
var x = 1;
function inner(){
var y = 2;
console.log(x + y)
}
inner();
}
out();
결과: 3
콜백 함수
- 다른 함수에 매개변수를 넘겨준 함수이다.
- 매개변수로 넘겨받은 함수는 일단 넘겨받고, 때가 되면 나중에 호출한다는 것이 콜백 함수의 개념이다.
콜백 함수
function add(a, b, callback) {
callback(a + b);
}
function sayResult(value) {
console.log(value);
}
add(3, 4, sayResult);
결과: 7
13장 스코프
- 스코프는 모든 식별자(변수 이름, 함수 이름, 클래스 이름 등)는 자신이 선언된 위치에 의해 다른 코드가 식별자 자신을 참조 할 수 있는 유효 범위가 결정된다.
- 스코프는 식별자가 유요한 범위를 말한다.
- 위의 코드에서 함수의 식별자의 유효 범위는 함수 내부 이기 때문에 Error가 발생한다.
식별자 결정
- 자바스크립트 엔진은 이름이 같은 두 개의 변수 중 어떤 변수를 참조할 것인지 결정해야 한다.
- 자바스크립트 엔진은 스코프를 통해 어떤 변수를 참조할 것인지 결정한다.
- 스코프란 자바스크립트 엔진이 식별자를 검색할 때 사용되는 규칙이라고도 한다.
- 하나의 값은 유일한 식별자에 연결되어야 한다.
지역, 전역 스코프
- 전역 스코프
- 코드의 가장 바깥 영역
- 전역 변수
- 전역 변수는 어디에서든 참조할 수 있다.
- 지역 스코프
- 함수 몸체 내부
- 지역 변수
- 지역변수는 자신이 선언된 지역 스코프 와 하위 지역 스코프에서 유효하다.
스코프 체인
- 함수는 중첩이 가능하다.(스코프도 중첩이 가능하다.)
- 스코프가 함수의 중첩에 의해 계층적 구조를 갖는다는 것을 의미한다.
- 이때 외부함수의 지역 스코프를 내부 함수의 상위 스코프 라고 한다.
- 모든 스코프가 하나의 계층적 구조로 연결되며, 모든 지역 스코프의 최상위 스코프는 전역 스코프 그리고 스코프가 계층으로 연결된 스코프를 스코프 체인 이라고 한다.
- 자바스크립트 엔진에서 변수를 참조할때 스코프 체인을 통하여 변수를 참조하는 스코프에서 상위 스코프로 이동하면서 선언된 변수를 검색한다.
스코프 체인에 의한 변수 검색
스코프 체인에 의한 함수 검색
var x = "global x"
var y = "global y"
function outer(){
var z = "global z"
function inner(){
var x = "local's local x"
console.log(x); // local's local x
console.log(y); // global y
console.log(z); // global z
}
inner()
}
outer()
함수 레벨 스코프
- 함수 내에서 선언 된 변수는 함수 내에서만 유효하며 함수 외부에서는 참조할 수 없다.
- 함수 내부에서 선언한 변수는 지역 변수 이며 함수 외부에서 선언한 변수는 모두 전역 변수이다.
블록 레벨 스코프
- 모든 코드 블록(함수,if 문, while 문, try/catch 문 등) 내에서 선언된 변수는 코드 블록 내에서만 유효하며 코드 블록 외부에서는 참조할 수 없다.
- 코드 블록 내부에서 선언한 변수는 지역 변수이다.
예외
- var 키워드는 블록 레벨 스코프를 따르지 않는다.
- var 키워드는 코드 블럭 내의 변수는 전역 변수이다.
- 전역 변수 foo가 선언되었으나. var 키워드는 중복 선언이 허용되므로 아래 코드는 문제가 없다.
- 단, 블럭 내의 변수 foo가 전역변수 이기 때문에 전역에 선언된 전역 변수 foo의 값이 (123 -> 456)으로 바뀐다.
블록 레벨 키워드 예외
var foo = 123; // 전역 변수
console.log(foo); // 123
{
var foo = 456; // 전역 변수
}
console.log(foo); // 456
- ES6에서는 블록 레벨 스코프를 따르는 변수를 선언하기 위해 let 키워드를 제공한다.
let foo = 123; // 전역 변수
{
let foo = 456; // 지역 변수
let bar = 456; // 지역 변수
}
console.log(foo); // 123
console.log(bar); // ReferenceError: bar is not defined
렉시컬 스코프
- 함수가 어디에 선언되었는지에 따라 상위 스코프를 결정한다.
- 함수가 호출된 위치는 상위 스코프 결정에 어떠한 영향도 주지 않는다.
- 함수의 상위 스코프는 언제나 자신이 정의된 스코프이다.
동적 스코프
- 함수가 어디서 호출되었는지에 따라 상위 스코프가 결정된다.
렉시컬 스코프
var x = 1;
function foo() {
var x = 10;
bar();
}
function bar() {
console.log(x);
}
foo(); // 10? 1?
bar(); // 1
bar 함수를 호출한 곳은 foo함수 내부였지만, bar 함수가 선언된 곳은 전역이기 때문에 x는 전역에 선언된 1의 값을 가진다.
14장 전역 변수의 문제점
- 변수는 선언에 의해 생성되고 할당을 통해 값을 갖는다.
- 변수는 생성되고 소멸되는 생명주기가 있다.
지역 변수의 생명 주기
function foo(){
var x = "local";
console.log(x); // local
return x;
}
foo();
console.log(x)// Error: x is not defined
- 지역 변수 x 는 foo 함수가 호출되기 이전까지는 생성되지 않는다.
- 지역 변수는 함수 호출 된 직후에 함수의 코드가 한줄 씩 실행되기 전에 실행된다.
- 지역 변수 생성 주기는 함수의 생명 주기와 일치한다.
전역 변수의 생명 주기
- 함수와 달리 전역 코드는 명시적인 호출이 없이 실행된다.
- 전역변수의 생명주기는 전역 객체의 생명주기와 같다.
- 전역 객체: 코드가 실행되기 이전에 자바스크립트 엔진에 의해 어떤 객체보다도 먼저 생성되는 특수한 객체이다.(window, self, this, frames, global)
전역 변수의 문제점
암묵적 결합
- 전역 변수는 코드 어디서든 참조하고 할당할 수 있는 변수이다.
- 이는 모든 코드가 전역변수를 침조하고 변형할 수 있는 암묵적 결합을 허용하는 것이다.
- 변수의 유효 범위가 클수록 코드의 가독성이 나빠지고 의도치 않게 상태가 변경될 위험성도 커진다.
긴 생명주기
- 전역 변수는 생명주기가 길다.
스코프 체인 상에서 종점에 존재
- 전역 변수는 스코프 체인 상에서 종점에 존재한다 -> 전역 변수의 검색 속도가 가장 느리다.
네임 스페이스 오염
- 자바스크립트의 가장 큰 문제점 중 하나는 파일이 분리되어 있다 해도 하나의 전역 스코프를 공유한다는 것이다.
- 다른 파일 내에서 동일한 전역 변수나 전역 함수가 같은 스코프에 존재할 경우 예상치 못한 결과를 가져올 수 있다.
전역 변수 사용을 억제하는 방법
즉시 실행 함수
- 함수 정의와 동시에 호출 되는 함수이다.
- 단 한번만 호출된다.
- 모든 코드를 즉시 실행 함수에 감싸면 모든 변수는 즉시 실행 함수의 지역변수가 된다.
네임 스페이스 객체
- 전역에 네임스페이스 역할을 담당할 객체를 생성하고 전역 변수 처럼 사용하고 싶은 변수를 프로퍼티로 추가하는 방법이다.
- 네임스페이스 객체에 또 다른 네임스페이스 객체를 프로퍼티로 추가하여 계층적으로 구성할 수 있다.
- 네임스페이스를 분리하여 식별자 충돌을 방지하는 효과는 있으나 네임스페이스 객체가 전역 변수에 할당 되므로 그다지 유용하지 않다.
모듈 패턴
- 자바스크립트에서 중요한 패턴 중 하나이며 가장 일반적으로 사용되는 디자인 패턴이다.
- 프로젝트의 코드를 명확하게 나누고 구분하는데 좋다.
- 관련 있는 변수 그리고 함수를 즉시 실행함수()로 감싸 하나의 모듈로 만들고 클로져 기반으로 작동한다.
모듈패턴 사용 x
let counter = {
value: 0,
increase: function() {
this.value++ // 메서드 호출을 할 경우, this는 counter을 가리킵니다
},
decrease: function() {
this.value--
},
getValue: function() {
return this.value
}
}
모듈패턴
var Counter = (function () { //즉시실행함수로 감싼다
// private 변수
var num = 0;
// 클로저
//외부로 공개할 데이터나 메서드를 프로퍼티로 추가한 객체를 반환한다.
return {
increase() {
return ++num;
},
decrease() {
return --num;
}
};
}());
- Counter는 메서드를 호출하여 값을 담고 있는 프로퍼티 객체를 반환한다.
- 즉시 실행 함수()는 한번만 실행되므로 Count를 호출할 때마다 num을 초기화 하지 않는다.
- 즉시 실행 함수()가 반환한 클로저는 Counter 변수에 할당되어 호출된다.
- num은 클로저에 의해 외부에 접근할 수 없고 메서드들을 통해서 접근이 가능하다.
모듈 패턴을 쓰는 이유
- 캡슐화, 정보 은닉이 가능하다.
- 캡슐화 : 객체의 상태를 데이터(속성)와 기능(메서드)을 하나의 단위로 묶는 것
- 언제든 구현을 수정할 수 있다.
전역변수 억제
- 전역 변수는 side effect를 발생시키기 떄문에 side effect를 최소화 하면 좋다.
재사용성
- 모듈로 묶여있기 때문에 재사용성에 좋다.