0w0

Js문서를 읽으며 느낀 비기

image/iframes

loading = "lazy"

font

<link rel="preload" />

array 얼리기

Object.freeze(array);

중복 제거

Array(...new Set(array));

??

값이 null이나 undefined이면 어떤 값을 보여준다

// 예시)

let foo;

// foo는 undefined 상태

foo ?? '아직 정의가 안됨';

?.

객체가 유효한지 확인하는 것, 없다면 undefined를 보여준다.

// 예시

const akazaAkari = {
  age: 13,

  grade: 'middleSchool 1年',

  role: {
    main: 'air',

    sub: '착한아이',
  },
};

Object.freeze(akazaAkari);

akazaAkari?.role;

// {"main": "air", "sub": "착한아이"}

akazaAkari?.role?.주인공;

// undefined

akazaAkari?.role?.주인공 ?? '아카자 아카리는 주인공이에요! (●` 3 ´●)';

// "아카자 아카리는 주인공이에요! (●` 3 ´●)"

||

// 값이 <falsy> 라면

falsy === null, NaN, 0, -0, '', '', ``, undefined;

let chinaKare;

chinaKare || console.log('치나카레 월드가 열린다아ㅏ');

// "치나카레 월드가 열린다아ㅏ"

const ronaldo = '호우!';

ronaldo || console.log('호ㅗㅗㅗㅗㅗㅗㅗㅗㅗㅗ우ㅜㅜㅜㅜㅜㅜㅜㅜㅜ');

// "호우!"

팩토리 함수

제네레이터 쓸 때는 팩토리 함수 생성자

// 일반적인 팩토리 함수
// 외부는 팩토리
// 내부는 제네레이터
function factory(factory_parameter) {
  // 최초의 제네레이터 상태 설정
  return function generator(generator_parameter) {
    // 상태 갱신 작업들
    return value;
  };
}
// es6 제네레이터
function* counter() {
  let count = 0;
  while (true) {
    count += 1;
    yield count;
  }
}

const gen = counter();

gen.next().value; // 1
gen.next().value; // 2
gen.next().value; // 3

// 값을 가진, value 속성을 가진, 객체를 반환하는 next 메서드를 가진, 객체를 반환하는 함수

// vs

// 값을 반환하는 함수

// 직접 만드는 제네레이터

function counter() {
  let count = 0;
  return function counter_generator() {
    count += 1;
    return count;
  };
}

const gen = counter();

gen(); // 1
gen(); // 2
gen(); // 3

생성자

그리고 은닉

function counter_constructor() {
  let counter = 0;

  function up() {
    counter += 1;
    return counter;
  }

  function down() {
    counter -= 1;
    return counter;
  }

  return Object.freeze({
    up,
    down,
  });
}

a = new counter_constructor();

a.up();

a.up();

a.up();

a.up();

a.up();

a.up();

a.down();

명명시 사용할 수 있는 것들

$

\_

소문자

대문자

알파벳(숫자)

한글, 일어 등등

하지만 왠만하면 $, \_, 숫자, 한글, 일어 같은 것은 쓰지 말자.

특히 $, \_, 숫자는 더더욱.

출처 : 자바스크립트는 왜 그모양일까? - 더글라스 크록포드

특정 숫자만큼 배열채우기.

Array.from(Array(10).keys())

[...Array(10).keys()]

Array.from({length: 10}, (\_, i) => i + 1)

출처 : https://stackoverflow.com/questions/3746725/how-to-create-an-array-containing-1-n

콜백함수

function 고계함수(콜백함수) {
  콜백함수();
}

메서드함수

const obj = {
  method1: function () {},
  method2: () => {},
};

더 편한 메서드함수(es2015)

const obj = {
  method1() {
    return 'this is method1';
  },

  method2() {
    return 'this is method2';
  },
};

깊은 복사

JS는 기본 얕은 복사일 경우가 많다.

얕은 복사1

// 얕은 복사
const obj = {
  level: 1,
  nest: {
    level: 2,
  },
};

const shallowClone = obj;

얕은 복사2

const obj = {
  level: 1,
  nest: {
    level: 2,
  },
};

const shallowClone = (obj) => {
  return Object.assign({}, obj);
};

const cloneObj = shallowClone(obj);

obj.nest.hp = '100';
console.log(obj.nest === cloneObj.nest); // true

일반적인 깊은 복사

...

const obj = {
  level: 1,
  nest: {
    level: 2,
  },
};

const isDeepCloneObj = { ...obj };

obj.nest.hp = '100';
console.log(obj.nest === isDeepCloneObj.nest); // true

Object.assign

const obj = {
  level: 1,
  nest: {
    level: 2,
  },
};

const shallowClone = (obj) => {
  return Object.assign({}, obj);
};

