티스토리 뷰
🔖TAG 💡Computed Property Name, 💡hasOwnProperty, 💡Lockup Table, 💡Object Destructuring, 💡Object.freeze, 💡Optional Chaining, 💡Prototype조작지양, 💡Shorthand Properties, 💡객체직접접근지양📕

출처😌
자료🙄
Git: pocojang/clean-code-js (github.com)
목차
1. 과정 소개
2. 변수 다루기
3. 타입 다루기
4. 경계 다루기
5. 분기 다루기
6. 배열 다루기
7-1. Shorthand Properties
7-2. Computed Property Name
7-3. Lockup Table
7-4. Object Destructuring
7-5. Object.freeze
7-6. Prototype 조작 지양하기
7-7. hasOwnProperty
7-8. 직접 접근 지양하기
7-9. Optional Chaining
7. 객체 다루기
7-1. Shorthand Properties
CSS의 backgroun-??? 프로퍼티들을 background로 한번에 축약할 수 있습니다.
이것을 CSS에서의 Shorthand Property라고 합니다.
.before {
background-color: #000;
background-image: url(images/bg.gif);
background-repeat: no-repeat;
background-position: left top;
margin-top: 10px;
margin-right: 5px;
margin-bottom: 10px;
margin-left: 5px;
}
.after {
background: #000 url(images/bg.gif) no-repeat left top;
margin: 10px 5px 10px 5px;
}
아래는 JS에서의 Shorthand Property입니다.
/**
* Shorthand Properties
*/
const counterApp = combineReducers({
counter: counter,
extra: extra,
});
// 축약형
const counterApp = combineReducers({
counter,
extra,
});
/**
* Shorthand Properties
* Concise Method (간결한 메서드) -> 함수구문을 간결하게 하기
* ES2015+
*/
const firstName = 'poco';
const lastName = 'jang';
const person = {
firstName: 'poco',
lastName: 'jang',
//위와 동일
firstName,
lastName,
getFullName: function () {
return this.firstName + ' ' + this.lastName;
},
// Concise Method
getFullName2() {
return this.firstName + ' ' + this.lastName;
},
getFullName3,
};
// 분리된 형태의 함수 구문 사용도 가능
function getFullName3() {
return this.firstName + ' ' + this.lastName;
}
7-2. Computed Property Name (계산된 속성 이름)
JS를 사용하는데 있어 React문법 인지 Vue문법인지 헤깔리는 분들을 위해 이번 챕터를 보겠습니다.
[ e.target.name ] 부분이 Computed Property Name 입니다.
[ ] 안에 식(표현식)이 들어가면 됩니다.
return부분의 onChange이벤트의 handleChange 함수는 이벤트객체(e)를 받을 수 있습니다.
handleChange함수안의 e.target에 접근해서 [ ]을 이용해서 동적인 프로퍼티 값을 받을 수 있습니다.
그러면 첫 번째 input을 클릭하게 되면,
value속성의 state.id 값이 [e.target.name] 인 name에 담기게 되는 것입니다.
아래처럼 말이죠.
setState({ name : state.id });
import React, { useState } from 'react';
function SomeComponent() {
const [state, setState] = useState({
id: '',
password: '',
});
const handleChange = e => {
setState({
[e.target.name]: e.target.value,
});
};
return (
<React.Fragment>
<input value={state.id} onChange={handleChange} name="name" />
<input value={state.password} onChange={handleChange} name="password" />
</React.Fragment>
);
}
export default SomeComponent;
두 번째 케이스는 redux문법 입니다.
key인 [ noop ]는 value값으로 함수를 가지고 있는 메서드 입니다.
여기서는 [ noop ] 부분이 computed property name입니다.
const noop = createAction('INCREMENT');
const reducer = handleActions(
{
[noop]: (state, action) => ({
counter: state.counter + action.payload,
}),
},
{ counter: 0 },
);
세 번째 케이스는 vuex입니다.
vuex의 Store도 mutations 부분을 보면
속성 -> [SOME_MUTATION]
매개변수 받는 부분 -> (state)
함수의 body -> { }
로 구분되어 있습니다.
[SOME_MUTATION](state) { } 자체가 함수입니다.
함수의 이름 부분만 Computed Property Name으로 되어있는 것입니다.
import Vuex from 'vuex';
import { SOME_MUTATION } from './mutation-types';
export const SOME_MUTATION = 'SOME_MUTATION';
const store = new Vuex.Store({
state: {
// some code...
},
mutations: {
[SOME_MUTATION](state) {},
↑ ↑ ↑
함수명 매개변수 함수몸체
},
});
JS로 Computed Prorperty Name 만들어보기
const funcName0 = 'func0';
const funcName1 = 'func1';
const funcName2 = 'func2';
const obj = {
[funcName0]() {
return 'func0';
},
[funcName1]() {
return 'func1';
},
[funcName2]() {
return 'func2';
},
};
for (let i = 0; i < 3; i++) {
console.log(obj[`func${i}`]());
}
/*출력 결과
func0
func1
func2
*/
결국 JS를 잘알아야 Vue든 React든 잘 이용할 수 있다는 것입니다.
7-3. Lockup Table
배열 데이터 구조에서 key와 value로 관리된 배열이 나열된 형태를 뜻합니다.
key와 value로된 JSON 데이터라고 생각하시면 됩니다.
아래는 많이 쓰는 if / else 구문입니다.
if문이 많이 늘어진다면 switch 문으로 바꿔서 쓰는게 좋습니다.
/**
* Object Lookup Table
*/
function getUserType(type) {
if (type === 'ADMIN') {
return '관리자';
} else if (type === 'INSTRUCTOR') {
return '강사';
} else if (type === 'STUDENT') {
return '수강생';
} else {
return '해당 없음';
}
}
case가 명확하기 때문에 switch문이 조금 더 적절해보입니다.
하지만 case도 점점 더 늘어진다면 switch문도 좋은 방법은 아닙니다.
/**
* Object Lookup Table
*/
function getUserType(type) {
switch (key) {
case 'ADMIN':
return '관리자';
case 'INSTRUCTOR':
return '강사';
case 'STUDENT':
return '수강생';
default:
return '해당 없음';
}
}
📌이럴 때 Object LoockUp Table을 쓰면 좋습니다.
위의 switch 분기문들이 아래 객체의 key와 value로 이루어져있는걸 알 수 있습니다.
USER_TYPE은 건들지 말라는 의미로 대문자로 이루어진 스네이크 케이스(상수로 쓸 때 규약)로 적혀있습니다.
return을 보면 USER_TYPE[type]으로 되어있으니 Dot 연산자로 key를 넣어주면 value값을 얻을 수 있게 되는 겁니다.
/**
* Object Lookup Table
*/
function getUserType(type) {
const USER_TYPE = {
ADMIN: '관리자',
INSTRUCTOR: '강사',
STUDENT: '수강생',
};
return USER_TYPE[type] || '해당 없음';
}
getUserType('ADMIN') // '관리자'
getUserType('INSTRUCTOR') // '강사'
getUserType('STUDENT') // '수강생'
getUserType('PARENT') // '해당 없음'
아래는 위의 코드에서
null 병합 연산자를 이용해서 바로 return을 시켜서
코드를 더욱 간결화 시켰습니다.
정리하자면 아래 코드보다 바로 위의 코드가 바람직한 코드 스타일입니다!
/**
* Object Lookup Table
*/
function getUserType(type) {
return (
{
ADMIN: '관리자',
INSTRUCTOR: '강사',
STUDENT: '수강생',
}[type] ?? '해당 없음'
);
}
JS에서 Computed Property Name 을 이용해서
불필요한 분기문을 줄일 수 있었던 예로 LoockUp Table을 보았는데요.
상수들을 잘 이용해서 LoockUp Table과 개체를 잘 이용하면 함수들을 더 유연하게 쓸 수 있습니다.
7-4. Object Destructuring (객체 구조분해)
아래의 코드 문제점을 보겠습니다.
먼저 매개변수들의 순서가 가장 문제가 됩니다.
function Person(name, age, location) {
this.name = name;
this.age = age;
this.location = location;
}
const poco = new Person('poco', 30, 'korea');
매개변수중에 undefined로 넘겨주고 싶을 때는 구조분해로 넘겨주면 좋습니다.
언뜻 보면 별 차이를 모르실 수 있지만, 아래의 구조분해는 순서를 지키지 않아도 되는 장점이 있습니다.
function Person({ name, age, location }) {
this.name = name;
this.age = age;
this.location = location;
}
const poco = new Person( {name: 'poco', age: 30, location: 'korea', });
두 번째 케이스를 보겠습니다.
아래 코드는 리액트에서 많이 보는 구조입니다.
배열을 구조분해 할당하는 방법과
배열을 이용해서 객체 구조분해 할당을 하는 방법을 보겠습니다.
const orders = ['First', 'Second', 'Third'];
const st = orders[0];
const rd = orders[2];
// 아래처럼 배열을 구조분해 할당 해도 됩니다.
const [ first, ,third ] = orders
// console.log(first) // 'First'
// console.log(third) // 'Third'
📌// 더 명확한 방법으로는 객체로 하는 방법입니다.
// 배열이 JS에서는 객체로 취급되는 점을 이용했습니다.
const {0: 하나, 2: 셋 } = orders
console.log(하나) // 'First'
console.log(셋) // 'Third'
세 번째 케이스도 리액트에서 볼 수 있는 구조입니다.
매개변수를 받아올 때 props를 많이 이용해서 씁니다.
props도 객체 구조분해할당으로 받을 수 있습니다.
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root'),
);
//객체 구조 분해 할당
function Welcome( { name } ) {
return <h1>Hello, {name} </h1>;
}
구조분해 할당을 사용하면 명시적인 코드작성을 할 수 있습니다😉
7-5. Object.freeze
의미 그대로 객체를 동결한다고 생각하시면 됩니다.
/**
* Object.freeze
* freeze는 shallow copy만 한다.
*
* 그래서 deep copy를 하려면 아래의 방법을 이용한다.
* 1. 대중적인 유틸 라이브러리 (lodash)
* 2. 직접 유틸 함수 생성
* 3. stackoverflow
* 4. TypeScript => readonly
*/
const STATUS = Object.freeze({
PENDING: 'PENDING',
SUCCESS: 'SUCCESS',
FAIL: 'FAIL',
OPTIONS: {
GREEN: 'GREEN',
RED: 'RED',
},
});
// Object.isFrozen(STATUS) // true
// Object.isFrozen(STATUS.PENDING) // true
STATUS.SUCCESS = 'NO SUCCESS'
STATUS.SUCCESS // SUCCESS <- freezing이 된 상태
// Object.isFrozen(STATUS.OPTIONS) // false <- ?!?! shallw copy만 된 상태
STATUS.OPTIONS.GREEN = 'G'
STATUS.OPTIONS.YELLOW = 'Y'
STATUS.OPTIONS // {GREEN: 'G', RED: 'RED', YELLOW: 'Y'} <- freezing이 안된상태라 값이 바뀔 수 있음
// shallow copy vs deep copy
// Object.freeze는 Deep Copy는 안되는 걸 알아야합니다.
// 그래서 객체안의 객체 프로퍼티를 프리징하려면 따로 프리징을 더 해줘야합니다.
// 2번째 방식(간단한 예시) - 직접 유틸 함수 생성
function deepFreeze(targetObj) {
// 1. 객체를 순회
// 2. 값이 객체인지 확인
// 3. 객체이면 재귀
// 4. 그렇지 않으면 Object.freeze
Object.keys(targetObj).forEach(key => {
if (/*객체가 맞다면 */) {
deepFreeze(targetObj[key])
}
})
return Object.freeze(targetObj);
}
7-6. Prototype 조작 지양하기
이유는
1. class를 활용할 수 있기 때문입니다.
2. JS의 내장 객체를 건들지 말아야 하기 때문입니다. (혼선이 생길 가능성有)
// BAD
function Car(name, brand) {
this.name = name;
this.brand = brand;
}
Car.prototype.sayName = function() {
return this.brand + '-' + this.name;
}
/**
* Prototype 조작 지양하기
*
* 1. 이미 JS는 많이 발전했다.
* 1-1. 직접 만들어서 모듈화
* 1-2. 직접 만들어서 모듈화 => 배포 => NPM
* 2. JS 빌트인 객체를 건들지말자
*/
class Car {
constructor(name, brand) {
this.name = name;
this.brand = brand;
}
sayName() {
return this.brand + '-' + this.name;
}
}
const casper = new Car('캐스퍼', '현대');
7-7. hasOwnProperty
hasOwnProperty() 메소드는 객체가 특정 프로퍼티를 가지고 있는지를 나타내는 불리언 값을 반환합니다.
const person = {
name: 'BOB'
}
person.hasOwnProperty('name') // true
person.hasOwnProperty('age') // false
보통 for in 문에서 많이 활용합니다.
for in 스니펫을 쓰면 아래와 같이 뜹니다. 왜그럴까요?
for (const key in object) {
if (Object.hasOwnProperty.call(object, key)) { // <- ?!?!
const element = object[key];
}
}
아래를 보면 hasOwnProperty 메서드의 출력결과가 서로 다릅니다.
그래서 Object.prototype.hasOwnProperty()를 사용하고 싶다면
📌Object에서 hasOwnProperty를 가져와서 써야합니다.
const person = {
name: 'hyeonseok',
};
// Object.protoptype.hasOwnProperty() 를 사용함
person.hasOwnProperty('name') // true
const foo = {
hasOwnProperty: function () {
return 'hasOwnProperty';
},
bar: 'string',
};
// 조작한 foo.hasOwnProperty()를 사용함
foo.hasOwnProperty('bar') // 'hasOwnProperty' <- 동작결과다름
//Object에서 hasOwnProperty를 가져와서 써야합니다.
Object.prototype.hasOwnProperty.call(foo, 'bar') // true
계속 가져와서 쓰는게 귀찮으면
코드 스니펫을 만들어 쓰는게 편합니다.
/**
* hasOwnProperty
*/
function hasOwnProp(targetObj, targetProp) {
return Object.prototype.hasOwnProperty.call(
targetObj,
targetProp,
);
}
const person = {
name: 'hyeonseok',
};
hasOwnProp(person, 'name'); // true
const foo = {
hasOwnProperty: function () {
return 'hasOwnProperty';
},
bar: 'string',
};
hasOwnProp(foo, 'hasOwnProperty'); // true
여기 부분은 참고용으로만 가볍게 보길 추천합니다.
7-8. 직접 접근 지양하기
접근자(get) - JavaScript | MDN (mozilla.org)
설정자(set) - JavaScript | MDN (mozilla.org)
/**
* 직접 접근 지양하기
* 예측 가능한 코드를 작성해서 동작이 예측 가능한 앱
*/
// 직접 접근 지양
const model = {
isLogin: false,
isValidToken: false,
};
// model에 대신 접근
function setLogin(bool) {
model.isLogin = bool;
serverAPI.log(model.isLogin);
}
// model에 대신 접근
function setValidToken(bool) {
model.isValidToken = bool;
serverAPI.log(model.isValidToken);
}
// model에 직접 접근 X
function login() {
setLogin(true);
setValidToken(true);
}
// model에 직접 접근 X
function logout() {
setLogin(false);
setValidToken(false);
}
someElement.addEventListener('click', login);
7-9. Optional Chaining
/**
* Optional Chaining
*/
const js = {
name: {
pasts: ['Mocha', 'LiveScript'],
current: 'JavaScript',
},
author: 'Brendan Eich',
birth: '1995-12-4',
extension: '.js',
paradigm: ['script', 'object', 'functional'],
};
if (js) {
if (js.name) {
if (js.name.current) {
return js.name.current;
}
}
}
if (js && js.name && js.name.current) {
return js.name.current;
}
/**
* Optional Chaining
*/
const js = {
version: [
{
name: '1st & 2nd',
birth: '1998-10',
},
{
name: '3rd',
birth: '2000-11',
},
{
name: '5th',
birth: '2010-07',
},
],
name: {
pasts: ['Mocha', 'LiveScript'],
current: 'JavaScript',
},
author: 'Brendan Eich',
birth: '1995-12-4',
extension: '.js',
paradigm: ['script', 'object', 'functional'],
};
if (js.version && js.version.length > 0) {
return js.version[0].name;
}'WEB > JavaScript' 카테고리의 다른 글
| [week4] JS (0) | 2022.07.25 |
|---|---|
| [클린코드 For JS] 8. 함수 다루기 (0) | 2022.06.30 |
| [클린코드 For JS] 6. 배열 (0) | 2022.06.29 |
| [클린코드 For JS] 5. 분기 (0) | 2022.06.28 |
| [클린코드 For JS] 4. 경계 (0) | 2022.06.27 |