1. 구조 분해 할당

배열이나 객체를 분해하여 저장된 값을 개별 변수에 원하는대로 담을 있는 문법입니다.

2. 배열 분해 할당하기

1) 배열 분해 할당

var arr = ["kim", "park", "lee"];

var [a, , c, d = "empty"] = arr; //배열 구조 분해 할당

console.log(a, c, d); // kim lee empty
arr 배열에 "kim", "park", "lee" 값이 들어있고, arr 배열을 구조 분해 할당해서 변수 a, c, d에 삽입합니다.
배열에 순서와 선언된 변수 순서에 맞게 분해 할당됩니다.
해당 자리가 비어있다면, 할당되지 않으며, 값이 할당되지 않았을 경우 디폴트 값을 설정할 수 있습니다.

 

2) "..." 연산자로 나머지 값 저장하기

var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

var [n1, n2, ...narr] = numbers;

console.log(n1, n2, narr);// 1 2 [3, 4, 5, 6, 7, 8, 9, 10]
배열의 앞 부분 몇개와 나머지 값들을 배열에 저장할 때 사용합니다.  

 

3) 배열 합치기

var a1 = [1, 2, 3];
var a2 = [4, 5, 6];
var a3 = [...a1, ...a2];

console.log(...a3, ...a2); //1 2 3 4 5 6 4 5 6
console.log(a3); //[1, 2, 3, 4, 5, 6]
"..." 연산자는 배열을 펼치는 연산자입니다. "..." 연산자를 사용해서 a1배열과 a2배열을 합쳐서 a3배열을 만들 수 있습니다. 

 

3. 객체 분해 할당하기

1) 객체 분해 할당

var person = {
    id: 35,
    name: "lee",
    age: 21,
    hobby: ["reading", "game", "bike ride"]
};

var {name, id, age, hobby, join = false} = person;

console.log(id, name, age, hobby, join); // 35 "lee" 21 ["reading", "game", "bike ride"] false

객체 분해 할당은 배열 분해 할당 방법과 비슷하다. 차이가 있다면 분해 할당을 선언하는 부분이 [ ... ]이 아니고 { ... } 입니다.
그리고 디폴트 값을 선언할 수 있으며, key 값만 맞다면 변수 선언의 순서가 달라도 상관없습니다. 

 

2) 객체의 나머지 연산

var person = {
    id: 35,
    name: "lee",
    age: "21",
    hobby: ["reading", "game", "bike ride"]
};

var {id, ...data} = person;

console.log(id, data); // 35 {name: "lee", age: "21", hobby: Array(3)}
배열 분해 할당과 마찬가지로 "..." 연산자를 통해 기본 변수와 객체에 원하는 만큼 나누어 할당할 수 있습니다. 

 

3) 객체 합치기

var person = {
    id: 35,
    name: "lee",
    age: "21",
    hobby: ["reading", "game", "bike ride"]
};

var extraInfo1 = {
    nationality: "KOREA",
    introduce: "Hi !"
};

var extraInfo2 = {
    a: "GOOD",
    b: "NICE"
};


var info = {...person, extraInfo2, ...extraInfo1};

console.log(info);
/*
{
  age: "21"
  extraInfo2: {a: "GOOD", b: "NICE"}
  hobby: ["reading", "game", "bike ride"]
  id: 35
  introduce: "Hi !"
  name: "lee"
  nationality: "KOREA"
}
*/

 

4) 중첩 객체 분해 할당

let config = {
    option: {
        a: true,
        b: false
    },
    arr: ["GOOD", "PERSON"],
};

var {
    option: {
        a,
        b
    },
    arr: [str1, str2],
    name = "app"
} = config;

console.log(name, a, b, str1, str2); // app true false GOOD PERSON

참조 :

https://ko.javascript.info/destructuring-assignment

1. 객체를 만드는 두가지 방법

1) 객체 리터럴을 이용한 방법

객체 리터럴은 {...}을 사용해서 객체를 생성합니다. 
var obj = {
    name: "kim",
    getName: function () {
        console.log(this.name);
    }
};

console.log(obj.name);
obj.getName();


2) 생성자 함수를 이용하는 방법

1) 관례상 함수 이름의 첫글자는 대문자로 시작합니다.
2) 함수 호출 시 "new" 연산자를 붙여서 실행합니다.
function Person(name) {
    this.name = name;
    this.showName = function () {
        console.log(`My name is ${this.name}`);
    };
}

