JS는 객체 기반의 프로그래밍 언어이다.
원시 값(Primitive type)을 제외한 나머지 값(함수, 배열, 정규 표현식 등) 모두 객체다
원시 타입의 값, 원시 값은 변경 불가능한 값 immiutable value
객체타입의 값은 mutable value이다.
Property는 속성이라는 뜻으로 JS에서 객체 내부의 속성을 의미한다.
JS의 객체는 0개 이상의 Property의 집합이다.
각 Property는 key와 value의 쌍으로 구성되어있다
var person = { name : "홍길동", age : 45 }
property key {name, age} = 문자열이거나 symbol 값이 될수 있다
property value {”홍길동”, 45} = 자바스크립트에서 value로 인식되는 모든 것
value에는 함수도 나올 수 있다 (메소드)
//property key에는 메서드가 들어갈 수 있다
var obj = {
'name': '홍길동',
'printName': function myPrint(){
//현재 사용하는 객체를 지칭하는 것 this
console.log(`내이름은 ${this.name}`);
},
myphone : '10-484848',
"@myphone" : '10-484848' //naming rule에 부합되지 않는 경우 "" 사용
};
console.log(typeof obj); //object
JS 객체는 함수와 밀접한 관계를 가진다. 객체의 집합으로 프로그램을 표현하려는 프로그래밍 패러다임을 객체지향 프로그래밍이라고 한다.
json은 객체가 아니다.
JavaScript Object Notation의 약자로 서버와 데이터를 교환할 때 사용하는 데이터 표준 형태이다.(자바스크립트 객체와 헷갈리지 말기)
Json은 타입이 String인 반면 JS Object는 Object 타입으로 함수를 할당 할 수 있다.
Object.create()
메소드JS에서 데이터타입은 크게 두가지로 나뉘어졌었다.
변수 : 하나의 값을 저장하기 위해 확보된 메모리 공간자체
값 : 변수에 저장된 데이터
변경 불가능 한 것은 변수가 아니라 값을 뜻한다
원시 값은 변경 불가능한 값. 읽기 전용 값이다.
변수가 참조하던 메모리 공간의 주소가 변경된 이유는 변수에 할당된 원시값이 변경 불가능하기 때문이다.
새로운 메모리공간 확보 -> 재할당한 값 저장 -> 변수가 참조하던 메모리 공간 주소 변경 이러한 특성을 불변성이라고 한다.
불변성을 갖는 원시 값을 할당한 변수는 재할당 이외에 변수 값을 변경할 수 있는 방법이 없다
//primity vlaue값은 절대 바뀌지 않는다
//유사배열 객체 Array-life object
let myStr = 'Hello'
myStr[0] = 'h'
console.log(myStr); //Hello
재할당 없이 프로퍼티를 동적으로 추가할 수도 있고 프로퍼티 값을 갱신할 수도 있으며 프로퍼티 자체를 삭제할 수도 있다
- JS는 함수를 기반으로 한 언어다
- 함수는 함수 이름으로 호출하지 않는다
- 함수에 대한 식별자를 이용하여 호출한다
일련의 과정을 수행하기 위한 satement를 {}(중괄호)를 이용해서 하나의 실행단위로 만들어 놓은 것이다
함수를 정의(defintion)해야한다 (추상메서드는 함수를 선언)
function add(x,y) {return x + y; }
함수를 호출한다 (call, invoke)
add(3,5);
JS함수는 객체 타입의 값이다. 따라서 숫자 값을 숫자 리터럴로 생성하고 객체를 객체 리턴으로 생성하는 것처럼 함수도 함수 리터럴로 생성 할 수 있다.
리터럴은 어떤 값을 표현하기 위한 기호, 변수에 저장되지 않으면 사용할 수 없다
var func = fucntion add ( x, y){ return x + y; }
fucntion add ( x, y){ return x + y; }
이 함수 리터럴이다
add
함수이름은 식별자(identyfier)이다
- 함수 이름은 함수 내에서만 사용 가능하다 (외부로 노출되지 않는다)
- name function 기명함수: 이름이 있는 함수
- anunemous function 익명 함수: 이름이 없는 함수
//기명함수
var myFunc = function add(x,y){
return x + y;
}
//js에서 함수는 함수 내에서만 사용 가능하다
console.log(add(3,5)); //error: not defined
//식별자를 사용해야 호출이 가능
console.log(myFunc(3,5)); //8
//함수는 함수 이름으로 호출하는게 아니라 식별자로 호출한다
//무기명 함수
var myFunc = function (x,y){
return x + y;
}
리터럴은 값을 생성하기위한 표기법이다. 따라서 함수 리터럴도 평가되어 값을 생성하며, 이값은 객체다. 즉, 함수는 객체다
일반객체는 호출 할 수 없지만 함수는 호출할 수 있다
JS엔진은 생성된 함수를 호출 하기 위해 함수 이름과 동일한 이름의 식별자를 암묵적으로 생성하고 거기에 함수 객체를 할당한다
함수 선언문
변수를 선언하지 않는다
fucntion add ( x, y){ return x + y; }
함수 표현식
var myFunc = fucntion add ( x, y){ return x + y; }
Function()
생성자 함수 → 쓰지말기
var add = new Fucntion('x', 'y') 'return x + y' }
ES6 화살표 함수
var add = ( x, y) => x + y;
code
//함수 선언문
function foo(){
console.log('foo 함수');
}
//함수 선언문
(function bar(){
console.log('bar 함수');
})
foo();
//함수 선언문
function add(x,y){
// arguments
return x + y;
}
console.log(add(2,5)); //7
console.log(add(2)); //NaN(연산불가) - 호출가능
console.log(add(2,4,6)); //NaN(연산불가) - 호출가능
//함수 내부에 들어가 있는 인자를 보관하는 변수 - arguments 라는 property가 있다
- 모든 식별자는 호이스팅이 일어난다 문제는 어떻게 일어나는지 파악하기
- 함수 선언문: 런타임 이전에 만들어진다
- 함수 표현식: 런타임 후에 만들어진다
JS에서는 함수 표현식을 사용하는것이 좋다
선언문은 호이스팅 되어서 사용될 수 있기 때문
foo(); //호출된다
add(); //error : not a function
//함수 선언문 -> 런타임 이전에 만들어짐
function foo(){
console.log('foo 함수');
}
//함수 표현식 -> 런타임 후에 만들어진다
var add = function bar(){
console.log('bar함수');
}
function add(){
// arguments [2,4,6]
let sum=0;
for(let i=0; i<arguments.length; i++){
sum += arguments[i];
}
return sum;
}
console.log(add(2,5)); //7
console.log(add(2));
console.log(add(2,4,6));
//IIFE : 즉시실행 함수
(function(){
let x = 10;
let y = 20;
console.log(x + y);
}());
중첩함수를 가지고 있는 바깥쪽 함수로 외부함수
모든 스코프는 체인으로 얽혀있다
전역변수를 계속 사용하게 되면 느려짐. 가능한한 지역 변수를 사용하도록 한다
var x = 100; //global scope (전역변수)
var y = 200; //global scope (전역변수)
//outer function
function outer(){
let x = 0; // function level scope(지역변수)
//inner function
function inner(){
let x = 10; // function level scope(지역변수)
console.log(y);
}
}
//잘만든 함수가 존재하는데 이 함수의 기능을 변경하는 방법
// 1. 원래 그 함수를 수정한다
// 2. 함수를 새로 만든다
// 3. 함수를 추상화 시켜서 인자로 받아들여서 사용
// 고차함수 Higher-Ordered Function
function repeat(n, f){
for(var i=0; i<n; i++){
//입력으로 들어온값을 추상화 시켜서 사용
f(i);
}
}
//함수 표현식
let logAll = function(j){
console.log(j);
}
let logOdd = function(k){
if(k%2){
console.log(k);
}
}
//식별자로 주솟값을 넘겨서 실행시킨다
repeat(6, logOdd , logAll);
Javascript Engine은 코드를 실행할때 문맥(context)을 고려해서 실행한다
현재 실행중인 코드가 어디에 있는 코드이고 코드 주변 정보를 파악해서 실행한다
⇒ lexical Enviroment → excution context
함수가 호출됐을때 Scope를
- 함수가 호출된 것을 기준으로 설정한다 ⇒ 동적 스코프 Dynamic Scope
- 함수가 정의된 곳을 기준으로 설정 ⇒ 정적 스코프 Static scope, Lexical Scope
- 정적 스코프는 거의 대부분의 언어에 적용된다 (js 포함)
- lexical, scope를 사용
렉시컬 스코프란(Lexical Scope)란?
함수를 어디서 호출하는지가 아니라 어디에 선언하였는지에 따라 결정되는 것을 말한다.
즉, 함수를 어디서 선언하였는지에 따라 상위 스코프를 결정한다는 뜻이며, 가장 중요한 점은 함수의 호출이 아니라 함수의 선언에 따라 결정된다는 점이다.
다른 말로, 정적 스코프(Static scope)라 부르기도 하다.그 이유는?
자바스크립트에서는 위와 같은 코드를 작성할 때, 이미 실행 단계에서 코드들의 스코프를 결정한다.
- global 범위에 있는 변수 x
- first() 함수 안에 있는 변수 x
- second() 함수 안에 있는 변수 x
자바스크립트는 렉시컬 스코프(Lexical Scope)를 따르므로 함수를 선언한 시점에 상위 스코프가 결정된다.
즉, 이 말은 함수를 어디에서 호출하였는지는 스코프 결정에 아무런 의미를 주지 않는다는 말이다.
var x = 1; //전역 scope의 전역 변수
function foo(){
var x = 10; //지역 scope의 지역 변수
bar();
}
function bar(){
console.log(x);
}
foo();
- 전역 스코프 Gloval scope
전역에 선언되어 있어 어느 곳에서든 해당 변수에 접근이 가능하다- 지역 스코프 Local scope
해당 지역 에서만 접근이 가능한 변수
함수 안에 있는 선언들을 모두 끌어올려서 해당 함수 유효 범위의 최상단에 선언하는 것을 말한다.
호이스팅이란
- 자바스크립트 함수는 실행되기 전에 함수 안에 필요한 변수값들을 모두 모아서 유효 범위의 최상단에 선언한다.
- 자바스크립트 Parser가 함수 실행 전 해당 함수를 한 번 훑는다.
- 함수 안에 존재하는 변수/함수선언에 대한 정보를 기억하고 있다가 실행시킨다.
- 유효 범위: 함수 블록 {} 안에서 유효
즉, 함수 내에서 아래쪽에 존재하는 내용 중 필요한 값들을 끌어올리는 것이다.- 실제로 코드가 끌어올려지는 건 아니며, 자바스크립트 Parser 내부적으로 끌어올려서 처리하는 것이다.
- 실제 메모리에서는 변화가 없다.
호이스팅의 대상
- var 변수 선언과 함수선언문에서만 호이스팅이 일어난다.
- var 변수/함수의 선언만 위로 끌어 올려지며, 할당은 끌어 올려지지 않는다.
- let/const 변수 선언과 함수표현식에서는 호이스팅이 발생하지 않는다.
참고자료
https://ordinary-code.tistory.com/142
https://gmlwjd9405.github.io/2019/04/22/javascript-hoisting.html
https://ljtaek2.tistory.com/145