
상태를 나타내는 프로퍼티와 동작을 나타내는 메서드를 하나의 논리적인 단위로 묶은 복합적인 자료구조
메서드는 객체에 포함된 것이 아니라 독립적으로 존재하는 별도의 객체이다!
프로퍼티가 함수 객체를 가리키고 있을 뿐!
동작을 나타내는 메서드는 자신이 속한 객체의 프로퍼티를 참조하고 변경할 수 있어야 한다.
👉 이때 자신이 속한 객체를 가리키는 식별자를 참조해야 한다!
👉 이것을 위해 this가 필요하다!
자신이 속한 객체 or 자신이 생성할 인스턴스를 가리키는 자기 참조 변수!
js엔진에 의해 암묵적으로 생성된다
function Circle(radius){
this.radius=raiuds; //이때의 this는 생성될 인스턴스를 가리킴
}
const circle={
radius: 0,
getRadius*({
console.log(this); //이때의 this는 메서드를 호출한 객체를 가리킴
}
}
const foo = function(){console.log(this);}
//일반 함수 호출에서의 this = 전역 객체 window
//strict mode에서는 undefined
foo(); //window
//메서드 호출에서의 this = 호출한 객체
const obj = {foo};
obj.foo(); //obj
//생성자 함수 호출에서의 this = 생성한 인스턴스
new foo(); //foo{}
const persone={
name: "홍길동",
foo(callback){setTeimout(callback, 100);}
//객체 리터럴 선언시의 this는 자기 자신을 가리킨다.
}
person.foo(function(){conosle.log(this)})
//콜백함수가 일반함수로 들어가 여기서의 this는 전역객체가 된다.
😧 콜백으로 들어오는 일반함수의 this도 콜백을 가져온 함수의 this와 일치시키려면..?
bind 메서드를 이용하자!
const person={ foo(callback){setTeimout(callback.bind(this), 100);} } person.foo(function(){conosle.log(this)}) //person
실행 컨텍스트는 소스코드를 실행하는 데 필요한 환경을 제공하고
소스코드의 실행 결과를 실제로 관리한다!
각 실행 컨텍스트들이 해당 스택에 순서대로 쌓여 코드 실행 순서를 관리한다!
실행 컨텍스트 가장 상위에 존재하는 컨텍스트!
스코프를 구분하여 식별자를 등록하고 관리한다!
스코프에 포함된 식별자를 등록하고 바인딩된 값을 관리한다.
소스코드 타입에 따라 관리내용에 차이 존재
상위 스코프(상위코드 렉시컬 환경)를 가리켜 스코프 체인 구현
전역 객체의 경우에는 [[GlobalThisValue]]가 되고, window를 참조한다!

전역 객체와 전역 실행 컨텍스트가 생성되고,
실행 컨텍스트 스택에 전역 실행 컨텍스트가 푸시된다!

전역 렉시컬 환경 객체가 생성되고,
전역 실행 컨텍스트의 렉시컬 환경에 전역 렉시컬 환경을 바인딩한다!

객체 환경 레코드와 선언적 환경 레코드가 생성되고, 전역 환경 레코드에 바인딩된다!
var 키워드로 선언된 것들 관리!
전역 객체와 연결되어 있어 객체 환경 레코드에 정의된 전역 변수와 함수들은 전역 객체의 프로퍼티와 메소드가 된다!
let, const 키워드로 선언된 것들 관리!
따라서 let과 const는 전역이지만!ㅠ전역 객체 프로퍼티가 되지 않고 개념적인 블록 내에 존재하게 된다!

전역 환경 레코드의 [[GlobalThisValue]] 내부 슬롯에 this가 바인딩된다!
여기서는 전역 객체이므로 전역 객체에 바인딩되었다!
전역(최상위)이므로 외부 렉시컬 환경에 대한 참조(상위 스코프)는 null!
변수들에 값이 할당되고 함수가 호출된다!
🚦 식별자 결정
동일한 이름의 식별자가 다른 스코프에 존재한다면 어떤 것을 참조해야 할지 결정하는 것!
식별자를 검색할 때는 실행중인 실행 컨텍스트에서부터 식별자 검색을 실행한다!
함수가 호출되면 코드 실행을 일시중단하고 함수 내부로 진입하여 함수 코드를 평가한다!
함수 코드 평가 과정은 전역 코드 평가 과정과 마찬가지로..
function foo(){ function bar(){...} //bar 함수 실행 컨텍스트가 실행컨텍스트스택에서 pop된다! //foo 함수 실행 컨텍스트가 실행중인 실행컨텍스트가 된다! }
😏 함수 실행 컨텍스트가 제거되어도....
bar 함수 렉시컬 환경 객체는 남아 있다! 두개는 별도의 객체니까~
😳 가비지 컬렉터가 없애지 않나요?
가비지 컬렉터는 누구에게도 참조되지 않는 값 혹은 객체의 메모리 공간을 해제한다!
따라서, foo함수 렉시컬 환경이 누군가에게 참조되어지고 있다면 소멸하지 않는다~~👉 이것을 응용한 것이 바로 클로저고, 클로저를 응용한 것이 useState !
😚 잊지 않았죠? 렉시컬(정적) 스코프는...
함수를 어디서 호출했는지가 아니라, 어디에 정의했는지에 따라 상위 스코프를 결정합니다!
js 엔진은 이렇게 결정한 상위 스코프를 함수 객체의 내부 슬롯 [[Environment]]에 저장해요~
새로운 렉시컬 환경 객체가 생성되어 기존 렉시컬 환경 객체를 교체한다!
참고로.. 기존 렉시컬 환경 객체도 아직 남아있는 상태.
😨 그럼 설마 for문은..?
그렇다!! 한번 돌아갈 때마다 렉시컬 환경을 생성한다!!
클로저는 JS고유 개념이 아니다!
함수와 그 함수가 선언된 렉시컬 환경의 조합!
함수를 일급객체로 사용하는 함수형 프로그래밍 언어에서 사용되는 특성이다.
😤 몇 번이고 말하는데요, 함수 호출 위치는...
함수의 상위 스코프에 아무런 영향을 주지 못합니다!
내부함수의 [[Environment]]가 참조중인 외부함수 렉시컬 환경 객체는 남아 있다!
function outer(){
const x=10;
const inner=function(){console.log(x)};
return inner;
}
const innerFunc=outer(); //inner를 반환하고 outer는 스택에서 pop!
innerFunc(); //inner를 실행하는데.. x가 살아 있다!
외부함수보다 중첩함수가 더 오래 유지되는 경우, 중첩함수는 이미 생명주기가 종료된 외부 함수의 변수를 참조할 수 있다!
👉 여기서 중첩함수(inner)가 바로 클로저!
🛹 자유 변수
위 예시에서 x가 바로 자유 변수!
클로저란 "자유 변수에 대해 닫혀있는 함수", 혹은 "자유 변수에 묶여있는 함수"로 표현할 수 있다.
상태를 안전하게 변경하기 위해 사용한다!
특정 함수에게만 상태 변경을 허용한다!
const increase=( function(){
let num=0;
return function(){return ++num;} //즉시실행함수
})
increase(); //increase가 pop되어도 return된 내부함수가 실행되므로, num은 살아있다!
increase(); //즉시실행함수는 한 번만 실행되므로, 리터럴 환경 객체를 새로 생성x! 계속 같은 num!
🔗 암묵적 결합
전역 변수는 누구나 접근할 수 있고, 누구나 변경할 수 있다.
function makeCount(aux){
let count=0;
return function(){ //즉시실행함수
counter=aux(counter);
return counter;
}
}
function increase(n){return ++n;} //보조 함수
function decrease(n){return --n;}
//둘은 서로 독립된 렉시컬 환경을 가지므로, count를 공유하지 않음!
const increaser=makeCount(increase);
const decrease=makeCount(increase);
//하나의 렉시컬 환경, 하나의 자유변수를 공유하게 해야 한다
counter(increase);
counter(decrease);
👉 객체로 정의된 외부함수 수만큼 렉시컬 환경이 생성되고, 공유되지 않는다!
const Person = (function(){
let age = 0; //private
function Person(propname, propage){ //생성자 함수
this.name=propname; //public
age=propage;
}
Person.prototype.sayHi=function(){ //단 한번 생성되는 클로저!
console.log(`내 이름은 ${name}이고 ${age}살입니다");
}
return Person; //생성자함수를 반환
}
const me = new Person("홍길동", 20);
const you = new Person("김길동", 30);
me.sayHi(); // 내이름은 홍길동이고 30살입니다
👉 sayHi가 단 한번만 정의되기 때문에, 렉시컬 환경 객체도 한번만 생성되어 모든 객체가 공유한다!
👉 그냥 private를 씁시다. 이제는 JS도 private를 지원한다.
js에서 클래스는 값처럼 사용할 수 있는 일급 객체인 함수를 의미한다!
클래스 내 모든 코드는 암묵적으로 strict mode가 지정되어 해제가 불가능함!
다만, let/const처럼 TDZ(템퍼럴 데드 존)이 존재하여 호이스팅이 되지 않는 것처럼 보인다!
const Person = class MyClass{};
const me = new Person(); //오류x
const you = new MyClass(); //오류o



class Person{
constructor(name){
this.name=name; //인스턴스 프로퍼티
}
//접근자 프로퍼티
get fullName(){ return name; }
set fullName(name){this.name=name;}
}
class Person{
this.name=''; //에러!
}
😎 private 정의하기
private는 constructor가 아닌 클래스 몸체에 직접 정의해야한다!
class Person{ #privateName=''; //해당 함수 내부에서만 접근 가능 publicName=''; constructor(privateName, publicName){ this.#privateName=privateName; this.publicName=publicName; } }
Person.prototype 객체는 Children.prototype 객체의 상위 프로토타입 체인([[Prototype]])에 존재한다.

생성자 함수를 상속받을 수도 있다!
class Children extends Person{}
const test = new Children(1);
상위클래스 prototype를 가리키는 키워드([[Prototype]]처럼!)
class Base{
constructor(a){
this.a=a;
}
sayHi(){
return "hi!";
}
}
class Derived extends Base{
constructor(a,b){
super(a);
this.b=b;
}
sayHi(){
return super.sayHi();
}
}
construct dervied=new Derived(1,2);
👉 서브클래스에서 constructor를 생략하지 않는다면, 서브클래스 constructor에서는 반드시 super를 호출해야 한다!
🙄 왜 constructor에서 super를 호출해야 하나요?
서브클래스로 인스턴스를 생성하면, 서브클래스가 직접 생성하는 것이 아니라 수퍼클래스에게 인스턴스 생성을 위임한다.
따라서 서브클래스에서 super로 수퍼클래스 constructor를 반드시 가져와주어야 한다!
ES6에서는 함수를 아래 세 가지로 구분하였다.
메서드 축약 표현으로 정의된 함수만을 의미한다.
const obj={
x:1,
foo() {return this.x;}, //메서드
bar: function() {return this.x;} //일반 함수
}
const arrow1 = (x,y) => {..};
const arrow2 = x => x**2;
const arrow3 = () => {..};
const arrow4 = (x=>({sayHi(return x;)}))(x);
console.log(arrow4.sayHi("hi");); //hi
🍜 렉시컬 this
렉시컬 스코프와 같이 화살표 함수 내 this는 함수가 정의된 위치에 의해 결정되는 것을 말한다!
일반적으로 클래스 생성자의 내부에서 화살표 함수를 사용하면, this는 상위 스코프인 전역이 된다.
그러나 this.name으로 프로퍼티를 선언해 주면 this에 생성할 인스턴스가 바인딩되므로,
화살표 함수에서의 this는 생성할 인스턴스가 된다.
class Person{
constructor(){
this.name="홍길동"ㅣ
this.sayHi=()=>console.log(this.name); //홍길동
}
}
화살표 함수에는 없는 arguments 대신 여러 인자를 받아올 때 사용하는 파라미터.
함수에 전달된 인수들의 목록을 배열로 전달받는다.
👉따라서, 유사배열객체인 arguments와 달리 배열 메서드를 사용할 수 있다!
function foo(...rest){
console.log(rest);
}