var person = new Person("Son");
person.showName();

 

2-1) 함수 앞에 new를 붙일 경우

new Person("name")을 사용해서 함수를 실행하면 아래와 같은 알고리즘이 동작합니다.
1) 빈 객체를 생성하고 this가 빈 객체를 가리키게 합니다.
2) 함수를 실행합니다. 
3) this를 반환합니다.
function Person(name) {
// 빈 객체가 생성되고 this에 저장합니다.
// this = {};

// 함수가 실행됩니다. 
// this는 객체({})기 때문에 this.name = name 구문은 객체에 name 값을 넣는 것과 같습니다.
    this.name = name;
    this.showName = function () {
        console.log(`My name is ${this.name}`);
    };
    
// return this;
}

var person = new Person("Son");
person.showName();

생성자 함수를 사용하는 이유는 객체를 재사용하기 위함입니다. 리터럴 객체를 만드는 방법보다 수고를 덜 수 있지요.

var person01 = new Person("kim");
var person02 = new Person("park");
var person03 = new Person("lee");

2-2) 생성자 함수의 return문

생성자 함수에서 명시적으로 return문을 사용할 경우 다음과 같은 값을 반환합니다.

return문이 없을 경우

this를 반환한다.

return 값이 객체일 경우

해당 객체를 반환한다.

return 값이 기본 변수일 경우

기본 변수 반환을 무시하고 this를 반환한다. 

참조

https://ko.javascript.info/constructor-new

1. Prototype이란?

자바스크립트의 모든 객체는 부모 객체와 연결되어 있습니다. 최상위 객체는 Object 객체이며, 해당 객체의 모든 메소드를 상속받습니다. 이 때 부모 객체를 가리켜 Prototype 객체라고 합니다.

 2. Prototype을 사용하는 이유

1) 생성자 함수로 생성된 객체에 공통 변수와 메소드를 공유할 수 있습니다.
2) 상속을 구현할 수 있습니다.

3. __proto__

__proto__ 는 prototype 객체를 가리키는 프로퍼티입니다. 모든 객체가 이 프로퍼티를 가지고 있습니다.

prototype 객체는 다음과 같은 데이터를 저장하고 있습니다.

1) constructor
   생성자 함수에 대한 데이터를 저장하는 객체입니다.
2) 공통 메소드와 공통 변수
    해당 프로토타입으로 생성된 객체들은 공통 메소드와 공통 변수에 접근할 수 있습니다.
3) __proto__
   상위 프로토타입을 가리킵니다 (prototype link)
    function Person(n) {
        this.name = n;
    }

    Person.prototype.getName = function () {
        return `My name is ${this.name}`;
    };

    var person = new Person("kim");


    console.log(person);
    console.log(person.getName());
Person 객체를 선언하고, 생성자 함수 호출 시 this.name을 매개변수 n으로 초기화합니다.
그리고 Person.prototype에 getName 공통 메소드를 선언했습니다. 공통 메소드를 선언하면 Person 생성자 함수로 생성된 객체들은 모두 getName 메소드에 접근할 수 있습니다. 

person 변수에 Person 생성자 함수를 호출하면 새로운 Person객체가 생성됩니다.  
console.log의 출력 결과는 다음과 같습니다.

Person 생성자 함수를 통해 생성한 객체의 정보입니다. name과 __proto__가 저장되어 있는 것을 알 수 있습니다. 
그리고 __proto__에는 공통 메소드인 getName()과 constructor 그리고 __proto__가 저장되어있습니다.
__proto__는 모든 객체의 상위 객체인 Object 프로퍼티를 가리킵니다.

4. prototype chain

객체의 프로퍼티에 접근하려면 객체.프로퍼티이름으로 접근할 수 있습니다. 그런데 해당 프로퍼티가 없으면 부모 객체의 프로퍼티를 참조합니다. 부모의 프로퍼티를 참조한다는 것은 객체의 __proto__를 참조해서 해당 프로퍼티가 있는지 확인하고 없으면 상위 __proto__를 참조합니다. 이것을 prototype chain이라고 합니다. 

prototype chain의 종점은 Object.prototype 입니다. 만약 여기에도 찾고자하는 프로퍼티가 없다면 오류를 발생시킵니다.

 

5. 객체 상속

