3주차_이나영_회고록 - hyeone999/Docs-JS_deepDive_Study GitHub Wiki
객체 = "자바스크립트를 구성하는 모든 것" 원시 값을 제외한 나머지 값(함수, 배열, 정규 표현식 등)
타입 | 설명 |
---|---|
원시타입 | 단 하나의 값만 나타냄 |
객체 타입 | 다양한 타입의 값 (원시값/다른 객체)을 하나의 단위로 구성한 복합적인 자료 구조 |
원시 값 변경 불가능한 값이지만 객체 타입의 값(객체는 변경 가능한 값)
객체 = 0개이상의 프로퍼티로 구성된 집합 프로퍼티 = 키 + 값
var person = {
name: "Lee", // 프로퍼티
age: 20, // age : 키, 20 : 프로퍼티 값
};
→ 자바스크립트에서 사용할 수 있는 모든 값은 프로퍼티 값이 될 수 있음 → 함수도 프로퍼티값이긴 하지만 일반 함수와 구분하기 위해 메서드라 부른다.
var counter = {
num =0; // 프로퍼티
increase: function () { // 메서드
this.num++;
}
}
→ 프로퍼티 : 객체의 상태를 나타내는 값 (data) → 메서드 : 프로퍼티(상태 데이터)를 참조하고 조작할 수 있는 동작 (behavior)
JS는 클래스 기반 객체지향 언어와 달리 다양한 객체 생성 방법을 지원한다.
- 객체 리터럴
- Object 생성자 함수
- 생성자 함수
- Object.create 메서드
- 클래스
객체 리터럴 : 객체를 생성하기 위한 표기법 → 중괄호 내에 0개 이상의 프로퍼티를 정의 → 변수에 할당되는 시점에 자바스크립트 엔진은 객체 리터럴을 해석해 객체를 생성
var person = {
name: "LEE",
sayHello: function () {
console.log(`Name is ${this.name}`);
},
};
→ 중괄호는 코드 블록을 의미하지 않아 뒤에 세미콜론을 붙이지 않는다. → 객체 리터럴은 값으로 평가되는 표현식으로 중괄호 뒤에는 세미콜론을 붙인다.
객체는 프로퍼티의 집합이며, 프로퍼티는 키와 값으로 구성된다
→ 프로퍼티를 나열할 때는 쉼표(,)로 구분하고, 마지막 프로퍼티는 사용하지 않음
- 프로퍼티 키 : 빈 문자열을 포함하는 모든 문자열 또는 심벌값 (식별자 역할)
- 프로퍼티 값 : 자바스크립트에서 사용할 수 있는 모든 값
[식별자 네이밍 규칙]
- 프로퍼티 키는 문자열 이므로 따옴표로 묶는다.
- 식별자 네이밍 규칙을 준수하면 따옴표 생략이 가능하다.
var person = {
fisrtName: "NaYoung", // 식별자 네이밍 규칙 준수
"last-name": "lee", // 식별자 네이밍 규칙 미준수(-때문)
};
var foo = {
"": "",
};
→ 프로퍼티 키에 문자열과 심벌값 외의 값을 사용하면 암묵적 타입 변환을 통해 문자열이 된다.
var foo = {
name: "Lee",
name: "Kim",
};
→ 이미 존재하는 프로퍼티 키를 중복 선언하면 나중에 선언한 프로퍼티가 덮어쓴다. (에러 X)
프로퍼티 값이 함수일 경우 일반 함수와 구분하기 위해 메서드라 부른다. 즉 메서드는 객체 묶여있는 함수이다.
var circle = {
raduis: 5,
getDiameter: function () {
// 메서드
return 2 * this.radius; // this = circle
},
};
console.log(circle.getDiameter()); // 10
- 마침표 표기법: 마침표 프로퍼티 접근 연산자 사용 (.)
- 대괄호 표기법 : 대괄호 프로퍼티 접근 연산자 ([...])
→ 좌측에는 객체로 평가되는 표현식을 기술 → 마침표의 우측, 대괄호의 내부에는 프로퍼티 키를 지정
var person = {
name: "lee",
};
// 마침표 표기법
console.log(person.name);
// 대괄호 표기법
console.log(person["name"]);
→ 대괄호 표기법은 대괄호 프로퍼티 접근 연산자 내부에 지정하는 프로퍼티 키는 반드시 따옴표로 감싼 문자열이어야 한다. → 그렇지 않으면 식별자로 해석해 버린다. → 객체에 존재하지 않은 프로퍼티에 접근하면 undefined 를 반환한다.
이미 존재하는 프로퍼티에 값을 할당하면 프로퍼티 값이 갱신된다.
var person = {
name: "LEE",
};
person.name = "KIM";
console.log(person); // { name: 'KIM' }
존재하지 않은 프로퍼티에 값을 할당하면 프로퍼티가 동적으로 생성되어 추가되고 프로퍼티 값이 할당된다.
var person = {
name: "LEE",
};
person.age = 20;
console.log(person); // { name: 'LEE', age: 20 }
delete
연산자는 객체의 프로퍼티를 삭제한다.
이때 delete 연산자의 피연산자는 프로퍼티 값에 접근할 수 있는 표현식이어야 한다.
만약 존재 하지 않는 프로퍼티를 삭제하면 아무런 에러없이 무시된다.
var person = {
name: "LEE",
};
person.age = 20;
// 삭제하기
delete person.age;
// 존재하지 않는 프로퍼티 삭제 -> 에러 X 무시
delete person.address;
console.log(person); // { name: 'Lee' }
객체 리터럴의 프로퍼티는 프로퍼티 키와 값으로 구성된다. 프로퍼티 값은 변수에 할당된 값 즉 식별자 표현식일 수도 있다.
→ 프로퍼티 값으로 변수를 사용하는 경우 변수 이름과 프로퍼티 키가 동일한 이름일 때 프로퍼티 키를 생략 할 수 있다. 키는 변수 이름으로 자동 생성된다.
let x = 1,
y = 2;
//프로퍼티 축약
const obj = { x, y };
console.log(obj); // {x:1, y:2}
문자열 또는 문자열로 타입 변환할 수 있는 값으로 평가되는 표현식을 사용해 프로퍼티 키를 동적으로 생성할 수도 있다. 단, 프로퍼티 키로 사용할 표현식을 대괄호로 묶어야 한다.
const prefix = 'prop'
let i = 0;
// 객체 리터럴 내부에서 계산된 프로퍼티 이름으로 프로퍼티 키를 동적 생성
const obj = {
[`${prefix}- ${++i}`] : i,
[`${prefix}- ${++i}`] : i,
[`${prefix}- ${++i}`] : i
};
console.log(obj); //{ 'prop- 1': 1, 'prop- 2': 2, 'prop- 3': 3 }
메서드를 정의할 때 function 키워드를 생략한 축약 표현을 사용한다.
const obj = {
name: 'lee',
// 메서드 축약 표현
sayHi() {
console.log('Hi' + this.name);
}
};
obj.sayHi(); // Hi lee
구분 | 원시 값 | 객체 |
---|---|---|
예시 |
10 ,hello ,true ,null
|
{ name: "LEE" } , [1,2,3] , function() {}
|
값의 특성 | 변경 불가능한 값 (재할당만 가능) | 변경 가능한 값 (프로퍼티 추가/삭제/수정) |
변수에 할당 시 | 변수에 실제 값이 저장됨 | 변수에 참조 값(주소)이 저장됨 |
변수 간 전달 방식 | 값에 의한 전달 (값이 복사) | 참조에 의한 전달 (주소 복사) |
원시 타입의 값(원시 값)은 변경 불가능한 값이다. 한 번 생성된 원시 값은 읽기 전용 값으로서 변경할 수 없다.
- 변수 : 하나의 값을 저장하기 위해 확보한 메모리 공간 자체 또는 그 메모리 공간을 식별하기 위해 붙인 이름
- 값 : 변수에 저장된 데이터로서 표현식이 평가되어 생성된 결과
→ 변경 불가능하다 = 변수가 아닌 값에 대한 진술
var a = 1; // a : 변수, 1: 값
let x = 1;
x = 2; // OK
const y = 10;
y = 20; // 에러: Assignment to constant variable.
→ 원시값은 변경 불가능한 값으로 불변하기 때문에 신뢰성을 보장한다.
→ 불변성을 갖는 원시 값을 할당한 변수는 재할당 이외에 변수 값을 변경할 방법이 없다.
→ 값을 일부만 수정하는 건 불가능 → 변경하고 싶으면 통째로 새 값을 넣는 '재할당'만 가능
- 문자열은 0개 이상의 문자로 이뤄진 집합 (빈문자열도 가능)
- 1개 문자는 2byte의 메모리 공간에 저장됨.
- 몇 개의 문자로 이뤄졌느냐에 따라 필요한 메모리 공간의 크기도 결정됨
문자열 같은 경우 JS는 원시 타입으로 변경 불가능하다.
var str = "hello";
str = "world";
→ hello, world 둘 다 메모리에는 존재하는데 str이 가르키는 건 world로 변경된 것이다
Q. 문자열의 한 문자를 변경하면?
var str = "hello";
str[0] = "b";
console.log(str); // hello
//bello로 바꾸려면?
str = "bello";
console.log(str);
→ 이미 생성된 문자열의 일부 문자를 변경하려해도 반영이 안된다. → 하지만 변수에 새로운 문자열을 재할당하는 것은 가능하다
var score = 80;
var copy = score;
console.log(score); // 80
console.log(copy); // 80
score = 100;
console.log(score); // 100
console.log(copy); // 80
→ 변수에는 값이 전달되는 것이 아닌 메모리 주소가 전달되고, 이는 변수와 같은 식별자는 값이 아니라 메모리 주소를 기억한다.
∴ 두 변수의 원시 값은 서로 다른 메모리 공간에 저장된 별개의 값이 되어 어느 한 쪽에서 재할당을 통해 값을 변경하더라도 서로 간섭할 수 없다 !
프로퍼티의 개수가 정해져있지 않으며, 동적으로 추가되고 삭제할 수 있다. 프로퍼티의 값에도 제약이 없다. (크기가 큼) ∴ 객체는 원시값과 같이 확보해야할 메모리 공간의 크기를 사전에 정해둘 수 없다
var a = 10;
원시 값은 할당한 변수가 기억하는 메모리 주소를 통해 메모리 공간에 접근하면 원시값에 접근할 수 있다. → 원시값을 할당한 변수는 원시 값 자체를 값으로 가진다
var person = {
name: "LEE",
};
객체를 할당한 변수가 기억하는 메모리 주소를 통해 메모리 공간에 접근하면 참조값에 접근할 수 있다. → 참조값은 생성된 객체가 저장된 메모리 공간의 주소 그 자체다.
→ 원시 값을 할당한 변수를 참조하면 메모리에 저장되어 있는 원시 값에 접근한다.
→ 원시 값은 변경 불가능한 값이므로 원시 값을 갖는 변수의 값을 변경하려면 재할당 외에는 방법이 없다. 하지만 객체는 변경 가능한 값으로 객체를 할당한 변수는 재할당 없이 객체를 직접 변경할 수 있다. 재할당 없이 프로퍼티를 동적으로 추가할 수도 있고, 프로퍼티 값을 갱신할 수도 있고, 삭제도 가능하다
var person = {
name: "LEE",
};
// 프로퍼티 값 갱신
person.name = "KIM";
// 프로퍼티 동적 생성
person.address = "Seoul";
console.log(person);
BUT, 객체는 비용도 많이 들고, 메모리의 효율적 소비가 어렵고 성능이 나쁘다. 원시값과는 다르게 여러 개의 식별자가 하나의 객체를 공유할 수 있다는 것이다.
var person = {
name: "LEE",
};
var copy = person; // 얕은 복사
객체를 가리키는 변수 (원본 person)를 다른 변수 (사본 copy)에 할당하면 원본의 참조값이 복사되어 전달된다.
→ 두 개의 식별자가 하나의 객체를 공유한다는 것 → 원본 또는 사본 중 어느 한 쪽에서 객체를 변경하면 서로 영향을 받는다 (재할당 X)
값에 의한 전달과 참조에 의한 전달은 식별자가 기억하는 메모리 공간에 저장되어 있는 값을 복사해서 전달한다는 면에서 동일하다.
식별자가 기억하는 메모리공간, 즉 변수에 저장되어 있는 값이 원시 값이냐 참조 값이냐의 차이만 있다.
자바스크립트에는 참조에 의한 전달은 존재하지 않고, 값에 의한 전달만이 존재한다.
var p1 = {
name: "LEE",
};
var p2 = {
name: "LEE",
};
console.log(p1 === p2); // false
console.log(p1.name === p2.name); // true
→ p1,p2의 주소가 다르기 때문
입력을 받아 출력을 내보내는 일련의 과정이다. 함수는 일련의 과정을 문으로 구현하고 코드 블록으로 감싸서 하나의 실행단위로 정의한 것이다.
// f(x,y) = x + y
function add(x, y) {
return x + y;
}
// f(2, 5) = 7
add(2, 5); //7
- 매개변수 : 함수 내부로 입력을 전달받는 변수
- 인수 : 입력
- 반환값: 출력
- 함수 정의 : 함수를 생성
- 함수 호출 : 실행결과인 반환값 반환
// 함수 정의
function add(x, y) {
return x + y;
}
// 함수 호출
var result = add(2, 5);
console.log(result);
- 함수는 필요할 때 여러 번 호출이 가능한 코드의 재사용의 특징이 있다.
- 코드의 중복을 억제하고 재사용성을 높이는 함수는 유지 보수의 편의성을 높이고 실수를 줄여 코드의 신뢰성을 높인다.
- 적절한 함수 이름을 사용하여 내부 코드를 이해하지 않고 함수의 역할을 파악할 수 있게 코드의 가독성을 높인다.
JS에서 함수는 객체 타입의 값으로 함수 리터럴은 function키워드, 함수 이름, 매개변수 목록, 함수 몸체로 구성된다.
var f = function add(x, y) {
return x + y;
};
구성 요소 | 설명 |
---|---|
함수 이름 | - 함수 이름은 식별자다. 따라서 식별자 네이밍 규칙을 준수해야 한다. - 함수 이름은 함수 몸체 내에서만 참조할 수 있는 식별자다. - 함수 이름은 생략할 수 있다. 이름이 있는 함수는 기명 함수(named function), 이름이 없는 함수는 무명/익명 함수(anonymous function)라 한다. |
매개변수 목록 | - 0개 이상의 매개변수를 소괄호로 감싸고 쉼표로 구분한다. - 각 매개변수에는 함수를 호출할 때 지정한 인수가 순서대로 할당된다. 즉, 매개변수 목록은 순서에 의미가 있다. - 매개변수는 함수 몸체 내에서 변수와 동일하게 취급된다. 따라서 매개변수도 변수와 마찬가지로 식별자 네이밍 규칙을 준수해야 한다. |
함수 몸체 | - 함수가 호출되었을 때 일괄적으로 실행될 문들을 하나의 실행 단위로 정의한 코드 블록이다. - 함수 몸체는 함수 호출에 의해 실행된다. |
→ 함수는 객체다. 하지만 일반 객체와 다르게 호출이 가능하다. 또한, 일반 객체에는 없는 함수 객체만의 고유한 프로퍼티를 갖는다.
함수를 호출하기 이전에 인수를 전달받을 매개변수와 실행할 문들, 반환할 값을 지정하는 것.
함수 정의 방식 | 예시 |
---|---|
함수 선언문 | function add(x, y) { return x + y; } |
함수 표현식 | var add = function(x, y) { return x + y; }; |
Function 생성자 함수 | var add = new Function('x', 'y', 'return x + y'); |
화살표 함수 (ES6) | var add = (x, y) => x + y; |
※ 변수 선언과 함수 정의 변수는 '선언'한다고 했지만, 함수는 '정의'한다고 표현한다. 함수 선언문이 평가되면 식별자가 암묵적으로 생성되고 함수 객체가 할당된다.
// 함수 선언문
function add(x, y) {
return x + y;
}
// 함수 참조
console.dir(add); // f add(x,y)
// 함수 호출
console.log(add(2, 5)); // 7
→ 함수 선언문은 함수 리터럴과 형태가 동일하다. 단, 함수 리터럴은 함수이름을 생략할 수 있으나 함수 선언문은 함수 이름을 생략할 수 없다.
→ 함수 선언문은 표현식이 아닌 문이다. 따라서 undefined 값이 나온다.
→ 자바스크립트 엔진은 생성된 함수를 호출하기 위해 함수 이름과 동일한 이름의 식별자를 암묵적으로 생성하고, 거기에 함수 객체를 할당한다
// 식별자 add, 함수이름 add
var add = function add(x, y) {
return x + y;
};
// 식별자 사용하여 불러옴
console.log(add(2, 5));
함수는 함수를 가리키는 식별자와 한 쌍의 소괄호인 함수 호출 연산자로 호출한다. 함수 호출 연산자 내에는 0개 이상의 인수를 쉼표로 구분하여 나열한다. 함수를 호출하면 현재의 실행 흐름을 중단하고 호출된 함수로 실행 흐름을 옮긴다.
함수를 실행하기 위해 필요한 값을 함수 외부에서 함수 내부로 전달할 필요가 있는 경우, 매개변수를 통해 인수를 전달한다.
// 함수 선언문
function add(x, y) {
return x + y;
}
// 함수 호출
console.log(add(1, 2)); // 3
// 인수가 부족하면
console.log(x, y); // undefined error
// 인수 개수에 맞추지 않으면
console.log(add(2)); // NaN
// 인수 개수를 초과하면
console.log(add(2, 5, 8)); // 7
// 문자열을 주면?
console.log(add("a", "b")); // 'ab'
→ 함수가 호출되면 함수 몸체 내에서 암묵적으로 매개 변수가 생성되고 일반 변수와 마찬가지로 undefined로 초기화된 이후 인수가 순서대로 할당 된다.
타입을 지정해주어서 부적절한 호출을 방지해준다.
function add(a = 0, b = 0, c = 0) {
return a + b + c;
}
console.log(add(1, 2, 3)); //6
console.log(add(1, 2)); //3
console.log(add(1)); //1
이상적인 함수는 한 가지 일만 해야하며 가급적 작게 만들어야한다. 따라서 매개변수는 최대 3개 이상을 넘지 않는 것을 권장한다. 그 이상의 매개변수가 필요하다면 하나의 매개변수를 선언하고 객체를 인수로 전달하는게 유리하다.
return
키워드와 표현식(반환값)으로 이뤄진 반환문을 사용해 실행 결과를 함수 외부로 반환할 수 있다.
function multiply(x, y) {
return x * y; // 반환문
}
console.log(multiply(3, 5)); // 15
- 반환문은 함수의 실행을 중단하고 함수 몸체를 빠져나간다.
- 반환문은 return 키워드 뒤에 오는 표현식을 평가해 반환한다.
반환문 특징
- 반환문은 생략할 수 있으나 undefined를 반환한다.
- 반환문은 함수 몸체 내부에서만 사용할 수 있다.
함수 정의와 동시에 즉시 호출되는 함수 단 한 번만 호출되며 다시 호출할 수 없다.
// 기본 형태
function functionName() {
//내용물
}();
// 익명 즉시 실행 함수
(function () {
var a = 3;
var b =5;
return a * b;
}());
// 기명 즉시 실행 함수
(function foo() {
var a = 3;
var b =5;
return a * b;
}());
자기 자신을 호출하는 행위 재귀 호출을 수행하는 함수를 말한다. 주로 반복되는 처리에서 많이 이용된다.
function countdown(n) {
if (n < 0) return;
console.log(n);
countdown(n - 1); // 재귀호출
}
countdown(10);
재귀함수는 자신을 무한 재귀호출하므로 멈출 수 있는 탈출 조건을 반드시 만들어야 한다. 탈출 조건이 없으면 함수가 무한 호출되어 스택 오버플로우 에러가 발생한다.
var fac = function foo(n) {
//탈출 조건: n이 1이하일 때 재귀호출을 멈춘다
if (n <= 1) return 1;
// 함수를 가리키는 식별자로 자기자신을 재귀호출
return n * fatorial(n - 1);
};
console.log(fac(5));
함수 내부에 정의된 함수를 중첩 함수, 내부함수라고 부른다. 중첩함수를 포함하는 함수는 외부함수라 부른다. 중첩함수는 외부함수 내부에서만 호출할 수 있으며, 자신을 포함하는 외부함수를 도와주어 헬퍼 함수 역할을 한다.
function outer() {
var x = 1;
//중첩 함수
function inner() {
var y = 2;
console.log(x + y); // 3
}
inner();
}
outer();
함수의 매개변수를 통해 다른 함수의 내부로 전달되는 함수 매개변수를 통해 함수의 외부에서 콜백 함수를 전달받은 함수를 고차함수라고 한다.
고차함수는 콜백 함수를 자신의 일부분으로 합성하여 매개변수를 통해 전달받은 콜백 함수의 호출 시점을 결정해서 호출한다. 콜백 함수는 고차 함수에 의해 호출되며 이 때 고차 함수는 필요에 따라 콜백 함수에 인수를 전달할 수 있다.
// 익명 함수 리터럴을 콜백 함수로 고차함수에 전달한다
// 익명 함수 리터럴은 repeat함수를 호출할 때마다 평가되어 함수 객체를 생성한다.
repeat(5,function(i) {
if(i %2)console.log(i);
}) // 1 3
→ 콜백함수를 다른 곳에서도 호출할 필요가 있거나 콜백함수를 전달받는 함수가 자주 호출된다면 함수 외부에서 콜백함수를 정의한 후 함수 참조를 고차 함수에 전달하는 편이 효율적이다 → 콜백함수는 함수형 프로그래밍 패러다임뿐만 아니라 비동기처리(이벤트처리, 타이머함수 등) 에 중요한 패턴이다.
순수함수 : 어떤 외부 상태에 의존하지 않고 변경하지도 않는 부수효과가 없는 함수 비순수함수 : 외부 상태에 의존하거나 외부상태를 변경하는 부수효과가 있는 함수
구분 | 순수 함수 (Pure Function) | 비순수 함수 (Impure Function) |
---|---|---|
외부 상태 의존 | 외부 상태에 의존하지 않음 | 외부 상태에 의존함 |
외부 상태 변경 | 외부 상태를 변경하지 않음 | 외부 상태를 변경함 |
결과 일관성 | 동일한 인수를 전달하면 항상 동일한 값을 반환함 | 동일한 인수를 전달해도 상황에 따라 반환값이 달라질 수 있음 |
부수 효과 | 없음 | 있음 (ex: 콘솔 출력, 파일 쓰기, DOM 조작 등) |
예시 |
Math.max(1, 2) / x => x * 2
|
Date.now() , Math.random() , console.log() 등 |
상태 변경 | 함수 외부 상태에 영향을 주지 않음 | 함수 외부 상태를 읽거나 변경함 |
함수 목적 | 예측 가능하고 테스트 용이한 함수 작성에 적합 | 비동기 처리, 입출력, UI 업데이트 등 부수 효과가 필요한 경우에 사용 |
이번 공부는 특히나 더 어려웠던 거 같다. 함수같은 경우에는 활용도가 높아 이해가 잘되었지만 객체리터럴 부분은 내부의 저장소와 연결되어 있어 이해하는 데 시간이 오래 걸린 것 같다. 다음 주차에도 남은 공부 열심히해서 밀리지 말고 해야겠다