공부용으로 정리하는 것이므로, 주어진 순서나 목차를 강조하기 보다는 모르는 내용, 복습할 키워드 위주로 '####'을 나타내기
-> 수정: 너무 많은 목차들은 오히려 가독성과 강조 기능을 잘 못하는 듯하다. 그냥 공부하다가 필요한 부분을 알아서 하이라이트 하는 걸로... (오히려 코드로 나타내는 게 더 눈에 잘 띈다.)
import "other.css";
import NameReater from "./NameReapter";
파일 위계 구조
import "./app.css";
import NameRepeater from "./NameRepeater";
const App = () => {
...
};
export default App;
const App = () => { ... };
export default App;
import와 export 등의 기능으로 여러 개의 js 파일 사용 가능!
const NameReapter = { ... };
export default NameRepeater;
const NameRepeater = {
name: "My Name",
setName: function setName(name) {
// NameRepeater의 이름을 세팅합니다.
this.name=name
return this.name;
}
};
export default NameRepeater;
var name = 'a';
var name = 'b'; // ok
let name = 'a';
let name = 'b'; // Uncaught SyntaxError: Identifier 'name' has already been declared
name = 'b'; // ok
const name = 'a'
const name = 'b' // Uncaught SyntaxError: ~
name = 'b'; // Uncaught TypeError: Assignment to constant variable
var, let, const 차이점: 변수 재선언, 변수 재할당 가능/불가능 여부
const는 상수라고 생각하면 될 것 같다. 값이 변화할 필요가 없는 것들을 선언하는 느낌. 그런데 앞서 const App = () => {} 사례와 const NameRepeater = {} 사례를 보았을 때 약간 일반적인 자료형에만 국한되는 것이 아니라, function이나 class 느낌도 나는 것 같다.
뒤의 사례에서 export할 때, return을 선언해야 코드가 올바르게 동작하는 경우도 있었다. (NameRepeater는 따로 return이 없다.)
setButton.addEventListener("click",()=>{ ... })
// () ==> {} 함수 기능 하지롱~
setButton.addEventListener("click", () => {
NameRepeater.setName(input.value);
result.innerHTML = "설정되었습니다.";
});
addEventListener() 새로운 사용 방법: function 표현이 ()=>{ ... }로 바뀌었다.
참고로 후술하겠지만, 화살표 함수는 원래 함수 표현과 완전히 같지는 않다. (this 라든지..)
bind 관련 설명: this가 의미하는 바를 지정해줌. (명확)bind()를 사용하여 this 키워드가 mySite를 가리키도록 한다.
getYourSite = mySite.getSite;// undefined
getYourSite = mySite.getSite.bind(mySite); // site
const o = {
name: "Kim",
changeMyName: function (name) { this.name = name },
};
const o2 = {
name: "Song",
};
o.changeMyName.bind(o2)("Sam");
console.log(o.name); // Kim
console.log(o2.name); // Sam
o.changeMyName.bind(o2)("Sam");에서 o2.name="Sam"으로 바꾼 것이고 o.name은 여전히 "Kim"이기 때문에 그렇다.
<!doctype html>
<html lang="ko">
<head>test</head>
<body>
<script>
const o = {
name: "Kim",
changeMyName: function (name) { this.name = name },
};
const o2 = {
name: "Song",
};
function callFuncWithArg(f, arg) {
f(arg);
}
// 1번이 Sam인 이유는, bind 메서드로 기존 o의 execution context를 o2의 execution context로 변환했기 때문이다.
o.changeMyName.bind(o2)("Sam");
console.log("1번 - ", o2.name); // Sam
// o의 changeMyName 메서드는, callFuncWithArg의 인자로 넘겨질 때 execution context가 dynamic binding 됩니다.
// 따라서 o.changeMyName은 global을 execution context로 갖게 되고, o.name은 바뀌지 않습니다.
callFuncWithArg(o.changeMyName, "Daniel");
console.log("2번 - ", o.name); // Kim
// 3번이 Sam인 이유는, o.changeMyName 메서드의 execution context가 o로 유지되기 때문이다.
o.changeMyName("Sam");
console.log("3번 - ", o.name); // Sam
// 이해가 안 되더라도, 실제로 사용할 때는 거의 사용하지 않기 때문에 괜찮다..(?)
</script>
</body>
</html>
간단하게, 2번이 안 되는 이유는 this.name에서 this가 global을 가리키는 것이므로 this.name="Daniel"이 o.name을 변화시키지 못하였기 때문이다. 따라서 o.name은 여전히 "Kim"을 출력하게 된다.
// Context.js
function mul(num) {
let a = 10;
let b = 20;
function mul30(n) {
// return n*this.a*this.b; // 이 부분을 수정하세요.
// Context.js:5 Uncaught TypeError: Cannot read properties of undefined (reading 'a' ~~)
return n*a*b;
}
return mul30(num);
}
function add30(num) {
let a = 10;
return (function () {
let b = 20;
function add() {
return num+a+b; // 이 부분을 수정하세요.
}
return add();
})();
}
const myModule = { mul, add30 };
export default myModule;
function들을 외부에 따로 정의한 뒤,const MyModule을 {func1, func2}로 묶어서 반환하는 방법. (new!)
+) 효율 따지면 사실 이렇게 구현해야..
function mul(num) {
let a = 10;
let b = 20;
return n*a*b;
}
return n*this.a*this.b; 안되는 이유
간단하게, undefined 문제 때문임.. (그리고 문제에서 내부에 정의된 a, b를 사용하라고 하였음) this가 가리키는 게 상황별로 달라서...
// Context.js
const myModule = { mul, add30 };
export default myModule;
// App.js
import Context from "./Context";
const App = () => {
const result = document.getElementById("result");
mulButton.addEventListener("click", () => {
result.innerHTML = Context.mul(Number(input.value));
});
}
myModule을 넘겨 줬는데 Context로 받고 있다..
const myModule = { mul, add30 };
export default myModule;
import Context from "./Context";
Context.mul(Number(input.value));
원래대로라면, as를 써서 바꿔주거나, 원래 이름 그대로 가져오는 게 자연스럽다.
// s.js
export {sHi, sBye};
// main.js
import {sHi, sBye} from './s.js';
sHi('J');
sBye('J');
import * as s from './s.js';
s.sHi('J');
s.sBye('John');
// s.js
export {sHi as hi, sBye as bye};
// main.js
import * as s from './s.js';
s.hi('J');
s.bye('J');
// s.js
export function sHi() { ... }
export function sBye() { ... } // 따로따로 export하기
// main.js
import {sHi} from './s.js';
그러나 named export와는 다르게 default export는 가져오기 할 때 개발자가 원하는 대로 이름을 지정해 줄 수 있다. 그런데 이름을 마음대로 지정하다보면 나중에 혼돈을 줄 수 있기 때문에 다시 내보내기를 해주는 것이 좋다고 한다.
자바스크립트 파일을 import할 때는 .js를 빼도 되는 줄 알았는데, 위의 예시를 보니 넣어도 상관 없는듯 하다.
myFunc()o.method()function Person() { this.name="Peter"; }const p = new Person();f.call()myFunc('Daniel',callback)setTimeout(function{...}, second)function Person(name) {
this.name = name
this.print = function() {
console.log(this.name)
}
}
const p = new Person('Daniel'); // constructor
setTimeout(p.print.bind(p), 1000);
함수는 다양한 방법으로 호출될 수 있고, 호출 환경에 따라 this는 동적으로 세팅 됨.
이처럼 호출 환경에 따라 this가 바뀌는 것을 dynamic binding이라고 함.
bind, apply, call 등으로 this가 가리키는 것 조작 가능.
let o = {
name: "Daniel", // let o = {내부} 에서는 field 값은 ":"으로, field와 method 사이는 ,로 연결해야 함.
f1: () => {
console.log("[f1] this : ",this.name); // 없음 (공란)
}, // 화살표 함수는 자신보다 상위의 this를 가리키게 된다.
f2: function () {
console.log("[f2] this : ",this.name); // Daniel
}
}
o.f1(); // global // 없음
o.f2(); // o //"Daniel"
()=>{} 방법이랑 function () {} 방법이랑 완전히 같지는 않다. 화살표 함수는 자신보다 상위의 this를 가리키므로, 위의 함수에서는 window를 가리킨다.
:와 ,을 사용하며, 세미콜론이 내부에 존재하면 에러가 발생한다.문법 조심하기. let o = { field: "value", method: ~ }
그리고 뭐지? let o = {} 뒤에 ; 없어도 잘 돌아간다.. 이건 그냥 function = call(f,arg) {f(arg);} 뒤에 ; 유무도 마찬가지.. 뭐지? ;가 필수가 아닌가?
사실 자바스크립트는 자동으로 세미콜론;을 넣어준다고 한다. 하지만 혹시 모를 에러가 발생할 수 있으니 꼭 넣는 것을 습관화하자. 바람직한 코드 작성!
setTimeout(o.f1, 10); // global
setTimeout(o.f2, 20); // global
setTimeout(func,time)으로 함수의 실행 환경을 바꾼다.
setTimeout 함수의 인자로 콜백 함수를 보내면 특정 시간 이후에 해당 함수는 실행된다. 특정 객체의 메서드라 하더라도, 실행될 때 함수가 실행되는 환경은 바뀌게 된다. 미리 바인딩 된 this가 없을 경우 this는 함수를 둘러싼 환경을 가리키도록 바뀐다.
()=>{} 방법이랑 function () {} 방법이랑 완전히 같지는 않다. 화살표 함수는 자신보다 상위의 this를 가리키므로, 위의 함수에서는 window를 가리킨다.
자바스크립트에서 함수는 일급 객체이다.
var f = function() { ... }
f는 함수 객체가 되었음, function이라는 객체는 전역으로 존재하는 객체임. 모든 function은 전역 객체 function을 상속받은 instance가 됨.
일급 객체란? 함수를 변수처럼 다룰 수 있는 것!
console.log(printName === person.print);
객체 비교하는 것처럼 함수도 비교할 수 있다.
함수의 일급 객체 성질을 이용함,
함수가 생성될 때, 함수 내부에서 사용되는 변수들이 외부에 존재하는 경우 그 변수들은 함수의 스코프에 저장됨
function createCard() {
let title = "":
let content = "";
function changeTitle(text) { title = text; }
function changeContent(text) { content = text; }
return { changeTitle, changeContent };
}
const card = createCard(); // 클로저 하나 생성
card.changeTitle("카드");
card.changeContent("만들었다!");
const card2 = createCard(); // 별도의 클로저 하나 더 생성
card2.changeTitle("카드");
card2.changeContent("만들었다!");
createCard()에 return이 있네?
클로저는 그냥 함수 선언 별로 다른 공간에 메모리를 할당한다고 생각하면 될 것 같음.
단순하게, 클로저는 저장공간을 의미한다고 이해하면 될듯.
let r = 1.05; // 1회만 생성
function app() {
let b = 10; // app() 호출할 때마다 매번 생성
return function (p) {
return p * r * b;
};
}
console.log(app()(1)); // 11.05
r = 1.1;
console.log(app()(1)); // 11.1
(질문) argument를 받는 function 내부에 argument를 받는 function이 또 존재할 때 어떻게 각각 나눠서 argument를 할당할 수 있을까?
(답) func(arg1)(arg2) 신기하다..
// Counter.js
const Counter = () => {
let count = 0;
function getCount() {
return count;
};
function increase() {
count++;
};
// 예상 외의 복병..!
return {getCount, increase};
};
export default Counter;
return 값이 있어야만 한다!! (심지어는 return Counter;도 안 됨. 일부러 클로저 예시를 만드려고 count 변수를 제외하기 위해 나머지 함수들만{}로 싸서 return했던 것이 아니었음)
// App.js
import Counter from "./Counter";
const App = () => {
const counter = Counter();
increaseButton.addEventListener("click", () => {
counter.increase();
});
}
count 변수가 각각의 function에서 변수로 사용되므로 count를 제외하고 return하여도 함수 수행 과정에서 count 변수가 클로저 내부에 존재하여 메모리에 남아있게 됨.
그런데, return을 아예 빼먹으면 코드 에러남.. 아래 App.js에서 counter.increase()를 못찾는다고 함. 분명 return 없이도 잘 수행되는 과정이 있었던 것 같은데, 왜 그럴까.
// case 1
const myModule = { mul, add30 };
Context.mul(~);
// case 2
const NameRepeater = {
name: "My Name",
setName: function setName(name) {
return this.name=name
}
};
NameRepeater.setName(input.value);
// case들의 공통점은 {} 꼴이 반환되어, p.func()이 사용되고 있다는 점.
추측으로는, {} 꼴이어야 x.func() 호출이 가능해서 그런 것 같다.
왜냐하면 단순히 return Counter; 이런 식으로는 코드 실행이 안 되었기 때문이다. 추후 검증을 해봐야할 것 같다.
function min(...rest) {
// rest operator는 인자들을 배열로 묶는다
return rest.reduce( (a,b) => a<b ? a : b )
}
min(2,3,6,9,3,1) //1
const o = {
name: "Sherlock",
age: 35,
address: "Baker Street",
job: "detector"
};
// 지정된 필드 외 나머지 필드를 객체로 묶음
const {age,name,...rest} = o;
만약 const {age,name,job,address,...rest} = o; 였다면, ...rest는 빈 배열을 반환한다. 즉, 배열의 rest operator는 나머지 인자가 하나도 없어도 묶을 수 있다.
function sumArray( sum, arr ) {
// tail은 하나씩 줄어 들어 마지막에는 결국 합을 반환
if (arr.length==0) return sum;
// 나머지 인자를 다시 배열로 묶음
const [head, ...tail] = arr;
return sumArray(sum+head, tail); // 재귀 구조
}
// 꼭 ...rest가 아니라 ...tail도 가능.
const o = {
name: "Sherlock",
age: 35,
address: "Baker Street",
job: "detector"
};
// 객체 spread operator
let o2 = {...o, name: "Watson", age:40} // name, age 업데이트
let o3 = {name: "Watson", age:40, ...o} // o 값으로 업데이트
일일히 field들을 써주지 않아도 되어서 편리하지만, 순서가 중요하다.
일반 객체는 iterable 성질이 없으므로, 배열 같은 iterable 객체에 합치려 하면 에러가 발생한다. 단, iterable을 지원하는 객체는 spread operator로 합칠 수 있다.
function findMinInObject(o) {
return Math.min(
// 배열 spread operator
...Object.values(o)
)
}
let o1 = { a:1 }
let o2 = { b:2 }
findMinInObject(o1,o2); // 1
Object.values(o)는 객체 값들의 배열을 반환. // [1, 2]
배열은 iterable 성질을 가진 객체이므로, 객체에 spread operator로 합쳐도 에러가 발생하지 않는다. 배열의 각 index에 있는 값들이 ‘0’: 1, ‘1’: 2 처럼 객체에 합쳐진다.
객체나 배열 필드는 단순히 reference만 복사된다. 따라서, deep copy를 위한 별도의 작업을 해주어야 한다.
배열과 객체는 여러 데이터를 묶어놓은 집합이라는 점에서 서로 비슷하다.
하지만 객체는 프로퍼티의 집합이며, 배열은 데이터값의 집합이라는 차이가 있다.
{"dog":["포메라니안","푸들","웰시코기"]}
객체는 {a:1,b:2} 형태, 배열은 [a,b,c] 형태라는 점.
var array = [3,4,5];
for (var i = 0; i < 3; i++) {
document.write(i + "<br>"); // 0,1,2
document.write(array[i] + "<br>"); // 3,4,5
}
for (var a in array) {
console.log(a); // [0,1,2]
}
for (var a of array) {
console.log(a); // [3,4,5]
}
파이썬의 for x in array : ~ 와는 다르다. 변수 하나하나 차례로 불러 오려면 for (var x in array) { ~ }를 써야 한다.
arr.reduce(callback(accumulator, currentValue, index, array), initialValue)