const isDeepCloneObj = shallowClone(obj);
obj.nest.hp = '100';
console.log(obj.nest === isDeepCloneObj.nest); // true

진짜 깊은 복사

재귀

const shallowClone = (obj) => {
  return Object.assign({}, obj);
};
// obj 깊은 복사해서 오브젝트를 반환

function deepClone(obj) {
  const newObj = shallowClone(obj);

  // 프로퍼티가 오브젝트이므로, 재귀적으로 복제
  Object.keys(newObj)
    .filter((k) => typeof newObj[k] === 'object')
    .forEach((k) => (newObj[k] = deepClone(newObj[k])));

  return newObj;
}

const obj = {
  level: 1,
  nest: {
    level: 2,
  },
};

const cloneObj = deepClone(obj);

// `nest` 오브젝트도 재귀적으로 복제된다.
obj.nest.hp = '100';
console.log(obj.nest === cloneObj.nest); // => false oh :)
console.log(cloneObj.nest.hp); // => undefined oh :)

JSON

const obj = {
  level: 1,
  nest: {
    level: 2,
  },
};

const isDeepCloneObj = JSON.parse(JSON.stringify(obj));

obj.nest.hp = '100';
console.log(obj.nest === isDeepCloneObj.nest); // => false oh! :)
console.log(isDeepCloneObj.nest.hp); // undefined oh :)

깊은 동결

function deepFreeze(obj) {
  if (typeof obj === 'object' && !Object.isFrozen(obj)) {
    Object.keys(obj).forEach((x) => deepFreeze(obj[x]));
    Object.freeze(obj);
  }

  return obj;
}

const obj = {
  1: '!',

  2: '@',

  3: {
    4: '$',

    5: '^',
  },
};

const obj2 = {
  1: '!',

  2: '@',

  3: {
    4: '$',

    5: '^',
  },
};

deepFreeze(obj);

Object.isFrozen(obj); // true

Object.isFrozen(obj[3]); // true

Object.freeze(obj2);

Object.isFrozen(obj2); // true

Object.isFrozen(obj2[3]); // false

스코프

const z = 'z';

{
  const x = 'x';

  {
    const y = 'y';

    console.log(x); // x
    console.log(y); // y
    console.log(z); // z
  }
  console.log(x); // x
  console.log(y); // y is not defined
  console.log(z); // z
}

console.log(x); // x is not defined
console.log(y); // y is not defined
console.log(z); // z

호이스팅

함수, var, let호이스팅이 된다.

정적 스코프

const x = 10; // *1

function printX() {
  // x는 이미 *1 변수 x 참조

  console.log(x); // => 10
}

function run() {
  const x = 20; // *2

  printX(); // 10이 출력된다.
}

run();
  1. printx 함수 스코프에 변수 x 정의되어 있지 않다

  2. 글로벌 스코프를 확인한다.

  3. 스코프에 const x = 10 정의되어 있으므로, 식별자 x는 이 변수를 참조한다.

printx 함수 안에 쓰인 x 식별자는, run 함수 실행과 무관하게, 정적으로 *1으로 정의된 변수 x를 참조한다.

이렇게 어떤 식별자가 어떤 변수를 참조하는가를 정적으로 결정되는 성질을, 정적 스코프라 한다.

그럼 동적 스코프는?

// 유사 동적 스코프 코드 예시(JS에는 없다)

var x = 10;

fn printX() {

    // 동적 스코프 언어라면, 식별자 x는 호출의 장소에 따라 변수 x 참조가 달라진다.

    // 콘솔출력

    print(x);

}

fn run() {

    // 호출 장소 스코프에는 변수 x가 따로 정의 됨

    var x = 20;

    printX();

}

printX(); // 10

run(); // 20

메모리 관리는 가비지 컬렉터로 한다.

function printX() {

    const x = "X";

    console.log(x); // => "X"

}

printX();

// 이 시점에서 X를 참조하는 것들은 사라진다. === 해방된다.

function createArray() {

    const tempArray = [1, 2, 3];

    return tempArray;

}

const array = createArray();

console.log(array); // => [1, 2, 3]

// 변수 array가 [1, 2, 3] 값을 참조하고 있으므로 === 아직 해방되지 않음

함수선언

function fn() {}

함수식

const fn = function () {};

분할 대입

function printUserId(user) {
  console.log(user.id); // => 42
}

const user = {
  id: 42,
};

printUserId(user);
function printUserId({ id }) {
  console.log(id); // => 42
}

const user = {
  id: 42,
};

printUserId(user);
const user = {
  id: 42,
};

const { id } = user;

console.log(id); // => 42

function printUserId({ id }) {
  console.log(id); // => 42
}

printUserId(user);
function print([first, second]) {
  console.log(first); // => 1

  console.log(second); // => 2
}

