자바스크립트 스타일 가이드 학습하기 #2 (Airbnb) - 문자열, 함수, 화살표 함수, 클래스, 생성자

REASON·2022년 11월 4일
0

STUDY

목록 보기
109/127

어제에 이어 에어비앤비 스타일 가이드 학습하기

문자열

문자열에는 작은 따옴표(``) 를 사용하자.

// bad
const name = "Capt. Janeway";

// bad - template literals should contain interpolation or newlines
const name = `Capt. Janeway`;

// good
const name = 'Capt. Janeway';

문자열 작성할 때 템플릿 리터럴로 사용할 때가 종종 있었는데 권장되는 방식이 아니였다니...!

문자열 연결은 템플릿 리터럴을 사용하자.

// bad
function sayHi(name) {
  return 'How are you, ' + name + '?';
}

// bad
function sayHi(name) {
  return ['How are you, ', name, '?'].join();
}

// bad
function sayHi(name) {
  return `How are you, ${ name }?`;
}

// good
function sayHi(name) {
  return `How are you, ${name}?`;
}

+로 연결하는 방식은 학부생때 자바 배울 때 종종 봤던 거라 거부감은 없었지만 읽기가 헷갈리는 경우가 많았었다. 배열 형태로 만들어서 join 시키는 방법은 생각치못했지만 몰라도 되는 방법이였다.

템플릿 문자열 사용할때 가끔 ${ name } 처럼 사이에 공백을 넣어서 사용할 때도 있었는데 이게 bad한 거였다니..또르르 나름 눈에 잘 띄어서 좋다고 생각했는데 아니였나봅니다.

함수

함수 선언식보다 함수 표현식을 사용하자.

// bad
function foo() {
  // ...
}

// bad
const foo = function () {
  // ...
};

// good
// 변수 참조 호출과 구분되는 이름
const short = function longUniqueMoreDescriptiveLexicalFoo() {
  // ...
};

선언식 함수가 호이스팅 된다는 점 때문에 좋아해서 자주 사용했다. 개인적으로 표현식보다 선언식을 더 좋아는 이유중 하나였는데.. 그거때문에 권장하지 않는다고 한다.
함수는 변수처럼 위에서부터 하나씩 착착착 쓰는거보다 함수는 아래로 쭉 빼서 정의 해놓고 위에서 꺼내쓰는걸 선호했었는데 선언식 함수보다 표현식을 권장한다니 너무 슬프더 ㅠㅠㅠㅠ..
이제 에어비앤비 컨벤션을 따라야 한다면 보내줘야 하는 건가...ㅋㅋㅋ

표현식 함수를 명시적으로 작명해줘야 한다는 점도 놀라웠다. 디버깅에 더 좋으니 그렇게 사용하라는 것 같다.

변수명만 정의하고 무명으로 사용해도 되지 않을까? 싶었는데 이 부분은 정말.. 내 코드 스타일과 너무나도 상반됐다. ㅋㅋㅋㅋ
그냥 화살표 함수를 사용하는 게 좋겠다..

함수 이외의 블록에서 함수를 선언하지 말 것.

예를 들면 if나 while문에서 함수를 선언하는 것이 있을 수 있다.

// bad
if (currentUser) {
  function test() {
    console.log('Nope.');
  }
}

// good
let test;
if (currentUser) {
  test = () => {
    console.log('Yup.');
  };
}

오.. if문 내부에서는 화살표 함수를 사용해야 하는 건가보다. test 변수가 if문 바깥에 선언되어 있는데 if문 내부에서 함수로 정의해주어도 상관이 없나보다.

함수 인자를 변경하지 말 것.

// really bad
function handleThings(opts) {
  opts = opts || {};
  // ...
}

// still bad
function handleThings(opts) {
  if (opts === void 0) {
    opts = {};
  }
  // ...
}

// good
function handleThings(opts = {}) {
  // ...
}

default로 작성해주는 게 더 좋구나.
함수 매개인자가 비어있는 경우 함수 내부에서 고쳐쓰지 말라는 것 같다.

함수 생성자를 사용하지 않기

// bad
var add = new Function('a', 'b', 'return a + b');

// still bad
var subtract = Function('a', 'b', 'return a - b');

함수 생성자.. 엄청 생소하다.
이렇게 작성하는 사람은 아직까지 못봤는데.

함수 시그니처에 공백 넣기

// bad
const f = function(){};
const g = function (){};
const h = function() {};

// good
const x = function () {};
const y = function a() {};

매개 변수를 변경하지 않기

// bad
function f1(obj) {
  obj.key = 1;
}

// good
function f2(obj) {
  const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;
}

매개변수 객체를 변경하면 사이드 이펙트가 발생할 수 있다.

const obj = {
  name: "아이유"
};

console.log(obj); // {name: '아이유'}

const test = (obj) => {
  obj.name = "IU";
};

test(obj);
console.log(obj); // {name: 'IU'}

오브젝트와 같이 레퍼런스 변수들은 함수 내부에서 변경하지 않도록 주의!

매개 변수를 재할당 하지 말자

최적화 문제도 있을 수 있구나..
가끔 한번씩 했던 짓이라 조심해야겠다. ㅋㅋㅋㅋ

// bad
function f1(a) {
  a = 1;
  // ...
}

function f2(a) {
  if (!a) { a = 1; }
  // ...
}

// good
function f3(a) {
  const b = a || 1;
  // ...
}

function f4(a = 1) {
  // ...
}

아예 새로운 변수를 만들어서 할당하거나, default 값을 사용해서 해결할 수 있는 경우엔 defult를 사용하도록 하자.

가변 인자 함수에는 ... 를 사용하자.

// bad
const x = [1, 2, 3, 4, 5];
console.log.apply(console, x);

