티스토리 뷰

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

 

출처😌

클린코드 자바스크립트 | Udemy

자료🙄

Git: pocojang/clean-code-js (github.com)


목차

1. 과정 소개 
2. 변수 다루기 
3. 타입 다루기
4. 경계 다루기
5. 분기 다루기
6. 배열 다루기

7. 객체 다루기🚩

    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

8. 함수 다루기


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
댓글
다크모드
Document description javascript psychology
더보기 ,제목1 태그 호버