자바스크립트에서 상속을 구현하려면 prototype 객체를 이용해야 합니다.

    function Person(n) {
        this.name = n;
    }

    Person.prototype.getName = function () {
        return `My name is ${this.name}`;
    };

    function Student(n, i) {
        this.name = n;
        this.id = i;
    }

    Student.prototype = new Person(); //Person 객체 상속
    Student.prototype.constructor = Student; // 생성자 함수는 Student함수를 가리킨다.
    Student.prototype.getInfo = function () {
        return `${this.getName()} and My id is ${this.id}`;
    };

    var student = new Student("lee", "st1111");
    var student2 = new Student("park", "st1112");

    console.log(student);
    console.log(student.getInfo());
    console.log(student2.getInfo());

 

Student에 Person을 상속하려면 Student.prototype에 Person 생성자 함수를 통해 객체를 상속 받습니다.
그리고 생성자 함수를 다시 지정해주어야 합니다.

 

클로저란?

함수 내부에 선언된 변수는 함수 종료 시, 모두 반환되지만, 외부에서 그 값을 계속 참조할 경우 반환하지 않습니다.
클로저는 이러한 특성을 이용해서 사용되는 데, 함수 내부의 변수를 참조하는 함수를 리턴하는 형식으로 사용됩니다.
    function func() {
        var num = 1;

        return function () {
            return ++num;
        };
    }
    
    var closure = func();
    
    closure(); // 2
    closure(); // 3
    closure(); // 4

func()함수는 num 변수를 선언하고 num 변수를 증가시키는 함수를 반환하는데, func()함수가 종료되어도 num 변수는 사라지지 않고 클로저 함수에서 사용됩니다. 이러한 동작이 가능한 이유는 함수는 1급 객체이고, 실행 컨텍스트라는 특수한 환경을 지니고 있기 때문입니다. 

1급 객체란?

1) 함수를 변수에 저장할 수 있다.
2) 함수를 인자로 전달할 수 있다.
3) 함수를 반환할 수 있다.

실행 컨텍스트란?

2020/12/05 - [IT/javascript] - [javascript] 실행 컨텍스트(Execution Context)

 

클로저를 이용한 캡슐화

    function Person(){
        var name = "";
        
        this.getName = function(){
            return name;
        };
        
        this.setName = function(n){
            name = n;
        };
    }
    
    var person = new Person();
    
    person.setName("kim");
    console.log(person.getName());
생성자 함수 Person은 getName()과 setName() 메소드를 갖는 인스턴스를 생성합니다. 이 메소드들은 자신이 생성되었을 때의 생성자 함수 Person의 스코프에 속한 name 변수를 기억하는 클로저입니다. 생성자 함수 Person의 변수 name은  this에 바인딩된 프로퍼티가 아닌 지역변수입니다. name 변수가 this에 바인딩된 변수라면 외부에서 접근 가능한 변수가 되지만, 지역변수로 선언되었으므로 외부에서 접근할 수 없고, getName(), setName() 메소드를 통해서 접근이 가능합니다. 이러한 클로저의 특징을 이용해 private 변수를 흉내낼 수 있습니다. 

 

자주 발생하는 실수

    
    for (var index = 0; index < 10; index++) {
        setTimeout(function () {
            console.log(index);
        }, 100);
    }
    
0.1초 마다 0에서 9까지 출력할 것으로 예상하겠지만 결과는 그렇지 않습니다. 이유는 setTimeout 내부에 생성된 클로저는 index 값을 받은 시점의 값이 출력되는 것이 아니라 index 변수 자체를 공유하고있기 때문입니다.  

 

let 변수

    for (let index = 0; index < 10; index++) {
        setTimeout(function () {
            console.log(index);
        }, 100);
    }
let은 블록 스코프이기 때문에, 반복문의 각 단계가 같은 index를 공유하지 않습니다.

참조 :
https://victorydntmd.tistory.com/44?category=704012
https://poiemaweb.com/js-closure

1. this

javascript의 this는 다른 언어의 this와 동작하는 방식이 조금 다릅니다.
javascript에서 대부분의 경우 this는 함수를 호출하는 방법에 따라 결정됩니다.

즉, 함수를 호출 할 때 마다 this가 가리키는 값이 다를 수 있습니다. 

 

2. 전역에서 this를 호출 했을 경우

