Javascript
Javascript Basics (최종)
코딩을 하거나 코딩 테스트 문제를 풀면서 매번 느끼는 것은 JavaScript 기본기가 정말 가장 중요하구나였다. 따라서 JavsScript의 Basics들을 다시금 정리해가며 되돌아보고자 한다. 다만 아주 예전의 공부했던 자료들이어서 가독성이 조금 떨어지는 점은 양해 바람. 그리고 스압 주의.
목차
- Map 기초
- Map for loop
- Map - keys( ), values( ), entries( )
- Map - Spread
- Set 기초
- Set methods
- next() method ⇒ array, map, set
- has(), for of loop
- set, array and forEach
- Decorators and forwarding, call/apply
- Call
- arguments and apply
- Function binding
Map 기초
Map, Set, WeakMap, and WeakSet const car = {} car['color'] = 'red' car['owner'] = 'Flavio' console.log(car['color']); // red console.log(car['owner']); // Flavio console.log(car); // { color: 'red', owner: 'Flavio' } const m = new Map(); m.set(`color`, `red`); // [[`color`, `red`]] m.set(`age`, 2); // [[`color, `red`], [`age`, 2]] console.log(m); // Map(2) { 'color' => 'red', 'age' => 2 } == [Array[2], Array[2]] // map이라는 것은 Object에 비슷한데, Array에 훨씬 더 가깝다는 것... 사실상 Array 형태란 말인데.. // Map.get(array[0]) console.log(m.get(`color`)); // red // array[1] console.log(m.get(`age`)); // 2 // Object에 비해서 Map의 경우는 서로 쌍으로 되있는 data... (key와 value 같은) 형태의 많은 데이타를 처리하는데 있어서 굉장히 효과적임.
Map for loop
map을 만든 다음에 하나씩 하나씩 추가하는 방식 == Map Constructer 방식 const m = new Map(); m.set(`color`, `red`); m.set(`age`, 2); console.log(m); // Map(2) { 'color' => 'red', 'age' => 2 } // 0: {"color" => "red"} // key: "color" // value: "red" // 1: {"age" => 2} // key: "age" // value: 2 console.log(m.get(`color`)); // red console.log(m.get(`age`)); // 2 // m.delete(`color`); // console.log(m); // Map(1) { 'age' => 2 } // m.clear(); // console.log(m); // [] 텅 빈 상태로 바뀌게 됌. console.log(m.has(`color`)); // true console.log(m.size); // 2
Map - keys( ), values( ), entries( )
Map - keys( ), values( ), entries( ) // Map Literal const m = new Map([[`color`, `red`], [`owner`, `Flavio`], [`age`, 2]]) Map은 key, value/ key, value 로 구성되고있음. console.log(m); // Map(3) { 'color' => 'red', 'owner' => 'Flavio', 'age' => 2 } for (const k of m.keys()) { console.log(k) // color, owner, age } for (const k of m.values()) { console.log(k) // red, Flavio, 2 } for (const [k, v] of m.entries()) { console.log(k, v); // color red, owner Flavio, age 2 } for (const [k, v] of m) { console.log(k, v); // color red, owner Flavio, age 2 } // Object vs Array vs Map // Map이라고 하는 것은 Object를 좀 더 array에 가깝게 구성하는 datatype. 그래서 대용량의, 용량이 큰 것들이 많을 때 Object에 비해서 Map으로 처리하는게 훨씬 더 컴퓨터 자원을 적게 소모하며 처리할 수 있다. 한마디로 연산속도가 훨씬 더 빠르다.
Map - Spread
Map - Spread const m = new Map([[`color`, `red`], [`owner`, `Flavio`], [`age`, 2]]) // Map은 key, value/ key, value 로 구성되고있음. console.log(m); // Map(3) { 'color' => 'red', 'owner' => 'Flavio', 'age' => 2 } 만약에 Map의 key들만 뽑아보고 싶다면 ? const a = m.keys(); console.log(a); // {} 이건 아님 const a = [m.keys()]; console.log(a); // [ [Map Iterator] { 'color', 'owner', 'age' } ] 이것도 아님 const a = [...m.keys()]; // Spread Operator console.log(a); // [ 'color', 'owner', 'age' ] 이거네!! const b = [...m.values()]; // Spread Operator console.log(b); // [ 'red', 'Flavio', 2 ] const c = [...m.entries()]; // Spread Operator console.log(c); // [ [ 'color', 'red' ], [ 'owner', 'Flavio' ], [ 'age', 2 ] ]
Set 기초
Set 기초 // Set => 기본적으로 Map과 대동소이. 하나정도가 다름. let animals = new Set(); animals.add('🐷'); animals.add('🐼'); animals.add('🐢'); animals.add('🐿'); console.log(animals.size); // 4 animals.add('🐼'); console.log(animals.size); // 4 이게 Map과 Set의 차이 // Map은 그냥 추가하면 추가되지만은 Set은 반복되는 item은 자동적으로 취소(삭제)함. console.log(animals.has('🐷')); // true animals.delete('🐷'); console.log(animals.has('🐷')); // false animals.forEach(animal => { console.log(`Hey ${animal}!`); }); // Hey 🐼! // Hey 🐢! // Hey 🐿! console.log(animals); // Set {} ==> Set이라는 것은 알 수 있지만, 그 안에 무엇이 들었는지는 알 수 x, 그래서 안에 무엇이 들어있는지 확인하려면 forEach를 하던가 혹은 다른 명령을 줘야한다. animals.clear(); console.log(animals.size); // 0 let myAnimals = new Set(['🐷', '🐢', '🐷', '🐷']); console.log(myAnimals.size); // 2 myAnimals.add(['🐨', '🐑']); // myAnimals.add('🐨', '🐑'); // 요런 식으로는 코딩하지 말라는 겁니당. + 두개 다 안나오고 앞에 아이만 나옴. myAnimals.add({ name: 'Rud', type: '🐢' }); // // 요런 식으로는 코딩하지 말라는 겁니당 console.log(myAnimals.size); // 4 동작은 하네용 myAnimals.forEach(animal => { console.log(animal); }); // 🐷 // 🐢 // ["🐨", "🐑"] // Aarray 자체가 통째로 들어감 // Object { name: "Rud", type: "🐢" } // Object 자체가 통째로 들어감 // 요런 식으로는 코딩하지 말라는 겁니당 ==> 이유: Set을 만들때에는 element들이 하나로 같은 종류여야 해용 '돼지'랑 '거북이'는 하나의 동물인데 3번째거는 하나의 array고 네번째는 하나의 object자나용, 이렇게 서로 다른 종류를 섞어서 Set을 구성하면 말라는 겁니당. 동작은 하는데 그런식으로 코딩하는 것은 바람직하지 않다는 겁니당.
Set methods
Set methods console.log('Only unique characters will be in this set.'.length); // 43 let sentence = new Set('Only unique characters will be in this set.'); console.log(sentence.size); // 18 반복되는 character들은 다 삭제한다는 거죵 let moreAnimals = new Set(['🐺', '🐴', '🐕', '🐇']); // iterable(item을 하나씩 셀 수 있다) == a kind of array for (let animal of moreAnimals) { console.log(`Howdy ${ animal }`); } // Howdy 🐺 // Howdy 🐴 // Howdy 🐕 // Howdy 🐇 // iterable한 data type의 특징 = next() let partyItems = new Set(['🍕', '🍾', '🎊']); console.log(partyItems); // Set(3) {"🍕", "🍾", "🎊"} // let items = partyItems.values(); // console.log(items.next()); // {value: "🍕", done: false} // console.log(items.next()); // {value: "🍾", done: false} // console.log(items.next()); // {value: "🎊", done: false} // console.log(items.next()); // {value: undefined, done: true} // console.log(items.next().done); // true let keys = partyItems.keys(); console.log(keys.next()); // {value: "🍕", done: false} console.log(keys.next()); // {value: "🍾", done: false} console.log(keys.next()); // {value: "🎊", done: false} console.log(keys.next()); // {value: undefined, done: true} console.log(keys.next().done); // true // next라고 하는 것은 항상 key를 보여주는 것이 아니라 item(value)만을 보여준다. // Object { // done: false, // value: "🍕" // } // Object { // done: false, // value: "🍾" // } // Object { // done: false, // value: "🎊" // } // true // done: false 는 다 아직 안둘러봤다는 뜻, 다 둘러봤으면 true.
next() method ⇒ array, map, set
next() method = array, map, set let set1 = new Set(); // adding element to the set set1.add(50); // value without key // Map은 key+value였지만, Set같은 경우 value만 줄 수 있음 set1.add(40); set1.add(30); set1.add(20); set1.add(10); console.log(set1); // Set(5) {50, 30, 40, 20, 10} // using entries to get iterator let getEntriesArry = set1.entries(); let getKeysArry = set1.keys(); let getValuesArry = set1.values(); // each iterator is array of [value, value] // prints [50, 50] console.log(getEntriesArry.next()); // {value: Array(2), done: false} // 자동적으로 key와 value를 만듬 console.log(getEntriesArry.next().value); // prints [40, 40] console.log(getEntriesArry.next().value); // prints [30, 30] console.log(getEntriesArry.next().value); // prints [20, 20] console.log(getEntriesArry.next().value); // prints [10, 10] console.log(getEntriesArry.next().value); // prints nothing console.log(getEntriesArry.next().value); // undefined console.log(getEntriesArry.next()); // {value: undefined, done: true} // next라고 하는 function은 주로 어떤 Set이든 혹은 Map이든 혹은 Array이든 각각의 아이템들을 불러오는, 그니까 어떤 집합속에 있는 원소들을 하나씩 하나씩 차례차례 순서대로 불러오는 역할을 함. // 불러오는 순서는 들어가 있는 순서. // next를 한번 호출하고 나면 자동으로 그 다음 단계로 넘어감. 그냥 console.log(getEntriesArry.next());를 하던가 console.log(getEntriesArry.next().value); 하던가 상관없이 한번 호출되었으면은 그 다음 단계로 넘어간단 말이죵. // !!! 우리는 하나의 item(value)를 넣었지만은 Set은 반드시 key + value 형식으로 저장을 한다는 것.그래서 add(50)을 하더라도 [50, 50] 이런 식으로 쓰인다는 거에요, 그래서 key를 뽑더라도 50, 40, 30, 20, 10 이 나오고 value를 뽑더라도 50, 40, 30, 20, 10이 나온다는 것.
has(), for of loop
has(), for of loop let set1 = new Set(); // adding element to the set set1.add(50); set1.add(30); // prints true console.log(set1.has(50)); // true // prints false console.log(set1.has(10)); // false let set1 = new Set(); // adding element to the set set1.add(50); set1.add(30); set1.add(40); set1.add("Geeks"); set1.add("GFG"); // getting all the values let getValues = set1.values(); // prints a SetIterator // that contains {50, 30, 40, "Geeks", "GFG"} // console.log(getValues); // {} // for ... in ==> object의 key: value 조합에서, key를 가져올 때 // for ... of ==> value를 보여주는 loop // forEach ==> array에 적용 for(let value of getValues) { console.log(value); } // 50, 30, 40, Geeks, GFG // getting all the values let getKeys = set1.keys(); // prints a SetIterator // that contains {50, 30, 40, "Geeks", "GFG"} console.log(getKeys); for(let key of getKeys) { console.log(key); } // 50, 30, 40, Geeks, GFG // Set은 key와 value 가 같은 data가 들어간다. // key를 뽑으나 value를 뽑으나 같은 data가 뽑힌다.
set, array and forEach
set, array and forEach let set1 = new Set(); // adding element to the set set1.add({Firstname: "Sumit", Lastname: "Ghosh"}); set1.add(50); set1.add(30); set1.add(40); set1.add("Geeks"); set1.add("GFG"); // Declaring a call back function // we are using only one parameter value // so it will ignore other two . function printOne(values) { console.log(values); } // It prints value of all the element // of the set // forEach는 array를 위한 method. == set이 곧 array. set1.forEach(printOne); // {Firstname: "Sumit", Lastname: "Ghosh"}, 50, 30, 40, Geeks, GFG // Declaring a call back function // we are using two parameter value // so it will ignore last one function printTwo(key, values) { console.log(key+" "+values); } // As key and values are same in set // so it will print values twice set1.forEach(printTwo); // [object Object] [object Object], 50 50, 30 30, 40 40, Geeks Geeks, GFG GFG const a = [2, 3, 4, 5]; // 눈에 보이지않는 key가 들어가 있음 . // 0 1 2 3 순으로 key가 할당된다. // Set은 value 자체가 바로 key로 동일하게 할당된다 // 그니까 Set에서는 Object도 key가 될 수 있고 number도 key가 될 수있고 string도 key가 될 수 있다는 거죵 a.forEach(printOne); // 2, 3, 4, 5 a.forEach(printTwo); // 0 2, 1 3, 2 4, 3 5
Decorators and forwarding, call/apply
Decorators and forwarding, call/apply // 1 // Functional programming // Pure(순수) function vs. impure function // pure function == when input is the same, the return value also should be same. // f(x) => x + 1; // pure function // g(x) => random(x); // impure function (ex. 주사위 굴리는 횟수의 평균값을 return해라) => 들어가는 input값은 똑같이 3이지만 return값은 다 다를수밖에 없음. function slow(x) { // identity function // there can be a heavy CPU-intensive job here(가정) console.log(`Called with ${x}`); return x; } // decorator // higher order function function cachingDecorator(func) { // Closure let cache = new Map(); //wrapper return function(x) { if (cache.has(x)) { // if the result is in the map return cache.get(x); // return it } let result = func(x); // otherwise call func => // Called with 1, 1 cache.set(x, result); // and cache (remember) the result (1, 1) return result; // 1 }; } let slowFn = cachingDecorator(slow); console.log( slowFn(1) ); // slowFn(1) is cached // Called with 1, 1 console.log( "Again: " + slowFn(1) ); // the same // Again 1 console.log( slowFn(2) ); // slowFn(2) is cached // Called with 2, 2 console.log( "Again: " + slowFn(2) ); // the same as the previous line // Again 1 // 2 // we'll make worker.slow caching let worker = { someMethod() { return 3; }, slow(x) { // actually, there can be a scary CPU-heavy task here console.log("Called with " + x); // Called with 2 // return x * worker.someMethod(); // 6 OK return x * this.someMethod(); // (*) this가 문제... // return x * 5; // OK } }; // same code as before function cachingDecorator(func) { let cache = new Map(); return function (x) { if (cache.has(x)) { return cache.get(x); } let result = func(x); // (**) cache.set(x, result); return result; }; } console.log(worker.slow(1)); // the original method works // Called with 1, 3 worker.slow = cachingDecorator(worker.slow); console.log(worker.slow(2)); // Whoops! Error: Cannot read property 'someMethod' of undefined console.log(worker.slow(2)); // 주어 + 동사 => Object.function() // 영어에서 대명사(this, that, he, she, they)가 만약 주어가 없으면, 일반인 또는 일반적인 대상 // 주어가 없는 function() 내부의 this는 일반인 == 가장 큰 object == window == global object // 가장 바깥에 있는 scope는 window == global object.
Call
Call // 1 function sayHi() { console.log(this.name); } let user = { name: "John" }; let admin = { name: "Admin" }; user[`sayHi`] = sayHi; user[`sayHi`](); // John admin[`sayHi`] = sayHi; admin[`sayHi`](); // Admin // 위 4줄을 한줄 한줄 즉 두줄로 표현해라! 는 밑 두줄. // use call to pass different objects as "this" sayHi.call( user ); // John = this user라는 object에 sayHi라는 function을 add하고 실행. sayHi.call( admin ); // Admin = this admin이라는 object에 sayHi라는 function을 add하고 실행. Call 한 걸음 더 //2 function say(phrase) { console.log(this.name + ': ' + phrase); } let user = { name: "John" }; // // user becomes this, and "Hello" becomes the first argument say.call( user, "Hello" ); // John: Hello user라는 object에 say라는 function을 넣고 argument에 "hello"를 넣고 실행. //3 let worker = { // Object someMethod() { return 3; }, slow(x) { console.log("Called with " + x); return x * this.someMethod(); // (*) } }; function cachingDecorator(func) { // Function console.log(func); // f slow() let cache = new Map(); return function(x) { if (cache.has(x)) { return cache.get(x); } let result = func.call(this, x); // // "this" is passed correctly now this라는 object에 func이라는 function을 넣고 x라는 argument를 넣어서 실행. 여기서 func = slow 이고, x는 2 // let result = func(x); cache.set(x, result); return result; }; } worker.slow = cachingDecorator(worker.slow); // now make it caching // function cachingDecorator이 worker.slow에 들어감 -> function(x)이 들어가는 자리는 정확히 worker object의 slow라고 하는 key에 담기게 된다. slow라고 하는 key가 function(x)을 가리키게 되는 것. 그럼 이 function(x)의 주어는 누구인가? 바로 worker이다. 이 function(x)의 주어가 worker라는 것을 알려주는 것이 바로 this. 주어와 동사를 어울리게 해주는 방식이 func.call(this, x); console.log( worker.slow(2) ); // works // Called with 2, 6 console.log( worker.slow(2) ); // works, doesn't call the original (cached) // 6
arguments and apply
arguments and apply // 1 function say(time, phrase) { // console.log(arguments); // scope가 구성될 때, 자동으로 설정되는 이름 중 하나.// {0: Array[2]} // console.log(arguments[0]); // ["10:00". "Hello"] // console.log(arguments[1]); // undefined console.log(`[${time}] ${this.name}: ${phrase}`); } let user = { name: "John" }; // Object let messageData = ['10:00', 'Hello']; // become time and phrase // user becomes this, messageData is passed as a list of arguments (time, phrase) say.call(user, messageData); // [10:00,Hello] John: undefined function say(time, phrase) { // console.log(arguments); // {0: "10:00", 1: "Hello"} // console.log(arguments[0]); // 10:00 // console.log(arguments[1]); // Hello console.log(`[${time}] ${this.name}: ${phrase}`); } let user = { name: "John" }; let messageData = ['10:00', 'Hello']; say.call(user, ...messageData); // [10:00] John: Hello (this=user) say.apply(user, messageData); // [10:00] John: Hello (this=user) 똑같은 효과 // 2 // let args = [1, 2, 3]; // Array // func.call(context, ...args); // pass an array as list with spread operator // func.apply(context, args); // is same as using apply // 3 function myArgs(...rest) { console.log(arguments); } myArgs(1); // {0: 1} myArgs(1, 2, 3); // {0: 1, 1: 2, 2: 3} // apply는 array 뿐 아니라, object도 destructuring할 수 있다. // 4 let worker = { slow(min, max) { console.log(`Called with ${min},${max}`); return min + max; } }; function cachingDecorator(func, hash) { let cache = new Map(); return function(...rest) { let key = hash(arguments); // (*) if (cache.has(key)) { return cache.get(key); } // let result = func.call(this, ...rest); // ok // let result = func.apply(this, rest); // ok let result = func.apply(this, arguments); // ok // apply는 array 뿐 아니라 object도 destructuring(필요한 값만 뽑아서 변수에 할당하는 것) 할 수있는 call이라고 보면 된다. // console.log(...rest); // 3, 5 // console.log(arguments); // {0: 3, 1: 5} cache.set(key, result); return result; }; } function hash(args) { return args[0] + ',' + args[1]; } worker.slow = cachingDecorator(worker.slow, hash); console.log( worker.slow(3, 5) ); // works // Called with 3, 5 , 8 console.log( "Again " + worker.slow(3, 5) ); // same (cached) // Again 8
Function binding
Function binding == bind 묶다 // function의 this를 특정한 object로 묶어놓는 것. //1 let user = { firstName: "Superman", sayHi() { console.log(`Hello, ${this.firstName}!`); } }; user.sayHi(); // Hello, Superman! setTimeout(user.sayHi); // setTimeout 주어가 Window. ==> Window.name // Hello, undefined! setTimeout(user.sayHi, 1000); // 주어가 Window. ==> Window.name // Hello, undefined! user.sayHi(); // Hello, Superman! //2 let user = { firstName: "Superman", sayHi() { console.log(`Hello, ${this.firstName}!`); } }; // // 인위적으로 sayHi()의 주어를 user로 설정하는 방법 setTimeout(() => user.sayHi(), 500); // Hello, Superman! setTimeout(user.sayHi, 1000); // Hello, undefined! // // ...within 1 second user = { firstName: `Batman`, sayHi() { console.log("Another user in setTimeout!"); } }; // 3 let user = { firstName: "Superman" }; function func() { console.log(this.firstName); } // 38번째 에피소드: 어울림 let funcUser = func.bind(user); console.log(funcUser); // f bound func() user = { firstName: `Batman`, } console.log(user.firstName); // Batman funcUser(); // ? Superman or Batman => Superman // 4 let user = { firstName: "Superman", // property sayHi() { console.log(`Hello, ${this.firstName}!`); } }; // bind function and object, not function and a property of an object let sayHi = user.sayHi.bind(user); // (*) sayHi(); // Hello, Superman! user.firstName = `Batman`; sayHi(); // Hello, Batman! user = { firstName: `Catwoman`, sayHi() {} } setTimeout(sayHi, 1000); // Hello, Batman! //5 let user = { firstName: "Superman", say(phrase) { console.log(`${phrase}, ${this.firstName}!`); } }; let say = user.say.bind(user); say("Hello"); // Hello, Superman! user.firstName = `Batman`; user.say = (phrase) => console.log(42); say("Bye"); // Bye, Batman!
여기까지의 내용을 여러번 정독하는 것만으로도, 자바스크립트에 대한 기본기는 충분히 튼튼하게 갖추었다고 볼 수 있다. Javascript Basics에서 정리된 모든 예제들은 javascript.info에서 더욱 상세히 만나볼 수 있다.
# javascript