느슨한 타입(Loosely Typed)의 동적언어
장점
단점
⇒ 정적 타입 체크와 강력한 문법을 추가한 TypeScipt나 Flow 등을 사용
// 형변환 (type Conversion)
// 문자로 : String | 숫자로 : Number | 불린으로 : Boolean
console.log("10" + "5"); //문자열 105
console.log(10 + 5); //숫자형 15
console.log(Number("10") + Number("5")); // 숫자열 15
console.log(String(10) + String(5)); // 문자열 105
// 숫자 -> 문자
let x = 123;
console.log(x); //123
console.log(String(x)); //123
console.log(typeof x); //number
console.log(typeof String(x)); //string
// 불린 -> 숫자 (0과 1이 바뀜)
let y = true;
console.log(y); //true
console.log(String(y)); //true
console.log(typeof y); //boolenan
console.log(typeof String(y)); //string
// 문자 -> 숫자
let x = "문자";
console.log(x); // 문자
console.log(Number(x)); // NaN (: 문자열이라도 숫자형태의 문자열이라면 자연스럽게 숫자형으로 보여짐<if x = "123";)
console.log(typeof x); // string
console.log(typeof Number(x)); // number
// 불린 -> 숫자
let y = true;
console.log(y); // true
console.log(Number(y)); // 1 (불린값은 숫자형태로 형변환될떄 0,1로 숫자가 바뀐다)
console.log(typeof y); // boolean
console.log(typeof Number(y));
// 문자 -> 불린
let x = "문자";
console.log(x); // 문자
console.log(Boolean(x)); // true
console.log(typeof x); // string
console.log(typeof Boolean(x)); // Boolean
// 숫자 -> 불린
let y = 123;
console.log(y); //123
console.log(Boolean(y)); // true
console.log(typeof y); // number
console.log(typeof Boolean(y)); // boolean
// false로 되는경우 (falsy:없거나 비어있는경우) / 대부분은 true
// "" | 0 | NaN
console.log('4' - true); // 3
// 산술연산
console.log(4 + "2"); // "42"
// (+): 순서에 상관없이 어느 한쪽에 문자열이 있다면 → 모두 문자열로 바꾼담 문자열로 동작
console.log(4 + 2); // 6
console.log(4 - true); // 3
console.log(4 * false); // 0
console.log(4 / "2"); // 2 (숫자로)
console.log("4" ** true); // 4
console.log(4 % "two"); // NaN
특별한 경우가 아니면 두값을 모두 숫자로 바꿔 비교
console.log(2 < "3"); //true
console.log(2 > true); //true
console.log("2" <= false); //false
console.log("two" >= 1); //false
일치비교는 형변환이 일어나지 않지만 동등비교는 숫자형태로 형 변환이 일어나기 때문
console.log(1 === "1"); // 일치, 불일치(!==)
console.log(1 === true);
console.log(1 == "1"); // 동등, 부등(!=)
console.log(1 == true);
/*
일치비교는 형변환이 일어나지 않지만
동등비교는 숫자형태로 형 변환이 일어나기 때문
===가 안전하게 비교하는데 도움이 된다!
*/
==
: 동등비교일 때는 둘다 비슷한 의미를 가지고 있어서 true가 출력
===
: 일치비교를 하게 되면 두값이 서로 다른 자료형이기 때문에 false가 출력된다
// null과 undefined (자료형)
// 둘다 값이없다는 뜻
// null(의도적으로 표현할떄 사용하는값) | undefined(값이 없다는것을 확인하는 값)
console.log(null == undefined); // true
console.log(null === undefined); // false
의도적으로 '값이 없는 상태'를 표현하려면 반드시 null을 사용할것을 권장드림니다.
undefined
: 선언한다음 값을 정해주지않음 (지정된 값이 없다)
null
: 의도적으로 값이 비어있다는걸 표현할때!
불변성(Immutability) : 객체가 생성된 이후 그 상태를 변경할 수 없는 상태
원시 타입(primitive type)은 불변한다! 변수에 할당할 때 완전히 새로운 값이 만들어져 재 할당
원시 타입 이외의 모든 값은 객체(Object)타입(=참조형)이며 객체타입 은 변경 가능한 값(mutable value) 입니다...
∴ 객체는 새로운 값을 다시 만들 필요없이 직접 변경이 가능
→ 변수에 객체값을 할당한경우에는 특별하게 동작
→ 객체값이 어딘가에서 만들어지고 => 변수에는 그 객체값으로 가는 주소가 저장
💡 객체값이 수정되는게 아니라 주소가 수정!! 같은주소를 갖고있는 애들은 다 같이 수정!
let string = 'HI'; // string(주소)에 HI가 저장
console.log(string) // HI
string = '바보'; // 주소가 변경
console.log(string) // 바보
let arr = [];
console.log(arr.length); // 0
let potato = arr.push(2);
console.log(arr.length); // 1
🔼 복사본을 리턴하는 문자열의slice()
와는 달리 배열(객체)의 메소드 push()
는 배열 원본 자체를 변경해줌다...
객체를 값으로 갖는 모든 프로퍼티에 대해 재귀적으로
Object.freeze()
메소드를 호출!
function deepFreeze(target){
// 객체가 아니거나 동결된 객체는 무시하고 객체이고 동결되지 않은 객체만 동결
if(target && typeof target === 'object' && !Object.isFrozen(target)){
Object.freeze(target)
// 모든 프로퍼티를 순회하며 재귀적으로 동결, Object.keys메소드는 객체 자신의 열거 가능한 프로퍼티 키를 배열로 반환.
Object.keys(target).forEach(key => deepFreeze(target[key]))
}
return target;
}
const person = {
name : 'miinii',
address : {city : 'seoul'}
}
// 깊은 객체 동결
deepFreeze(person)
console.log(Object.isFrozen(person)) // true
// 중첩객체까지 동결
console.log(Object.isFrozen(person.address)) // true
person.address.city = 'anyang';
console.log(person) // { name: 'miinii', address: { city: 'seoul' } }
객체를 프로퍼티 값으로 갖는 객체의 경우 얕은복사는 한 단계까지만 복사하는것 깊은복사는 객체에 중첩되어 있는 객체까지 모두 복사하는것!
얕은복사와 깊은복사로 생성된 객체는 원본과는 다른 객체!(= 원본과 복사본은 참조값이 다른 별개의 객체!) 하.지.만 얕은복사는 객체에 중첩되어 있는 경우 참조값을 복사 / 깊은복사는 객체에 중첩되어 있는 객체까지 모두 복사해서 원시값 처럼 완전한 복사본을 만듦!!
const v = 1;
// 깊은복사
const c1 = v;
console.log(c1 === v); //true
const o = { x : 1 };
// 얕은복사
const c2 = o;
console.log(c2 === o); //true
이해하고 더 알아볼것~~
스코프 : 모든 식별자(변수이름, 함수이름, 클래스이름 등) 자신이 선언된 위치에 의해 다른 코드가 식별자 자신을 참조할 수 있는 유효범위가 결정! (= 스코프는 식별자가 유효한 범위)
함수의 매개변수는 함수 내에서만 참조할 수 있고 함수 외부에서는 참조할수 없다
=> 매개변수의 스코프가 함수 내부로 한정 되기 때문!
let
과 const
키워드로 선언한 변수는 if, for, function 등등 어떤 키워드와 관계없이 코드 블록, 즉 {} 중괄호로 감싸진 부분을 기준으로 scope를 갖게 되지만, var
키워드로 선언한 변수는 scope가 function에서만 구분
스코프가 필요한 이유@
변수가 끌어올려 지는 현상을 '호이스팅(hoisting)'이라고 부른다! 고나마 다행인건 호이스팅은 선언과 동시에 값을 할당하더라도, 선언문만 올려짐...
console.log(myVariable); // undefined
var myVariable;
함수를 한 번 선언하고 나면 어디서든 유연하게 사용할 수 있다는 장점이 있지만, 코드의 흐름에는 부정적인 영향을 끼친다.. 그래서 함수를 선언할 떄는 가급적 코드 윗부분에 선언하거나, 호출을 항상 아래쪽에서 한다거나 나름대로 규칙을 정해놓고 만들기!
!ES6에서 도입된 let
, const
, class
를 사용한 선언문은 호이스팅 현상 발생하지 않는 것처럼 동작!
스코프의 시작 지점부터 초기화 시작지점까지 변수를 참조할 수 없는 구간을 TDZ라고 부름
console.log(foo); // ReferenceError : foo is not undefined
let foo; // 변수선언문에서 초기화 단계가 실행
console.log(foo); // undefined
foo = 1; // 할당문에서 할당 단계가 실행
console.log(foo); // 1
변수에 할당되는 값이 함수 리터럴인 문!-> 함수표현식은 변수선언문과 변수 할당문을 한번에 기술한 축약표현!
// 함수 참조
console.dir(add); // f add(x,y)
console.dir(sub); // undefined
// 함수 호출
console.log(add(2,5)); // 7
console.log(sub(2,5)); // TypeError: sub is not a function
// 함수 선언문
function add(x, y) {
return x + y;
}
// 함수 표현식
var sub = function (x,y) {
return x - y;
};
∴ 변수 선언은 런타임 이전에 실행되어 undefined로 초기화 되지만 변수 할당문의 값은 런타임에 평가됨! -> 함수표현식의 함수 리터럴도 할당문이 실행되는 시점에 평가되어 함수 객체가 됨...
함수표현식으로 함수를 정의하면 함수 호이스팅이 발생하는 것이 아니라 변수 호이스팅이 발생
함수 호이스팅은 함수를 호출하기 전에 반드시 함수를 선언해야한다는 규칙을 무시한다! 그래서 함수선언문대신 함수 표현식 사용할것을 권장!
var
의 문제점?var myVariable = '보라돌이';
console.log(myVariable); // 보라돌이
var myVariable = '뚜비!';
console.log(myVariable); // 뚜비!
{ let x = 3; }
function myFunction() {
let y = 4;
}
console.log(x); // 3
console.log(y); // Uncaught ReferenceError: y is not defined
⭐️ 함수를 기준으로만 적용되는 스코프를 함수 스코프, 코드 블록을 기준으로 적용되는 스코프를 블록 스코프
console.log(myVariable); // undefined
var myVariable = 2;
console.log(myVariable); // 2
코드가 실행되려면 스코프, 식별자, 코드실행 순서등의 관리가 필요한데 실행 컨텍스트가 이 일을 한대요
∴ 소스코드를 실행하는데 필요한 환경을 제공하고 코드의 실행결과를 실제로 관리
구체적으로 말하면...
식별자(변수,함수, 클래스 등의 이름)를 등록하고 관리하는 스코프와 코드 실행 순서를 관리를 구현한 내부 매커니즘! 모든 코드는 실행컨텍스트를 통해 실행되고 관리
일단 이렇게만 알고.... 담에 더 자세히 다뤄보자..
실행 컨텍스트 스택을 콜스택이라고 부르기도 한대용!
자바스크립트 엔진은 먼저 전역코드를 평가하여 전역 실행 컨텍스트를 생성 하는디 함수가 호출되면 함수 코드를 평가하여 함수 실행 컨텍스트를 생성하고 얘네들을 스택 구조로 관리 한다고 합니다~~
함수내 함수가 정의된것을 함수 중첩이라고 한답니다. 중첩된 함수의 스코프의 레퍼런스를 차례대로 저장하고, 각각의 스코프가 어떻게 연결되어 있는지를 보여준대용. 또한 해당 코드레벨에 참조값이 없으면 상위 레벨의 스코프로 참조값을 찾으러 여행을 떠나는 현상...
직접적으로 변경되면 안되는 변수에 대한 접근을 막는것을 은닉화라고 한다.
(function hi() {
let a = 'hello;
})() // hi is not defined
function a(){
let temp = 'a'
return temp;
}
// console.log(temp) error: temp is not defined
const result = a()
console.log(result); //a
위 함수 내부적으로 선언된 temp에는 직접적으로 접근을 할 수 없습니다. 함수 a를 실행시켜 그 값을 result라는 변수에 담아 클로저를 생성함으로써 temp의 값에 접근이 가능합니다.
이렇게 함수 안에 값을 숨기고 싶은 경우 클로저를 활용해볼 수 있습니다.