블로그
전체 5#카테고리
- 프로그래밍 언어
#태그
- 자바스크립트
2022. 01. 05.
0
코어자바스크립트 강의정리 - 5. 클로저
클로저란 실행 컨텍스트 A 안에서 함수 B가 선언 되었을 때, 'A의 lexical environment와 내부함수 B의 조합에서 나타나는 특별한 현상' 이다. 대체 어떤 특별한 현상이 나타나는지 코드를 통해 살펴보자 var outer = function(){ var a= 1; var inner=function(){ console.log(++a); } inner() } outer() 위 코드에서 inner 함수는 outer함수의 lexical environment를 참조한다. 코드를 실행하면 outer 실행시작 - inner 실행시작 - inner안에 a변수가 없으므로 outer 에서 a 값을 찾아와 1을 더해 출력 - inner 컨텍스트 종료 - outer 컨텍스트 종료 순으로 이루어 진다. 딱히 특별한 현상이랄 것은 없어보인다. 코드를 살짝 바꾸어 다시 한번 살펴보자. 이번엔 inner에서 a를 콘솔로그하는대신 리턴하고 outer 함수 안에서도 inner을 실행하는대신 리턴한다. var outer = function(){ var a= 1; var inner=function(){ return++a } return inner } var outer2 = outer() console.log(outer2()) console.log(outer2()) 코드가 실행되면 전역컨텍스트에 outer라는 변수안에 함수가 들어가고, outer2 라는 변수도 수집은 했는데 아직 실행 전이므로 값은 undefined 이다. 그리고 outer함수를 실행하면 outer 함수의 전역 컨텍스트가 실행되어 a에는 1 , inner에는 함수가 담긴다. 이후 outer함수의 결과값으로 inner 가 반환되고 inner 함수는 outer2에 담긴다. 여기서 특별한 현상이 나타난다. 원래대로라면 이후 outer실행컨텍스트가 종료되고 outer 내부 변수도 사라지지만, a를 참조하고 있는 inner 함수가 outer2에 살아있기 때문에 a의 참조 카운트는 0이 아니게 되고, 따라서 a의 변수 값은 사라지지 않고 남아있게 된다. 그 결과로 outer2 를 두번 콘솔로그 하면 ++a 한 값인 2와 한번 더 ++a 한 값인 3이 출력된다. 하지만 이후에도 전역 컨텍스트가 종료되기 전까지 a 변수는 계속 남아있다. 왜냐하면 outer2 변수가 지니고 있는 inner 함수 안에서 a를 계속 참조하고 있기 때문이다. 이때 a의 참조카운트를 0으로 만드려면 outer2 변수에 다른 값을 대입해서 참조의 연결 고리를 끊으면 된다. 위의 결과를 토대로 closure를 다시 정리하면 "컨텍스트A에서 선언한 변수 a를 참조하는 내부함수 B를 A의 외부로 전달할 경우, A가 종료된 이후에도 a가 사라지지 않는 현상"이 closure이다. 즉 함수 종료 후에도 사라지지 않는 변수를 만들 수 있는 것이다. ++후반부 내용 추가예정
2021. 12. 30.
0
코어자바스크립트 강의정리 - 4. 콜백함수
a라는 함수가 있다. b라는 함수에게 a라는 함수를 넘겨주며 'a라는 함수를 알아서 잘 작동시켜주세요' 라고 하면 b 함수가 a 함수의 제어권을 갖는다. 그리고 a 함수를 콜백함수라고 한다. b함수가 a 함수의 제어권을 갖게 되면 a 함수의 실행시점, 매개변수의 내용, 순서, this(특별히 바인드 하지 않는 이상) 는 모두 b 함수가 결정한다. arr.forEach 매서드의 첫번째 인자는 콜백함수인데, 그 콜백함수의 첫번째 인자는 배열의 요소, 두번쨰 인자는 인덱스 이런식으로 정해져 있는 것이 그 예이다. 참고로 forEach의 두번째 인자는 thisArg이고 생략가능하다. 이를 이용해 forEach내부에서 사용할 this값을 정해줄 수 있다. 추가로 주의해야할 점이 있다. 콜백함수는 '함수' 라는 것이다. 당연한 이야기 같지만 예시를 보며 살펴보자 var arr=[1,2,3,4,5] var obj={ vals:[1,2,3], logValues:function(v,i){ if(this.vals){ console.log(this.vals,v,i); }else{ console.log(this,v,i) } } } 이 코드에서 obj.logValues(1, 2) 를 실행하면 3 강에서 배웠듯이 매서드로 호출했으니까 this는 obj가 되고 this가 존재하므로 [1, 2, 3] 1, 2 가 출력된다. 그러나 arr.forEach(obj.logValues) 와 같이 사용하면 obj.logValues는 메서드로 호출한게 아니라 forEach의 콜백으로 넘긴 것이 되고 this는 forEach가 결정하는 값이 된다. 이상황에서 this는 전역 객체가 된다. 만약에 obj를 this로 하고 싶다면 arr.forEach(obj.logValues.bind(obj)) arr.forEach(obj.logValues,obj) 의 두가지 방법이 있다. 콜백함수에 obj를 바인드 해주거나, forEach에 명시된대로 두번째 인자로 obj를 넘겨주어 this를 설정하거나
2021. 12. 27.
0
코어자바스크립트 강의정리 - 3. this
2강에서 배웠듯이 실행컨텍스트 안에는 VariableEnvironment, LexicalEnvironment, ThisBinding이 있다. 실행컨텍스트가 활성화 될때 ThisBinding을 한다는 것이고, 이는 함수가 호출될때 그제서야 this가 binding(연관, 세팅)된다는 것을 의미한다. 그렇기 때문에 함수를 어떻게 호출하느냐에 따라서 this가 가리키는 대상이 달라진다. this가 달라지는 조건은 크게 5가지가 있다. 1. 전역공간에서 전역공간에서 this를 호출하면 window/global (브라우저에선 window, node에선 global) 을 가리킨다. 2. 함수 호출시 함수 호출시 this는 전역공간에서와 똑같이 window/global 전역객체를 가리킨다. 함수안에서 호출하는 함수 또한 마찬가지다. somgFunc(); 형태로 호출하는 함수의 this는 모두 전역객체이다. 이는 직관적으로 좀 이해가 가지 않을수 있는 부분이다. 함수 호출시 this를 사용하면 전역객체를 가리키는 것이 맘에 들지 않을 수 있다. 이럴때 사용할수 있는 우회법은 3. 매서드 호출시에서 추가로 설명한다. ECMAScript 6 에서는 this를 바인딩하지않는 arrow function 을 통해 이 문제를 해결했다. arrow function은 애초에 this가 바인딩 되어있지 않기 때문에 arrow function 안에서 this를 사용하면 외부 렉시컬 범위에서 this를 찾아 사용한다. 3. 매서드 호출시 객체의 매서드로 호출될때는 조금 다르다. 예를들어 var a = { b: function() { console.log(this); } } a.b(); 는 코드가 있을때 출력값은 a 객체가 된다. 왜냐하면 매서드로 호출했을때의 this는 호출하는 함수를 제외하고 ' . ' 앞에 있는 객체가 this가 된다. 또 다른 예를들어 a.b.c( ) 와 같이 호출하면 a.b가 this가 된다. a['b'].c( ); 처럼 대괄호 표기법을 사용해도 this는 a.b 로 같다. 이를 응용하며 매서드 내부에서 또 함수를 호출하는 경우의 this도 제어할수 있다. 원래라면 매서드 내부에서 함수를 호출하면 매서드 안에서 호출했더라도 2. 의 조건때문에 this는 전역객체를 가리키겠지만. 매소드에 var self=this; 라는 식으로 this를 미리 저장해두고 매서드 내부함수는 this 대신 self(이름이 굳이 self가 아니어도 됨) 를 사용하면 우리가 원하는 결과를 얻을 수 있다. 그러나 2. 에서 설명했듯이 arrow function이 생긴 지금은 굳이 이렇게 할 필요가 없다. 그냥 신 문법 쓰면 된다. 4. callback 호출시 콜백 호출시에 대해 설명하기 전에 call, apply, bind를 이용한 명시적인 this 바인딩에 대해 잠깐 알고가자. call, apply, this매서드는 this를 내맘대로 바인딩하여 함수를 호출하게 해준다. a.call( thisArg, 나머지 인자들) a.apply(thisArg, [나머지 인자들]) a.bind(thisArg, 나머지 인자들) 의 식으로 사용하고 call 과 apply는 나머지 인자들을 배열로 넘겨주냐 그냥 넘겨주냐의 차이만 있고 완전히 같다. bind는 함수에 this를 바인드하고 호출하는 call, apply와 다르게 함수에 this를 바인드만 하고 호출하지는 않는다. 이제 콜백함수에서의 this를 알아보면 매서드 안에서의 콜백이던 뭐던 함수 호출형식으로 this를 사용하면 this는 전역객체를 가리킨다. 그러나 call, apply, bind 를 이용해 콜백함수에 명시적으로 this를 바인딩하면 콜백함수도 명시적으로 지정된 this를 사용한다. addEventListener처럼 미리 콜백함수에 this를 바인딩 해놓은 함수는 이때문에 콜백함수에서 this를 사용하면 전역객체가 아니라 addEventListener가 정의해놓은 (이벤트가 발생한 타겟 대상 엘리먼트) 요소를 this로 사용한다. 그런 경우에도 document.getElementById('a').addEventListener('click', function(){ console.dir(this); }.bind(customThisObj) ); 와 같은 방법으로 내가 원하는 this를 다시한번 지정해 줄 수 있다. 5. 생성자함수 호출시 생성자 함수 호출( new 연산자 사용)시 에는 새로 만들 인스턴스 객체 그 자체가 곧 this가 된다. function Person(n, a){ this.name=n; this.age=a; } var roy= Person('재남', 30); console.log(window.name, window.age); //재남 30 를 하면 생성자 함수 없이 그냥 함수로 Person을 호출했기 때문에 window 전역객체에 name과 age값이 담기게 된다. (roy에는 아무 값도 담기지 않는다) function Person(n, a){ this.name=n; this,age=a; } var roy= new Person('재남', 30); console.log(roy); //재남 30 그러나 new를 넣은채로 호출하면 this는 roy라는 인스턴스 그 자체를 this가 가리키게 되면서 roy.name은 재남 roy.age는 30 이 된다.
2021. 12. 24.
1
코어자바스크립트 강의정리 - 2. 실행 컨텍스트
2. 실행 컨텍스트 실행 컨텍스트=코드 흐름상에 배경이 되는 조건/환경정보를 담은 객체=>동일한 조건/환경을 가지는 코드 뭉치 정도로 느낌적인 느낌만 가져가자. 전역변수/지역변수의 개념이 생기는 이유가 이 실행 컨텍스트 때문이라고 보면 될것 같다. 자바스크립트에서 동일한 실행 컨텍스트를 갖게 하는 요인은 크게 네가지 (전역공간,함수,module,eval) 이 있는데 eval은 여러가지 문제를 야기하기 때문에 논외. 전역공간은 가장 큰 함수 하나로 보고 모듈도 import 되는 순간 컨텍스트가 생성되고 모듈 코드가 끝났을때 컨텍스트가 종료되기 때문에 모듈도 일종의 함수로 보면 결국 같은 '함수' 안에 있으면 같은 실행 컨텍스트를 갖는다고 보면 된다. 번외로 if/for/switch/while 같은 '문'들은 let const 가 es6부터 추가되며 별개의 독립된 공간으로서의 역할을 하고있지만 별개의 실행 컨텍스트를 생성하지는 않는다. 실행 컨텍스트에는 VariableEnvironment, LexicalEnvironment, thisBinding 이 있는데 VariableEnvironment는 식별자 정보를 수집하고 LexicalEnvironment는 각 식별자의 '데이터' 를 추적한다 (변화하는 값을 반영함) VariableEnvironment는 강의에서 중요하게 다루지 않고 LexicalEnvironment에 대해 설명한다.(thisBinding은 다음 강의에서 다룸) LexicalEnvironment의 뜻을 풀이해보면 어휘/사전적 환경을 뜻한다. 실제로 자신이 속한 실행 컨텍스트의 모든 식별자의 이름과 값, 그리고 자신이 참조하는 외부 컨텍스트의 정보를 사전처럼 구성한 객체가 LexicalEnvironment 이다. 예를들면 LexicalEnvironment는 내부식별자 a (environmentRecord) : 현재 값은 undefined이다 내부식별자 b (environmentRecord) : 현재 값은 20이다 외부 정보(outerEnvironmentReference) : D를 참조한다. 의 구조로 이루어져 있다. 그렇다면 실행컨텍스트는 어떻게 작동하여 LexicalEnvironment를 구성할까? 실행컨텍스트는 어떤 함수가 호출되면 함수의 범위 안에 있는 모든 선언을 위로 끌어온다. (이것을 Hoisting-끌어올림 이라고 부른다.) 함수는 전체를, 변수는 선언 까지만(할당은 원래 코드의 위치에서 함) 끌어온다. 그리고 나서야 자바스크립트는 함수 내 코드를 작동시킨다. 사실 실행 컨텍스트가 실제로 선언을 끌어 올리는것은 아니고 정보를 수집해서 LexicalEnvironment를 구성할 뿐이다. 그러나 모든 선언을 위로 끌어올린다고 생각해도(추상화된 개념-Hoisting) 문제될 것이 없기 때문에 그렇게 생각하는 편이 편할 것이다. 실행 컨텍스트는 언제 생성될까? 새로운 실행 컨텍스트의 범위가 필요해질 때 새로운 실행 컨텍스트는 생성된다. 새로운 범위가 필요해질 때 = 새로운 독립적 환경일 때 즉 어떤 함수가 호출될때 그 함수의 실행 컨텍스트가 생성된다. 자바스크립트는 호출한 함수를 콜 스택에 담아 순차적으로 처리하기 때문에(LIFO) 코드 작동 시작 때 전역 실행 컨텍스트가 생성되고 점점 깊은 곳의 코드들을 처리한 후에 전역 실행 컨텍스트를 종료하면서 작동이 끝난다. 내부의 각 함수들이 실행될때 본인의 LexicalEnvironment에 존재하지 않는 변수나 함수를 만나면 자신이 참조하고 있는 외부 함수의 실행 컨텍스트를 뒤져 변수를 찾는다. 또 존재하지 않으면 다시 외부의 함수에 가서 찾는 과정이 계속된다(ScopeChain. 그 과정에서 변수를 찾으면 그 변수의 값을 이용한다. 만약 변수를 찾았던 실행 컨텍스트 외부에 같은 이름의 변수가 또 존재하더라도 그 값은 인식되지 않는다(Shadowing).
2021. 12. 24.
1
코어자바스크립트 강의정리 - 1. 데이터 타입
1. 데이터 타입 자바스크립트 데이터 종류 (기본형, 참조형) 기본형 데이터 : 정적할당 참조형 데이터 : 동적할당 Why? 기본형 데이터의 할당 방식 var a = 1 이라고 하였을때 자바스크립트 내부적으로는 var a; a = 1; 로 처리 따라서 임의의 메모리 주소(1004) 에 (이름 : a, 값 : 없음) 저장 후 다른 임의의 메모리 주소(5000)에 데이터 1 을 저장. 그리고 메모리 주소 1004 의 값에 메모리 주소 5000을 저장하여 (이름 : a, 값 : @5000) 으로 만든다. 참조형 데이터의 할당방식 var obj= { a : 11, b : 11} 이라고 하였을때 메모리주소 1004에 (이름 : obj, 값 : 없음) 저장, 임의의 메모리 주소 5000에 @7000~@70?? 까지의 메모리 주소 데이터 저장(배열이나 객체는 값이 여러가 들어가기 때문에 미리 공간을 확보해 둠) @7000 에 정적 할당 방식과 같이 (이름 : a , 값 : 없음) 저장, 임의의 메모리 주소 @5001에 데이터 11 저장. @7000 의 값을 @5001 로 저장. @7001 에 (이름 : b, 값 : @5001) 저장. @1004의 값에 @5000 저장. 결국 어떤 데이터를 저장할때 기본형이나 참조형이나 가장 작은 데이터의 단위(위의 예시에서 1, 11 과 같은 값)를 중복하지 않고 공유해서 가지고 있고, 그 주소를 참조한다. 그렇기 때문에 const obj 를 사용하여도 obj 내부의 값을 바꿀수 있는 것. 같은 원리로 var obj2 = obj ; obj2.a=99; 를 하고 나면 obj나 obj2나 @5000이 가리키고 있는 @7000~@70?? 의 값을 받아오기 때문에 obj.a 도 99의 값을 갖는다. 번외 : 그럼 왜 자바스크립트는 가장작은 데이터의 단위를 굳이 따로 저장할까? var a = 1을 하였을때 @1004 (이름 : a, 값 :1 ) 을 하는 것이 할당 속도면에서 더 효율적이지 않을까(장점1)? 라는 의문이 생길 수 있다. 그러나 큰 값이 여러번 저장될때 예를들어 "매우매우긴문자열" 이라는 문자열을 매번 직접 값을 저장하는 방식을 사용하면 하나의 문자열을 계속해서 참조하는 방식에 비해 메모리를 더 차지하게 된다(단점1). 심지어 값을 비교하는 작업이 있다면 매우 긴 문자열의 값을 매 , 우, 매, 우, 긴, 문, 자, 열 로 하나하나 비교해야 해서 하나의 메모리 주소값만 비교하는 방식에 비해 오래걸린다(단점2). 현재 자바스크립트가 사용하고 있는 방식은 값을 한번 할당할때는 느리지만(단점1), 비교에 비용이 들지 않는다(장점1). 또한 할당한 값이 여러군데에서 중복해서 사용될 때에 메모리의 낭비가 적다(장점2). 그렇기때문에 단점보다 장점이 많은 현 자바스크립트의 방식을 자바스크립트는 채택한 것이다.
프로그래밍 언어
・
자바스크립트