스터디 8주차 주간공부 내용 - JS Stack 자료구조, push, pop 메서드, undo/redo 구현

잔잔바리디자이너·2022년 4월 29일
0

Study

목록 보기
14/19
post-thumbnail

Stack 이란?

데이터를 마지막에 밀어 넣고, 마지막에 밀어 넣은 데이터를 먼저 꺼내는 후입 선출 방식의 자료구조이다. 스택은 언제나 가장 마지막에 밀어 넣은 최신 데이터를 먼저 취득한다. push와 pop 메서드를 사용하면 쉽게 구현할 수 있다.

스택 구현해보기

  • 생성자 함수
const Stack = (function(){
  function Stack(array = []){
		this.array = array
  }
  Stack.prototype = {
    constructor: Stack,
    push(value){
      return this.array.push(value)
    },
    pop(){
      return this.array.pop()
    }
  }
 return Stack;
}())

const stack = new Stack();
  • ES6 클래스로 구현
class Stack{
  #array
  constructor(array = []){
    if(!Array.isArray(array)){
      throw new TypeError('This is not an array.')
    }
    this.#array = array
  }
  push(value){
    return this.#array.push(value)
  }
  pop(){
    return this.#array.pop()
  }
  get array(){
    return this.#array
  }
}

push와 pop 메서드 동작 이해해 보기, 직접 구현⭐️

class Stack{
  #array
  #index
  constructor(array = []){
    if(!Array.isArray(array)){
      throw new TypeError('This is not an array.')
    }
    this.#array = array
    this.#index = array.length
  }
  push(value){
 	this.#array[this.#index] = value
    this.#index++
    return this.#index
  }
  pop(){
    const returnValue = this.#array[this.#index-1]
    this.#array.length = --this.#index
    return returnValue
  }
  get array(){
    return this.#array
  }
}

배열이 비어있을 경우를 깜빡하고 처리를 안해줘서 에러를 발생 시켰다. 그래서 조건문으로 배열이 0 이하일 경우를 처리해줬다.

RangeError: Invalid array length
class Stack{
  #array
  #index
  constructor(array = []){
    if(!Array.isArray(array)){
      throw new TypeError('This is not an array.')
    }
    this.#array = array
    this.#index = array.length
  }
  push(value){
    if(value){
      this.#array[this.#index] = value
    	this.#index++
    	return this.#index 
    }
    return
  }
  pop(){
    if(this.#index>0){
    	const returnValue = this.#array[this.#index-1]
    	this.#array.length = --this.#index
    	return returnValue  
    }
    return
  }
  get array(){
    return this.#array
  }
}

스택 함수로 undo 기능 구헌해보기

Stack 클라스 함수 작성은 간단하니까 금방 했는데 막상 Undo, redo 동작을 구현하려니까 막-막...
이것 저것 뒤져보고 스택 구현 패턴까지 보다보니 그만하고싶어졌다;;
어떻게 짜야하는지 3일간 생각만하다가 일단 UI부터 때려박고 시작해서 어거지로 구현은 했다. 일단은 여기까지 만족하고 후퇴.

Undo function 구현 방법에는 두가지 디자인 패턴이 있다.

  • 메멘토 패턴
  • 커맨드 패턴

일단 간단하게 말하면 메멘토 패턴은 현재 상태를 캡쳐하고 저장하여 실행 취소를 구현하는 방법인데 커맨드 패턴에 비해서 구현이 쉽지만 메모리 관리 측면에서 비효율적이다. 하지만 난 쉽게가고 싶기때문에 메모리 따위는 신경쓰지 않는다.

⭐️결과물⭐️:

디자이너니까 박스쉐도우정도는 넣어줬다.

소스 코드:

// Create elements
const $app = document.getElementById("app");
const $wrapper = document.createElement("div");
$wrapper.className = "wrapper";
const $canvas = document.createElement("div");
$canvas.className = "canvas";
$app.appendChild($wrapper);
$wrapper.appendChild($canvas);

//palette color arr
const paletteList = ["red", "blue", "green", "black"];
createPalette(paletteList);

// create button
const $buttonDiv = document.createElement("div");
$buttonDiv.className = "buttonDiv";
$wrapper.appendChild($buttonDiv);
const $undoButton = document.createElement("div");
$undoButton.className = "undo";
$undoButton.innerHTML = "Undo";
const $redoButton = document.createElement("div");
$redoButton.className = "redo";
$redoButton.innerHTML = "Redo";
$buttonDiv.appendChild($undoButton);
$buttonDiv.appendChild($redoButton);

// push palette function
function createPalette(arr) {
  const $div = document.createElement("div");
  $div.className = "palette";
  $wrapper.appendChild($div);

  //push list
  arr.forEach((e) => {
    const $list = document.createElement("div");
    $list.className = "list";
    $list.setAttribute("id", e);
    $list.style.backgroundColor = e;
    $div.appendChild($list);
  });
}

// history object class
class Originator {
  constructor(model) {
    this._undoStack = [];
    this._redoStack = [];
    this._target = model;
  }
  take() {
    const snapshot = this._target.snapshot();
    this._undoStack.push(snapshot);
    this._redoStack = [];
  }

  undo() {
    if (this._undoStack.length) {
      const snapshot = this._target.snapshot();
      this._redoStack.push(snapshot);
      const preData = this._undoStack.pop();
      this._target.restore(preData);
    }
  }
  redo() {
    if (this._redoStack.length) {
      const snapshot = this._target.snapshot();
      this._undoStack.push(snapshot);
      const preData = this._redoStack.pop();
      this._target.restore(preData);
    }
  }
}

// target object class
class Model {
  constructor(data) {
    this.data = data;
  }
  snapshot() {
    return this.data;
  }
  restore(data) {
    this.data = data;
  }
}

// instance
const canvas = new Model("white");
const originator = new Originator(canvas);
// $canvas.style.backgroundColor = canvas.data;

// add event listner to palette lists
const e = document.getElementsByClassName("list");
for (let i = 0; i < e.length; i++) {
  e[i].addEventListener("click", () => {
    // capture and reset model's data
    originator.take();
    canvas.restore(e[i].getAttribute("id"));
    $canvas.style.backgroundColor = canvas.data;
  });
}

// add event listner to undo button
$undoButton.addEventListener("click", () => {
  originator.undo();
  $canvas.style.backgroundColor = canvas.data;
});

// add event listner to redo button
$redoButton.addEventListener("click", () => {
  originator.redo();
  $canvas.style.backgroundColor = canvas.data;
});

참조:

0개의 댓글