variable, operator, function

sorikiki·2022년 2월 7일
0

본 포스팅은 유튜브 드림코딩의 강좌를 수강한 후 필자가 홀로 복습한 내용과 함께 재구성하여 작성되었다.


프로그래밍의 가장 기본을 이루는 변수에 대해 알아볼 것이다. 변수의 이름 앞에 붙는 keyword에 따라 변수가 가리키는 값 혹은 객체가 mutable한지 immutable한지를 결정한다. javascript 언어의 가장 중요한 특징 중 하나는 dynamic type language라는 점이다. javascript의 variable에 대해 정리하기 전에 모던한 javascript 코드 작성을 위해 use strict을 왜 쓰면 좋은 지에 대해 간단하게 살펴보자.


'use strict'를 써야하는 이유

자세한 설명은 MDN을 참조하면 될 듯하다.
보통은 script 전체를 strict mode로 사용하는 것이 바람직하기 때문에, js 파일 최상단에 'use strict'을 붙여주면 된다.

여기서는 초심자가 이해할 수 있는 수준의 일부의 내용만 요약 정리하고자 한다.

  • 변수가 선언되지 않은 채 어떤 값에 할당되는 것을 막아준다.
  • 할당이 불가능한 변수에 값이 할당되는 것을 막아준다.
  • 함수를 작성할 때 parameter의 이름이 중복되는 것을 막아준다.
'use strict';

// No global variable declaration
x = 'dasol';  // ReferenceError

// Assignment to a non-writable property 
let undefined = 2; // TypeError
let Infinity = 3; // TypeError
let obj1 = {};
Object.defineProperty(obj1, 'x', { value: 42, writable: false });
obj1.x = 9; // TypeError

// Assignment to a getter-only property
var obj2 = { get x() { return 17; } };
obj2.x = 5; // TypeError

// Assignment to a new property on a non-extensible object
var fixed = {};
Object.preventExtensions(fixed);
fixed.newProp = 'ohai'; // TypeError

function sum(a, a, c) { // !! SyntaxError
  'use strict';
  return a + a + c; // wrong if this code ran
}



var가 아닌 let을 사용하자 ❗

let 키워드는 ES5에 도입되어 var 대신에 mutable varaible type 표지를 나타낼 수 있다. 기존에 var가 가지고 있던 치명적인 단점은 1. scope variable임에도 불구하고 scope 바깥에서 변수를 읽을 수 있다는 점이고 2. 변수에 대한 declaration을 인터프리터가 스크립트의 상단으로 hosting하여 런타임에러가 발생하지 않는다는 점이다. 따라서 자바스크립트 개발자는 var가 아닌 let을 사용할 것을 적극 권장된다.

let globalName = 'global name';
{
  let name = 'dasol';
  console.log(name);
  name = 'hello';
  console.log(name);
  console.log(globalName);
}
console.log(name); // ReferenceError
console.log(globalName);

// var (don't ever use this!)
// var hoisting (move declaration from bottom to top)
// has no block scope
{
  age = 4;
  var age;
}
console.log(age); // 4

📃 var hosting : JavaScript Hoisting refers to the process whereby the interpreter appears to move the declaration of functions, variables or classes to the top of their scope, prior to execution of the code. (MDN)



변경이 불가능하게 하는 CONST

let과 달리 read만 가능하게 하는 const가 있다. 되도록이면 const 타입의 변수를 자주 사용하는 것이 바람직하다.

// 3. Constant, r(read only)
// use const whenever possible.
// only use let if variable needs to change.
const daysInWeek = 7;
const maxNumber = 5;

// 데이터 자체의 타입의 mutability(변수의 mutability를 의미하는 것 x)
// * Immutable data types: primitive types, frozen objects (i.e. Object.freeze(<Object instance>))
// * Mutable data types: all objects by default are mutable in JS
// favor immutable data type always for a few reasons:
//  - security
//  - thread safety
//  - reduce human mistakes



Javascript: Dynamically Typed Language 🥲

javascript에서 변수를 선언할 때에는 변수의 타입을 지정해주지 않아도 된다. 이는 곧 변수의 타입이 런타임 과정에서 변화할 수 있다는 것을 의미한다.(dynamically typed language) 또한 다른 프로그래밍 언어와 달리, 숫자형에 대한 데이터 타입이 오직 number로 고정되어 있다. 아래 프로그램 코드가 보듯이 text의 데이터 타입이 string -> number -> string -> number로 다이나믹하게 변경됨을 알 수 있다. 이는 장점이기도 하지만 프로그램 실행 도중 런타임에러를 발생시킬 수 있다는 점에서 단점이기도 하다. 이러한 특성을 보완하고자 만들어진 TypeScript을 공부해야하는 이유이다!

