비교연산자에는 ==, ===가 존재함
둘의 차이는 엄격함과 느슨함이라고 할 수 있음.
==는 느슨한 동치 연산을 수행함
'=='의 같은경우 1과 "1"을 비교시 true로 반환하는데 그 이유는
==실행중 타입변환이 일어나 1과 "1"의 타입을 동일하게 하여 비교하기 때문.
===는 엄격한 동치연산을 수행
'=='와는 다르게 타입변환을 하지 않고 type과 값이 모두 같을경우 true를 반환
예시와 같이 상수에 변수를 할당하였다는 에러가 발생함.
const a = 3;
a = 5 // 에러 발생 Uncaught TypeError: Assignment to constant variable.
예시
const a = [];
a = [1,2,3] //Uncaught TypeError: Assignment to constant variable.
const b = [];
b.push(1)
console.log(b) // [1]
단어의 뜻은 끌어 올리기, 들어 올려 나르기
스코프 내부 어디서든 변수 선언은 최상위에 선언된 것 처럼 행동 하는것이 호이스팅
호이스팅은 스코프 단위로 발생됨을 유의!
var로 선언된 변수와 달리 let키워드로 선언된 변수를 선언문 이전에 참조하면 에러(Reference Error)가 발생.
console.log(foo) ; // undefined
var foo;
console.log(bar) ; //Uncaught ReferenceError: cosnole is not defined
var bar;
var키워드로 선언된 변수는 선언 단계와 초기화 단계가 한번에 이루어짐.
-> 스코프에 변수를 등록(선언단계)하고 메모리에 변수를 위한 공간을 확보한 후,
undefined로 초기화(초기화단계)한다. 따라서 변수 선언문 이전에 변수에 접근하여도 스코프에 변수가 존재하기 때문에 에러가 발생하지 않음. 다만 undefined를 반환. 이후 변수 할당문에 도달하면
비로소 값이 할당됨. 이것을 변수 호이스팅(variable Hoisting)이라고함.
let 키워드로 선언된 변수는 선언 단계와 초기화 단계가 분리되어 진행됨.
-> 스코프에 변수를 등록(선언단계)하지만 초기화 단계는 변수 선언문에 도달했을 때 이루어짐.
초기화 이전에 변수에 접근하려고 하면 참조 에러(ReferenceError)가 발생. 이는 변수가 아직 초기화 되지 않았기 때문. 풀어서 말하면 변수를 위한 메모리 공간이 아직 확보되지 않았기 때문에 참조에러가 발생. 따라서 스코프의 시작 지점부터 초기화 시점까지는 변수를 참조할 수 없다.
스코프의 시작 지점부터 초기화 시작 지점까지의 구간을 '일시적 사각지대(Temporal Dead Zone: TDZ)라고 부름.
예외 상황
-> let으로 선언된 변수는 블록 레벨 스코프를 가지므로 코드 블록{}내에서 선언된 변수는 지역 변수이며 지역변수 foo또한 대당 스코프에 호이스팅되고 코드 블록의 선두부터 초기화가 이루어지는 지점까지 TEZ에 빠져 Reference Error발생
let foo = 1; // 전역 변수
{
console.log(foo); // ReferenceError: Cannot access 'foo' before initialization
let foo = 2; // 지역 변수
}
선언 단계(Declaration phase)
->변수를 실행 컨텍스트의 변수 객체(Variable Object)에 등록. 이 변수 객체는 스코프가 참조하는 대상이 됨.
초기화단계 (Initialization Phase)
->변수 객체(Variable Object)에 등롣괸 변수를 위한 공간을 메모리에 확보 함.
이 단계에서 변수는 undefined로 초기화됨.
할당 단계(Assignment phase)
-> undefined로 초기화된 변수에 실제 값을 할당.
호이스팅에 대한 참조하는 아래에서 링크에서 참조.
https://poiemaweb.com/es6-block-scope#13-%ED%98%B8%EC%9D%B4%EC%8A%A4%ED%8C%85
변수 호이스팅과 비슷하지만 함수는 함수 선언문 방식일때만 호이스팅 됨. 함수 표현식은 호이스팅 되지 않음.
b()
function b() {
console.log("bbb")
}
b()
위 예제가 실행이 될 때 함수 자체가 맨위로 끌어 올려진다고 생각하면됨.
function b() {
console.log("bbb")
}
b()
b()
a()
let a = function() {
console.log("aaa")
}
첫번째 줄에서 에러발생: VM361:1 Uncaught ReferenceError: a is not defined
함수 선언식과 다르게 함수 표현식은 호이스팅이 되지 않아 함수가 위로 올라가지 않음.
스코프란 변수의 유효범위를 나타내는 용어
JS는 Lexcial scope를 따르고 있음.
함수를 어디서 선언하였는지에 따라 상위 스코프를 결정하는 것이며 함수의 선언에 따라 결정됨.
호출에 따라 범위가 결정되지는 않음
function init() {
var name = "Mozilla"; // name은 init에 의해 생성된 지역 변수이다.
function displayName() { // displayName() 은 내부 함수이며, 클로저다.
alert(name); // 부모 함수에서 선언된 변수를 사용한다.
}
displayName();
}
init(); // alert으로 Mozilla가 나옴.
a라는 객체의 주소값을 다른 b라는 객체에게도 동일하게 전달하여
a와 b가 동일한 주소값으로 데이터 저장소를 보게끔 하는 복사let a = [1,2,3] let b b = a //이때 a의 주소값을 b에게 전달.
요소 자체를 복사하지만 요소에 배열이나 객체등 주소를 참조하는 참조 타입이 있는경우 주소를 복사.
slice같은 경우 얕은 복사를 수행하여 아래와 같은 결과값이 나옴.
object.assign()의 경우도 얕은 복사를 수행.let a = [1,2,3,[4,5]] let b = a.slice()
a ===b // false
a[3] === b[3] // true.
## 깊은 복사
>요소 자체를 복사 시킨다. 만약에 요소에 배열이나 객체 등 주소를 참조하는 참조타입이 있다고 해도 전부 요소로 복사함.
```js
let a = [1,2,3,[4,5]]
let b = JSON.parse(JSON.stringify(a))
a[3] === b[3] // false
이것은 설명하자면 a라는 배열을 JSON.stringify()로 String타입으로 우선 바꿈
"1,2,3,[4,5]"
그 이후 JSON.parse()를 사용해서 문자열을 구분하여 배열로 바꿈
[1,2,3,[4,5]]가 됨.
이렇게 되면 a와 b는 완전 다른 주소값을 참조하는 깊은 복사가 됨.