// good
const x = [1, 2, 3, 4, 5];
console.log(...x);

// bad
new (Function.prototype.bind.apply(Date, [null, 2016, 8, 5]));

// good
new Date(...[2016, 8, 5]);

매개 변수가 여러줄 일 때

// bad
function foo(bar,
             baz,
             quux) {
  // ...
}

// good
function foo(
  bar,
  baz,
  quux,
) {
  // ...
}

// bad
console.log(foo,
  bar,
  baz);

// good
console.log(
  foo,
  bar,
  baz,
);

1번 function은 너무 생긴거부터 불편하게 생겼다...

근데 마지막 매개변수에 콤마를 넣는 건 처음보네.
보통 오브젝트 key : value, key : value 할때나 마지막 콤마 넣을지 안넣을지 취향에 따라 갈린다고 생각했는데..

매개 변수도 마지막에 콤마를 넣얼 수 있다니.. 저렇게 써도 에러가 안 나는구나..ㅋㅋㅋ

아무튼 이 코드의 핵심은 들여쓰기가 같이 들어가야 된다는 것이다!

화살표 함수

자바스크립트 초반에 화살표 함수 보고 이 이상하게 생긴건 어떻게 쓰는거지..? 했던 기억이 있다.
지금은 언제 그랬냐는 듯이 정말 너무 잘쓰는중. 마치 스프레드 연산자처럼..

익명 함수를 사용할 때는 화살표 함수를

// bad
[1, 2, 3].map(function (x) {
  const y = x + 1;
  return x * y;
});

// good
[1, 2, 3].map((x) => {
  const y = x + 1;
  return x * y;
});

this 를 알기에도 좋기 때문에 화살표 함수가 권장되는 경우가 많은 것 같다.

// bad
[1, 2, 3].map(number => {
  const nextNumber = number + 1;
  `A string containing the ${nextNumber}.`;
});

// good
[1, 2, 3].map(number => `A string containing the ${number}.`);

// good
[1, 2, 3].map((number) => {
  const nextNumber = number + 1;
  return `A string containing the ${nextNumber}.`;
});

// good
[1, 2, 3].map((number, index) => ({
  [index]: number,
}));

function foo(callback) {
  const val = callback();
  if (val === true) {
  }
}

let bool = false;

// bad
foo(() => bool = true);

// good
foo(() => {
  bool = true;
});

리턴을 명시하냐, 괄호를 쓰냐에 대한 코드 스타일인듯하다.
명확성, 일관성 유지를 위해서 인자는 괄호로 감싸는 것을 권장하고 있다.

클래스, 생성자

드디어 올 것이 왔구나..
클래스 울렁증이 있어서 클래스랑 친해지기 힘들었는데ㅠㅠ

prototype을 직접 조작하는 것을 피하자.
class를 사용하는 것이 권장된다.

// bad
function Queue(contents = []) {
  this.queue = [...contents];
}
Queue.prototype.pop = function () {
  const value = this.queue[0];
  this.queue.splice(0, 1);
  return value;
};

// good
class Queue {
  constructor(contents = []) {
    this.queue = [...contents];
  }
  pop() {
    const value = this.queue[0];
    this.queue.splice(0, 1);
    return value;
  }
}

prototype으로 추가하는게 익숙해서 자료구조 공부할 때 저렇게 짰었는데 사실 연관된 하나라고 생각하면 class가 더 구조화가 잘 되어있는 것은 사실이긴 하다.
다만 this가 꼴보기 싫을 때가 많을뿐..ㅜ

상속할 때는 extends

// bad
const inherits = require('inherits');
function PeekableQueue(contents) {
  Queue.apply(this, contents);
}
inherits(PeekableQueue, Queue);
PeekableQueue.prototype.peek = function () {
  return this.queue[0];
};

// good
class PeekableQueue extends Queue {
  peek() {
    return this.queue[0];
  }
}

생성자 함수를 상속하는 코드는 처음봤다.
오.. 저렇게 사용하는구나 보기에도 쓰기 싫게 생기긴 했다.

클래스의 메소드가 this를 반환하면 체이닝을 할 수 있다.

// bad
Jedi.prototype.jump = function () {
  this.jumping = true;
  return true;
};

Jedi.prototype.setHeight = function (height) {
  this.height = height;
};

const luke = new Jedi();
luke.jump(); // => true
luke.setHeight(20); // => undefined

// good
class Jedi {
  jump() {
    this.jumping = true;
    return this;
  }

  setHeight(height) {
    this.height = height;
    return this;
  }
}

const luke = new Jedi();

luke.jump()
  .setHeight(20);

와 this 반환하면 체이닝 할 수 있다는 점을 왜 모르고 있었지 ㅋㅋㅋㅋ
이건 알고 있으면 나중에 써볼 일이 있을 것 같다.

this를 사용하지 않으면 static으로 선언 해서 정적 메소드임을 표기하자.

// bad
class Foo {
  bar() {
    console.log('bar');
  }
}

// good 
class Foo {
  bar() {
    console.log(this.bar);
  }
}

// good 
class Foo {
  constructor() {
    // ...
  }
}

// good
class Foo {
  static bar() {
    console.log('bar');
  }
}

this가 사용되지 않으면 static 키워드를 사용해주면 되는구나.. 오앙..
단순히 인스턴스 안 만들고 사용할 때만 static을 사용해주면 된다 정도만 알고 있었는데 this와도 연관이 있을 수 있다는 것을 알게되었다.


참고 자료
Airbnb JavaScript Style Guide

2개의 댓글

comment-user-thumbnail
2022년 11월 4일

오호 코딩 스타일에 관심이 많으신가 봐욤^^ 이틀 연속으로 코드 스타일에 대해서 올리셨네욤^^
항상 열심히 하는 모습 보기 좋습니다.

1개의 답글