let text = 'hello';
console.log(`value: ${text}, type: ${typeof text}`); 
// value: hello, type: string

text = 1;
console.log(`value: ${text}, type: ${typeof text}`);
// value: 1, type: number

text = '7' + 5;
console.log(`value: ${text}, type: ${typeof text}`); 
// value: 75, type: string

text = '8' / '2';
console.log(`value: ${text}, type: ${typeof text}`);
// value: 4, type: number

console.log(text.charAt(0)); 
// TypeError: text의 type이 number로 변경되었으니 string method 사용 불가



Data Types

primitive type에는 대표적으로 number, string, boolean, null, undefined가 있다. 변수에 값을 할당하지 않으면 undefined 타입이며 null의 경우에는 직접 할당을 해서 명시를 해줘야 하고 이는 변수가 가리키는 객체가 존재하지 않음을 프로그래밍적으로 의미한다.

primitive type과 object type을 비교하자면, primitive type은 값 자체를 메모리에 저장하고 object type은 실제 객체를 가리키는 레퍼런스를 메모리에 저장한다. 조금 더 깊게 들어가보면, const인 object type의 변수를 선언하고 어떤 레퍼런스를 할당한 후 다른 레퍼런스로 할당하는 것은 불가능하다. 그러나, 레퍼런스가 가리키는 객체의 프로퍼티 값을 변경하는 것은 가능하다.

// primitive: single item ex. number, string, boolean, null, undefined, symbol
// object: box container
// function : first-class function

// number
const count = 17; // integer
const size = 17.1; // decimal number

console.log(`value: ${count}, type: ${typeof count}`);
// value: 17, type: number
console.log(`value: ${size}, type: ${typeof size}`);
// value: 17.1, type: number

// number - special numeric values: infinity, -infinity, NaN
const infinity = 1 / 0;
const negativeInfinity = -1 / 0;
const nAn = 'not a number' / 2;
console.log(infinity); // Infinity
console.log(negativeInfinity); // -Infinity
console.log(nAn); // NaN

// bigInt (fairly new, don't use it yet)
// just attach 'n' to a series of numbers
const bigInt = 1234567890123456789012345678901234567890n; // over (-2**53) ~ 2*53)
console.log(`value: ${bigInt}, type: ${typeof bigInt}`);
// value: 1234567890123456789012345678901234567890, type: bigint

// string
const char = 'c';
const brendan = 'brendan';
const greeting = 'hello ' + brendan;

console.log(`value: ${greeting}, type: ${typeof greeting}`);
// value: hello brendan, type: string

const helloBob = `hi ${brendan}!`; //template literals (string)

console.log(`value: ${helloBob}, type: ${typeof helloBob}`);
// value: hi brendan, type: string

console.log('value: ' + helloBob + ' type: ' + typeof helloBob);
// same with the result above, but complicating

// boolean
// false: 0, null, undefined, NaN, ''
// true: any other value
const canRead = true;
const test = 3 < 1; // false

console.log(`value: ${canRead}, type: ${typeof canRead}`);
// value: true, type: boolean

console.log(`value: ${test}, type: ${typeof test}`);
// value: false, type: boolean

// null
let nothing = null;

console.log(`value: ${nothing}, type: ${typeof nothing}`);
// value: null, type: object


// undefined
let x;

console.log(`value: ${x}, type: ${typeof x}`);
// value: undefined, type: undefined

// symbol, create unique identifiers for objects
const symbol1 = Symbol('id');
const symbol2 = Symbol('id');
console.log(symbol1 === symbol2); // false
const gSymbol1 = Symbol.for('id');
const gSymbol2 = Symbol.for('id');
console.log(gSymbol1 === gSymbol2); // true
console.log(`value: ${symbol1.description}, type: ${typeof symbol1}`);
// value: id, type: symbol 

// object, real-life object, data structure
const dasol = { name: 'dasol', age: 25 };
dasol.age = 25; 
Object.freeze(dasol);
dasol.age = 26; // TypeError

📃 First-class Function: A programming language is said to have First-class functions when functions in that language are treated like any other variable. For example, in such a language, a function can be passed as an argument to other functions, can be returned by another function and can be assigned as a value to a variable.



Operator

operator에 대한 설명은 상대적으로 이해하기 쉽고, 다른 프로그래밍 언어와 겹치는 부분이 많아 자세한 설명은 생략하고 유튜브로 공부한 예시 코드만 첨부하도록 하겠다.

// 1. String concatenation
console.log('my' + ' cat'); 
console.log('1' + 2); 
console.log(`template literals: 1 + 2 = ${1 + 2}`); 

