바닐라코딩 프렙 과정을 수강하면서 Underscore
과제를 통해 직접 메소드들을 구현해보니, 자바스크립트 내부 구조에 대해서 좀 더 명확히 이해할 수 있었다. Object, Array를 처리하는 방법 및 Higher-order function을 이해하기 위해서는 이만한 과제가 없는 듯하다.
부트캠프 어드미션을 대비하기 위해서라도, 추가적으로 Lodash
를 구현해보기로 하였다.
_.chunk(array, [size=1]) : size에 맞는 덩어리로 배열을 분리해주는 메소드
Examples
_.chunk(['a', 'b', 'c', 'd'], 2);
// => [['a', 'b'], ['c', 'd']]
_.chunk(['a', 'b', 'c', 'd'], 3);
// => [['a', 'b', 'c'], ['d']]
Code
function chunk (collection, size) {
// 예외처리 부분
size = Math.max(parseInt(size), 0); // 음수일 경우 0, 양수일 경우 정수
const length = collection === null ? 0 : collection.length;
if (!length || size < 1) return []; // 빈 배열이거나, size가 0일 때 빈 배열 return
// 구현 부분
const result = new Array(Math.ceil(length / size));
let index = 0;
let resIndex = 0;
while(index < length) {
result[resIndex++] = collection.slice(index, index+size);
index += size;
}
return result;
}
_.reduce(collection, iterator, accumulator) : collection의 각 element을 iterator에 적용하여 최종값을 return하는 메소드이며, 이 때 accumulator는 초기값임.
Examples
var sum = _.reduce([1, 2, 3], function(memo, num){ return memo + num; }, 0); // 6
Code
_.reduce = function (collection, iterator, accumulator) {
let tally;
let newCollection;
if (Array.isArray(collection)) {
newCollection = [...collection];
} else {
newCollection = Object.values(collection);
}
if (accumulator === undefined) tally = newCollection.shift();
else tally = accumulator;
for (let i = 0; i < newCollection.length; i++) {
tally = iterator(tally, newCollection[i]);
}
return tally;
};
_.curry(func, [arity=func.length]) : arguments를 받고, func.length와 일치하면 func를 실행하고, arguments가 부족하면 함수를 return 하는 함수
Examples
var abc = function(a, b, c) {
return [a, b, c];
};
var curried = _.curry(abc);
curried(1)(2)(3);
// => [1, 2, 3]
Code
// 첫번째 풀이
function curry(fn) {
const arity = fn.length;
const memory = [];
return function resolver (...args) {
for (let i = 0; i < args.length; i++) {
memory.push(args[i]);
}
if (memory.length === arity) {
return fn.apply(this, memory);
} else {
return resolver;
}
}
}
// 두번째 풀이
function curry(fn) {
const arity = fn.length;
return function resolver() {
const memory = Array.prototype.slice.call(arguments);
return function () {
const local = memory.slice();
Array.prototype.push.apply(local, arguments);
let next;
if (local.length === arity) {
next = fn;
} else next = resolver;
return next.apply(this, local);
}
}();
}
_.throttle(func, [wait=0], [options={}]) : 마지막 함수가 호출된 이후 일정 시간이 지나기 전에는 다시 호출되지 않도록 하는 메소드
Code
function throttle(func, delay) {
let isCalled = true;
return function (...args) {
const context = this;
if (isCalled) {
func.apply(context, args);
isCalled = false;
setTimeout (() => {
isCalled = true;
},delay)
}
}
}
_.debounce(func, [wait=0], [options={}]) : 연이어 호출되는 함수들 중 마지막 함수만 호출되도록 하는 메소드
Code
function debounce(func, delay) {
let timeoutId;
return function (...args) {
if (timeoutId) {
clearTimeout(timeoutId);
}
timeoutId = setTimeout(function () {
func(...args);
}, delay);
}
}
_.contains(list, value, [fromIndex]) :
value
가list
에 존재할 경우true
를 반환하는 메소드
Examples
_.contains([1, 2, 3], 3); // true
Code
_.contains = function (collection, target) {
let newCollection;
let result = false;
if (Array.isArray(collection)) newCollection = collection;
else newCollection = Object.values(collection);
_.reduce(newCollection, function (memo, item) {
if (item === target) result = true;
}, 0)
return result;
};
_.memoize(function, [hashFunction]) : 이미 처리된 연산의 경우 cache에서 바로 return 하고, 연산되지 않은 부분만 추가로 연산하고, 추후 재사용을 위헤 cache에 메모하여, 결과적으로 속도, 성능을 재고하는 메소드
Examples
var fibonacci = _.memoize(function(n) {
return n < 2 ? n: fibonacci(n - 1) + fibonacci(n - 2);
});
Code
_.memoize = function (func) {
const cache = {};
return function () {
const obj = JSON.stringify(arguments);
if (!(cache.hasOwnProperty(obj))) {
cache[obj] = func.apply(this, arguments);
}
return cache[obj];
}
};
_.concat(array, [values]) :
array
에 기타 array 또는 value를 합쳐 한 개의 array로 만들어주는 메소드
Examples
var array = [1];
var other = _.concat(array, 2, [3], [[4]]);
console.log(other);
// => [1, 2, 3, [4]]
console.log(array);
// => [1]
Code
function concat(array, ...args) {
let result = [];
for (let t = 0; t < array.length; t++) {
result[t] = array[t];
} // 깊은 복사를 위해서 위와 같이 설정
for (let i = 0; i < args.length; i++) {
if (Array.isArray(args[i])) {
for (let j = 0; j< args[i].length; j++) {
result.push(args[i][j]);
}
} else {
result.push(args[i]);
}
}
return result;
}
_.fill(array, value, [start=0], [end=array.length]) : 인자로 받은
value
값을 처음부터 끝까지array
에 override하는 메소드
Examples
var array = [1, 2, 3];
_.fill(array, 'a');
console.log(array);
// => ['a', 'a', 'a']
_.fill(Array(3), 2);
// => [2, 2, 2]
_.fill([4, 6, 8, 10], '*', 1, 3);
// => [4, '*', '*', 10]
Code
function fill (array, value, start = 0, end = array.length) {
const newArray = array;
let filled = [];
let length;
if (start || end) {
length = end - start;
}
for (let i = 0; i < length; i++) {
filled.push(value);
}
newArray.splice(start, length);
newArray.splice(start,0,...filled);
return newArray;
}
_.merge(object, [sources]) :
object
에other
를 병합하는 메소드로서, object에 key가 있을 경우, 해당 key의 value 값을 override 하고, key가 없을 경우, object에 key를 추가하여 병합하는 메소드
Examples
var object = {
'a': [{ 'b': 2 }, { 'd': 4 }]
};
var other = {
'a': [{ 'c': 3 }, { 'e': 5 }]
};
_.merge(object, other);
// => { 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] }
Code
function merge(object, other) {
for (let key in other) {
if (object.hasOwnProperty(key)) {
for (let i = 0; i < other[key].length; i++) {
Object.assign(object[key][i], other[key][i]);
// assgin 메소드 : 자바스크립트 기본 내장 메소드로 객체를 합침
}
} else {
object[key] = other[key];
}
}
return object;
}