console.log(this); // window
전역에서의 this는 기본적으로 window 객체를 가리킵니다. 

 

3. 함수 안에서 this를 호출 했을 경우

function doSomething(){
    return this; 
}

console.log(doSomething()); // window
console.log(window.doSomething()); // window
this를 결정하는 건 대부분 함수가 호출될 때, 이루어집니다. (bind 함수와 arrow 함수 제외 )
그리고 함수를 호출한 객체가 무엇이냐에 따라 this가 결정됩니다.

 

4.  메소드에서 this를 호출 했을 경우

function doSomething(){
    return this; 
}

var obj = {
    doSomething : doSomething
};

console.log(doSomething()); // window
console.log(obj.doSomething()); // obj
this를 결정하는 것은 함수를 호출한 객체가 무엇이냐에 따라 달라진다고 했었습니다. 따라서
doSomething 함수를 obj의 메소드로 등록하고 obj.doSomething()을 호출 했기 때문에 this는 obj가 됩니다.
(전역에서 선언되었을 경우 함수라 부르고, 객체에 속한 함수를 메소드라고 부릅니다.)
하지만 여기서 주의해야 할 점이 있습니다. 

 

var a = 10;
var obj = {
    a : 20,
    doSomething : function (){

        function func(){
            console.log(this.a); // 10
        }
    
        func();
    
        console.log(this.a); //20
    }
};

obj.doSomething();
doSomething 메소드 안에서 선언된 func 함수지만 호출해보면 console.log(this.a)에 10이 찍힙니다. 
이유는 obj 객체와 func 함수는 아무런 관련이 없는 함수이기 때문입니다.
위에서 설명했다시피 함수에서의 this는 기본적으로 window입니다.

그래서 메소드 내 함수에서 this를 가리키려면 self 변수에 객체의 this를 저장해서 접근합니다. 
var a = 10;
var obj = {
    a : 20,
    doSomething : function (){
        var self = this; // obj this를 저장한다.

        function func(){
            console.log(self.a); // 20
        }
    
        func();
    
        console.log(this.a); //20
    }
};

obj.doSomething();

 

5. 생성자에서 this를 호출 했을 경우

var age = 10;

function Person(){
    console.log(this.age);
}

var p = new Person(); 

Person();


/*

출력
--------
undefined
10

*/
Person함수는 window객체에 속한 함수입니다. 그래서 그냥 호출하면 this.age는 전역 변수의 age 값을 가리키게 됩니다.
하지만 Person 함수를 new 키워드를 통해 생성하면 새로운 객체가 만들어지면서 this는 window가 아닌 생성한 객체를 가리키게 되고, 함수의 내용은 생성자가 됩니다. 

그래서 생성자 호출 시 새로 생성한 객체에는 age 값이 없기 때문에 undefined가 출력됩니다.

 

6. bind 메서드

ECMAScript 5에서 Function.prototype.bind를 새롭게 도입했습니다.
bind 메서드란 함수 내의 this를 영구적으로 지정하는 메서드입니다.
var a = 10;
function func(){
    console.log(this.a);
}

func(); // 10

//func함수의 this를 obj 객체로 바인딩한다.
var obj = { a : 20 };
var new_func = func.bind(obj);

new_func(); // 20


var obj2 = { a : 30 };
var new_func2 = new_func.bind(obj2); // obj로 바인딩 된 함수를 다시 obj2로 바인딩한다.

//이미 바인딩 된 함수의 this는 변하지 않는다.
new_func2(); // 20  

 

7. 에로우 함수

에로우 함수에서 this는 자신을 감싼 lexical context입니다. 전역에서는 window를 가리킵니다.
에로우 함수 또한 한번 this를 지정하면 변하지 않습니다.
var func = (() => {
    console.log(this);
});
func(); // window


//obj의 func 메소드로 대입
var obj = {func: func};
obj.func(); // window


//obj 객체로 this를 바인딩
var new_func = func.bind(obj);
new_func(); // window

 


참조

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/this

1. 실행 컨텍스트란?

자바스크립트 코드가 실행되고 있는 구역 또는 범위에 대한 정보를 저장하고 있는 객체입니다.

자바스크립트 엔진은 스크립트 혹은 함수를 실행할 때 새로운 컨텍스트를 만들고, 실행 스택에 쌓습니다. 