// 2. Numeric operators
console.log(1 + 1); // add
console.log(1 - 1); // substract
console.log(1 / 1); // divide
console.log(1 * 1); // multiply
console.log(5 % 2); // remainder
console.log(2 ** 3); // exponentiation

// 3. Increment and decrement operators
let counter = 2;
const preIncrement = ++counter;
// counter = counter + 1;
// preIncrement = counter;
console.log(`preIncrement: ${preIncrement}, counter: ${counter}`);
const postIncrement = counter++;
// postIncrement = counter;
// counter = counter + 1;
console.log(`postIncrement: ${postIncrement}, counter: ${counter}`);
const preDecrement = --counter;
console.log(`preDecrement: ${preDecrement}, counter: ${counter}`);
const postDecrement = counter--;
console.log(`postDecrement: ${postDecrement}, counter: ${counter}`);

// 4. Assignment operators
let x = 3;
let y = 6;
x += y; // x = x + y;
x -= y;
x *= y;
x /= y;

// 5. Comparison operators
console.log(10 < 6); // less than
console.log(10 <= 6); // less than or equal
console.log(10 > 6); // greater than
console.log(10 >= 6); // greater than or equal

// 6. Logical operators: || (or), && (and), ! (not)
const value1 = true;
const value2 = 4 < 2;

// || (or), finds the first truthy value
console.log(`or: ${value1 || value2 || check()}`);

// && (and), finds the first falsy value
console.log(`and: ${value1 && value2 && check()}`);

// &&: often used to compress long if-statement
// null인 객체에 대해서는 field를 읽어올 수 없으니, nullableObject가 true인지 false인지 검사한다. 
// nullableObject && nullableObject.something

function check() { // 많은 연산을 수행해야 하는 함수를 가장 뒤에 배치
  for (let i = 0; i < 10; i++) {
    // wasting time
    console.log('😱');
  }
  return true;
}

// ! (not)
console.log(!value1);

// 7. Equality
const stringFive = '5';
const numberFive = 5;

// == loose equality, with type conversion
console.log(stringFive == numberFive); // true
console.log(stringFive != numberFive); // false

// === strict equality, no type conversion
console.log(stringFive === numberFive); // false
console.log(stringFive !== numberFive); // true

// object equality by reference
const dasol1 = { name: 'dasol' };
const dasol2 = { name: 'dasol' };
const dasol3 = dasol1;
console.log(dasol1 == dasol2); // false
console.log(dasol1 === dasol2); // false
console.log(dasol1 == dasol3); // true
console.log(dasol1 === dasol3); // true

// equality - puzzler
console.log(0 == false); // true
console.log(0 === false); // false
console.log('' == false); // true
console.log('' === false); // false
console.log(null == undefined); // true
console.log(null === undefined); // false

// 8. Conditional operators: if
// if, else if, else
const name = 'df';
if (name === 'ellie') {
  console.log('Welcome, Ellie!');
} else if (name === 'coder') {
  console.log('You are amazing coder');
} else {
  console.log('unkwnon');
}

// 9. Ternary operator: ?
// condition ? value1 : value2;
console.log(name === 'ellie' ? 'yes' : 'no');

// 10. Switch statement
// use for multiple if checks
// use for enum-like value check
// use for multiple type checks in TS
const browser = 'IE';
switch (browser) {
  case 'IE':
    console.log('go away!');
    break;
  case 'Chrome':
  case 'Firefox':
    console.log('love you!');
    break;
  default:
    console.log('same all!');
    break;
}

// 11. Loops
// while loop, while the condition is truthy,
// body code is executed.
let i = 3;
while (i > 0) {
  console.log(`while: ${i}`);
  i--;
}

// do while loop, body code is executed first,
// then check the condition.
do {
  console.log(`do while: ${i}`);
  i--;
} while (i > 0);

// for loop, for(begin; condition; step)
for (i = 3; i > 0; i--) {
  console.log(`for: ${i}`);
}

for (let i = 3; i > 0; i = i - 2) {
  // inline variable declaration
  console.log(`inline variable for: ${i}`);
}

// nested loops
for (let i = 0; i < 10; i++) {
  for (let j = 0; j < 10; j++) {
    console.log(`i: ${i}, j:${j}`);
  }
}

// break, continue
// Q1. iterate from 0 to 10 and print only even numbers (use continue)
for (let i = 0; i < 11; i++) {
  if (i % 2 === 0) {
    continue;
  }
  console.log(`q1. ${i}`);
}

// Q2. iterate from 0 to 10 and print numbers until reaching 8 (use break)
for (let i = 0; i < 11; i++) {
  if (i > 8) {
    break;
  }
  console.log(`q2. ${i}`);
}



function