const array = [1, 2];

print(array);

출처 : https://jsprimer.net/basic/function-declaration/

함수에 인수를 줄 때 (구조 분해 할당 - 객체 구조 분해)

const a = { a: 1, b: 2, c: 3, d: 4, e: 5 };
const b = [1, 2];

function normal(item) {
  console.log(item);
}
normal(a); // { "a": 1, "b": 2, "c": 3, "d": 4, "e": 5 }

function wonder({ item }) {
  console.log(item);
}
wonder(a); // undefined
wonder({ a }); // undefined

function wonder2({ a }) {
  console.log(a);
}
wonder2(a); // 1
wonder2({ a }); // { "a": 1, "b": 2, "c": 3, "d": 4, "e": 5 }

wonder2(b); // undefined
wonder2({ b }); // undefined

인수를 {}로 준다면 이름이 동일한 객체를 넣어야만 작동한다. 왜 그럴까?

추후조사

그래서 react-native FlatList 예시

<FlatList
  data={DATA}
  renderItem={renderItem}
  keyExtractor={(item) => item.id}
/>

에서 renderItem를 이용할 때 꼭 item을 이용해야된다.

renderItem({ item, index, separators });

renderItem은 이런 구조니까 {}를 이해하려면 반대로

const DATA = [
  {
    id: 'bd7acbea-c1b1-46c2-aed5-3ad53abb28ba',
    title: 'First Item',
  },
  {
    id: '3ac68afc-c605-48d3-a4f8-fbd91aa97f63',
    title: 'Second Item',
  },
  {
    id: '58694a0f-3da1-471f-bd96-145571e29d72',
    title: 'Third Item',
  },
];

// 여기를 시작으로 보면
<FlatList
  data={DATA} // 상기한 DATA
  renderItem={renderItem} // 이하 설명
  keyExtractor={(item) => item.id} // 여기는 뭐든 상관없음 평범한 함수라서 item에서 e를 넣든 x를 넣든
/>;

const renderItem = ({ item }) => <Item title={item.title} />;
// 여기는 반드시 { item }을 써야함
// FlatList의 renderItem에서 item으로 지정되어있음
// 그 외에도 index, separators 지정되어있음
// 다른거 넣어도 undefined 만 나옴

const Item = ({ title }) => (
  <View style={styles.item}>
    <Text style={styles.title}>{title}</Text>
  </View>
);
// 여기 { title }은 DATA.title. 즉

const Item = (DATA) => (
  <View style={styles.item}>
    <Text style={styles.title}>{DATA.title}</Text>
  </View>
);

그래서 {}를 이용할 때는 특정한 함수를 대상으로하는 함수를 만들 때 유용한 듯하다. 다른 것을 넣어도 undefined만 나오니

MDN에서 주는 예시에서도 그런듯한 느낌을 받는다.

// 오직 user.id만을 받기 위한 함수이다.
// 물론 아래와 같이 객체 안에 id만 있으면 멀쩡히 돌아간다만
// var sikaku = {id: "zzzz"}
// userId(sikaku) // "zzzz"

function userId({ id }) {
  return id;
}

function whois({ displayName: displayName, fullName: { firstName: name } }) {
  console.log(displayName + ' is ' + name);
}

var user = {
  id: 42,
  displayName: 'jdoe',
  fullName: {
    firstName: 'John',
    lastName: 'Doe',
  },
};

console.log('userId: ' + userId(user)); // "userId: 42"
whois(user); // "jdoe is John"

좋은 것만 사용하기 위한 방법

JS의 좋은 부분만 쓰면 좋은 프로그램을 만들 수 있다. 사용하지 말아야 할 기능들에 유의하자

사용하지 말아야 할 기능들

자바스크립트는 왜 그모양일까?

함수명

함수는 동작이다. 함수 하나에 한 가지 동작을 한다는 마음으로 명명을 동사로 하자. 또한 주저리주저리 주석을 다는 것보다, 함수명만 보아도 이것이 무엇인지 알아차리게 하는 것이 더 좋다.

배열로 할 수 있는 것

스택

const arr = [1, 2, 3, 4];

// LI
arr.push(5); // [1, 2, 3, 4, 5]

// FO
arr.pop(); // [1, 2, 3, 4]

const arr = [1, 2, 3, 4];

// FI
arr.unshift(0); // [0, 1, 2, 3, 4]

// FO
arr.pop(); // [0, 1, 2, 3]

기능 표기
꼬리 추가 push
선두 추가 unshift
꼬리 제거 pop
선두 제거 shift
꼬리 조사 obj[obj.length-1]
선두 조사 obj[0]

출처

MDN, jsprimer, 자바스크립트는 왜 그모양일까?, javascript.info, 경험