실행 스택이란 실행 컨텍스트가 생성될 때 마다 실행 컨텍스트를 쌓고 마지막으로 쌓인 실행 컨텍스트 부터 차례대로 실행하는 후입선출 자료구조입니다.

 

2. 실행 컨텍스트의 종류

1) Global Exception Context
자바스크립트 엔진이 실행될 때 가장 먼저 만드는 실행 컨텍스트입니다.
전역 컨텍스트라고 하며, window 객체를 만들고 전역 컨텍스트의 this를 window 객체로 할당합니다.

그리고 이후에 생성되는 모든 전역 변수, 함수는 window 객체에 저장됩니다.

2) Function Execution Context
함수가 호출되면, 함수의 실행 컨텍스트가 생성됩니다.

3) Eval Function Exception
사용되지 않음

 

실행 컨텍스트의 구조

ExecutionContext = {
    LexicalEnvironment : { 
    	EnvironmentRecords : {
           //function or variable
        },
        outer : (LexicalEnvironment),
        ThisBinding : (object)
    },
    
    VariableEnvironment : {(LexicalEnvironment와 동일한 구조)}
}

 

3. 실행 컨텍스트의 두가지 단계

모든 실행 컨텍스트는 Create 단계 Execution 단계를 통해 생성됩니다.

1) Create 단계

Create 단계에서 LexicalEnvironment 컴포넌트 VariableEnvironment 컴포넌트를 생성합니다.
두 컴포넌트가 생성될 때, VariableEnvironment 컴포넌트는 LexicalEnvironment 컴포넌트를 참조합니다.
그러다가 자바스크립트의 코드에 따라 참조가 바뀌기도 합니다.
executionContext.LexicalEnvironment = executionContext.VariableEnvironment;

 

 

1.1) LexicalEnvironment 

LexicalEnvironment 컴포넌트는 변수나 함수 등의 식별자를 저장하는 EnvironmentRecords 객체와 외부 LexicalEnvironment 컴포넌트를 참조하는 outer 그리고 this로 구성됩니다. 
outer는 스코프 탐색을 위해 사용됩니다.

LexicalEnvironment 컴포넌트는 3가지 동작을 합니다.

1) 변수와 함수의 저장공간을 메모리에 할당
실행 컨텍스트 내에서 선언된 변수와 함수의 저장공간을 메모리에 할당하고 EnvironmentRecords라는 객체에 저장합니다.

2) 외부 실행 컨텍스트의 LexicalEnvironment를 저장
코드를 실행하다가 특정 변수나 함수를 마주하면 LexicalEnviromemt에서 찾는데 찾지 못했을 경우 외부 LexicalEnvironment 에서 찾습니다. (scope chain)

3) 실핼 컨텍스트의 This 값을 지정
this 값은 이 단계에서 결정됩니다. 전역 실행 컨텍스트의 this 는 global object입니다. 

this 값은 함수를 호출한 객체를 가리키게 됩니다. 

 

this를 이해하기 위해 간단한 소스를 보겠습니다.

var player = {
    lastName : 'son',
    firstName : 'heung min',
    name : function(){
       console.log(`${this.lastName} ${this.firstName}`);
    }
}

player.name(); // son heung min

var getName = player.name;

getName(); // undefined undefined
// == window.getName()
player.name()은 player 객체가 호출했기 때문에 this값은 player 객체입니다.

getName()은 player.name 함수를 대입받았고, 그대로 실행 했기 때문에 this값은 window 객체를 가리킵니다.

 

정리하면 Create 단계에서는
1) 변수, 함수, 인자를 만들고 EnvronmentRecords에 저장합니다.
2) 외부 실행 컨텍스트를 가리킵니다. (Scope Chain)
3) 실행 컨텍스트 내에 선언된 변수와 함수를 찾아 메모리에 할당합니다. (호이스팅)
4) this가 결정됩니다.

2) Execution 단계

실행 컨텍스트 내에 코드들을 위에서 한줄 씩 읽으면서 선언해 두었던 변수에 값이 할당됩니다.

 


참조

https://velog.io/@imacoolgirlyo/JS-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%9D%98-Hoisting-The-Execution-Context-%ED%98%B8%EC%9D%B4%EC%8A%A4%ED%8C%85-%EC%8B%A4%ED%96%89-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8-6bjsmmlmgy

https://meetup.toast.com/posts/129

 

+ Recent posts