자바스크립트에서 함수는 first-class function으로 object의 일종이라고 할 수 있다. 이는 곧 어떤 변수에 할당될 수 있고, 함수의 매개변수 자리에 들어갈 수 있고, 함수의 반환값으로 사용될 수 있음을 의미한다.

📃 About Javascript : JavaScript (often shortened to JS) is a lightweight, interpreted, object-oriented language with first-class functions, and is best known as the scripting language for Web pages, but it's used in many non-browser environments as well. It is a prototype-based, multi-paradigm scripting language that is dynamic, and supports object-oriented, imperative, and functional programming styles.

이를 바탕으로 자바스크립트에서 함수가 쓰이는 패턴들에 대해 알아보도록 하자. 자바스크립트에서 함수는 일반적인 function declaration 외에도 first-class functions의 특성을 보여주는 function expression, 여기서 더욱 간결하게 만든 arrow function으로 표현될 수 있다.

// 1. Function declaration
// function name(param1, param2) { body... return; }
// one function === one thing
// naming: doSomething, command, verb
// e.g. createCardAndPoint -> createCard, createPoint

function printHello() {
  console.log('Hello');
}
printHello();

function log(message) {
  console.log(message);
}
log('Hello@');
log(1234);

// 2. Parameters
// primitive parameters: passed by value
// object parameters: passed by reference
function changeName(obj) {
  obj.name = 'coder';
}
const dasol = { name: 'dasol' };
changeName(dasol); 
console.log(dasol); // dasol

// 3. Default parameters (added in ES6)
function showMessage(message, from = 'unknown') {
  console.log(`${message} by ${from}`);
}
showMessage('Hi!');

// 4. Rest parameters (added in ES6)
// 정해지지 않은 수의 인자를 받아 이들 원소를 하나씩 도는 방법 3가지
function printAll(...args) {
  for (let i = 0; i < args.length; i++) {
    console.log(args[i]);
  }

  for (const arg of args) {
    console.log(arg);
  }

  args.forEach((arg) => console.log(arg));
}
printAll('dream', 'coding', 'ellie');

// 5. Local scope
// 함수의 body 역시 local scope로 간주한다.
// 안에서는 밖을 볼 수 있지만, 밖에서는 안을 볼 수 없음을 기억!
let globalMessage = 'global'; // global variable
function printMessage() {
  let message = 'hello';
  console.log(message); // local variable
  console.log(globalMessage);
  function printAnother() {
    console.log(message);
    let childMessage = 'hello';
  }
  // console.log(childMessage); //error
}
printMessage();

// 6. Return a value
// 순수 javascript에서는 함수의 매개변수의 타입과 리턴 타입을 명시하지 않는다. typescript를 배워야하는 이유! 
function sum(a, b) {
  return a + b;
}
const result = sum(1, 2); // 3
console.log(`sum: ${sum(1, 2)}`);

// 7. Early return, early exit
// bad
function upgradeUser(user) {
  if (user.point > 10) {
    // long upgrade logic...
  }
}

// good
function upgradeUser(user) {
  if (user.point <= 10) {
    return;
  }
  // long upgrade logic...
}

// 자바스크립트의 함수가 first-class functions임을 remind하면서 ... 

// ✨ Function expression
// a function declaration can be called earlier than it is defined. (hoisted). 이전 part에서 배운 var hoisting과 유사.
// a function expression은 var 타입의 변수에 할당하더라도 hoisting 불가. 즉 아래 print() 함수를 function expression statement보다 이전에 호출할 수 없다는 의미.
const print = function () {
  // anonymous function
  console.log('print');
};
print();
const printAgain = print;
printAgain();
const sumAgain = sum;
console.log(sumAgain(1, 3));
// 다른 변수가 동일한 함수 객체를 가리키는 레퍼런스를 가리킬 수도 있음.

// ✨ Callback function using function expression
function randomQuiz(answer, printYes, printNo) {
  if (answer === 'love you') {
    printYes();
  } else {
    printNo();
  }
}
// anonymous function
const printYes = function () {
  console.log('yes!');
};

// named function
// better debugging in debugger's stack traces
// recursions
const printNo = function print() {
  console.log('no!');
};
randomQuiz('wrong', printYes, printNo);
randomQuiz('love you', printYes, printNo);

// ✨ Arrow function
// always anonymous
// const simplePrint = function () {
//   console.log('simplePrint!');
// };

const simplePrint = () => console.log('simplePrint!');
const add = (a, b) => a + b;
const simpleMultiply = (a, b) => {
  // do something more
  return a * b;
};

// IIFE: Immediately Invoked Function Expression
(function hello() {
  console.log('IIFE');
})();

0개의 댓글