카카오 클라우드 스쿨 1기 개발자 과정 4주차 - 3

EUNLEE·2022년 7월 6일
0

2022.07.06 수요일

🐠 객체

객체 복습

  • 객체 literal ⇒ { key: value, key: value, … } 형태
  • key: value를 property라고 한다.
    • key는 property key라고 한다.
    • value는 property value라고 한다.
  • property가 없는 객체도 있다.(객체의 정의: 0개 이상의 property로 이루어진 집합)

📍 JavaScript에서 객체를 생성하는 방법
1. 객체 literal ⇒ (지난 시간에 살펴보았다!)
2. Object 생성자 함수
3. 사용자 정의 생성자 함수
4. Object.create() method
5. ‘ES6’ → class

💡 “함수”와 “생성자 함수”는 다른 것!


객체를 사용하는 방법

  1. . dot notation(점 표기법)
  2. [ ] bracket notation(괄호 표기법)

💡 만약 property key가 JavaScript의 identifier naimg rule을 따르면 객체 literal을 이용할 때 ‘ ‘ (quotation)를 생략해도 된다.

var obj = {
    name: '홍길동', // key가 식별자 명명 규칙에 맞다면 quotation으로 감싸지 않아도 된다. 'name'(o), name(o)
    "!myPhone": '010-1234-5678', // key가 식별자 명명 규칙에 맞지않다면 quotation으로 감싸주어야한다.
    10: 300 // key를 숫자(10)으로 써도 자동 casting해서 string('10')으로 인식한다.
};

간단한 객체 literal

  • name과 같은 property key는 이미 내부적으로 사용하고 있을 가능성이 높기 때문에 사용하지 않는 것이 좋다.
    • (ex) name → userName

📍 property 축약형

아래 예시를 통해 method와 property 구분하기

var age = 20;
var obj = {
    userName: '홍길동',
    getName: function(){ // 통칭 method라고 부르지만 진짜 method는 아니다. "property"일뿐.
        console.log("this is a property not a method");
    },
    setName(){ // (ES6)JavaScript에서 말하는 "method"는 이렇게 축약표현으로 나타낸 것을 말한다.
        console.log("this is a method")
    },
    age // age: age 라고 쓰는 것과 같다. 축약표현이 가능하다.
    // key는 "age"가 되고, value는 변수 age의 값이 된다.
}

console.log(obj);
/* 출력 결과
{
    userName: '홍길동',
    getName: [Function: getName],
    setName: [Function: setName],
    age: 20
  }
 */

Primitive value vs Reference(Object)

  • Primitive value → immutable
  • Reference → mutable

💡 객체를 mutable로 지정해놓은 이유
메모리 효율성을 위해서!!
만약 객체가 immutable이라면 메모리에 garbage collection이 계속해서 발생하기 때문이다.

🐠 함수

함수는 객체다.

  • parameter: 매개변수(formal parmeter), 함수 내에서 선언한 argument를 받아들이기 위한 역할을 하는 지역변수
  • argument: 실인자(actual parameter), 인수라고 한다. 함수를 호출할 때 실제로 사용하는 값

💡 간단하게 함수 선언 시 parameter(매개변수), 함수 호출 시 argument(인자)라고 기억해도 좋다.

function literal

💡 literal이란 자바스크립트 엔진에 의해서 하나의 값으로 떨어지는 것!!

/* 함수 literal */
function add(x,y) {
	 return x + y;
};

함수를 정의(definition)하는 방법

  1. 함수 표현식과 함수 선언문

    /* 함수 표현식 */
    var myAdd = function add(x,y) {
    	 return x + y;
    };
    
    /* 함수 선언문 */ // 함수 literal과 똑같이 생겼다.
    function add(x,y) {
    	 return x + y;
    };
    • 함수 선언문은 creation phase(런타임 이전)에 변수가 만들어지고 객체가 생성된다.
    • 함수 표현식은 execution phase(런타임)에 변수가 만들어지고 객체가 생성된다.
  2. Function( ) 생성자 함수

    var myFunc = new Function('x', 'y', 'return x+y');
    • var myFunc: 함수 객체
    • 'x', 'y': parameter
    • 'return x+y': 함수 실행 코드
  3. ‘ES6’ → 화살표함수(Arrow function)

    화살표 함수에서는 this를 사용할 수 없다.(화살표 함수는 non-constructor이기 때문이다. 관련 내용은 생성자 함수, 상속 부분에서 다룬다.)

    var add = (x, y) => x + y;

함수 호출(call)

→ 함수 내부에 arguments라는 유사 배열 객체가 있다.

IIFE(즉시 실행 함수)

→ 함수 선언과 동시에 호출(전역변수의 사용을 지양)

중첩함수

JavaScript는 함수 안쪽에 또 다른 함수를 선언하는 중첩(nested) 함수가 가능하다.

Callback 함수

  • callback함수를 가져다가 사용하는 함수를 고차함수(Higher-ordered function)이라고 한다.
  • Event 처리, AJAX를 처리할 때 많이 사용한다.
  • 함수가 일급객체이기 때문에 다른 함수의 인자로 넘어갈 수 있다. 따라서 콜백함수라는 매커니즘을 사용할 수 있는 것.

🐠 Scope

  • dynamic scope(동적 scope)
    • 사용된 위치에서 scope가 잡힘
  • static scope(정적 scope)lexical scope라고 많이 부른다.
    • 선언한 위치에서 scope가 잡힘
    • JavaScript를 포함한 대부분의 언어는 lexical scope를 따른다.

🐠 Property

Property attribute

  • 직접적인 접근이 불가능.
  • 자바스크립트 내부의 내부 slot으로 관리됨
    • 내부 slot과 내부 metod은 [[ ]] 대괄호 2개가 중첩된 형태로 표현된다.

💡 Property의 상세(Property attribute)
1. [[Value]] property의 값
2. [[Writable]] property의 값을 수정할 수 있는지 여부(key에 대한 value를 고정시킬지 말지)
3. [[Enumerable]] 해당 property가 열거될 수 있는지 여부
4. [[Configurable]] property attribute를 재정의 할 수 있는지 여부

객체의 property 변경

💡 JavaScript의 객체는 자유롭게 아래의 4가지가 가능하다.

  • Property의 추가
  • Property의 삭제
  • Property 값의 read
  • Property 값의 write
  1. 확장금지 Object.preventExtensions()
    • 객체에 새로운 property를 추가할 수 없다.
  2. 밀봉 Object.seal()
    • property의 추가와 삭제를 할 수 없다.
    • 확장금지 +delete
  3. 동결 Object.freeze()
    • property의 추가와 삭제, propety 값의 변경을 할 수 없다.
    • 밀봉 +writing

💡 use strict;
ES6에서 추가된 strict mode를 사용하기 위한 코드로 strict mode를 사용하면 JavaScript의 확장성은 떨어지지만 정형성은 올라간다.

/* 객체의 property 변경 */
'use strict'; // strict mode

const person = {
    name: 'Lee'
};
// 객체가 확장이 가능한지
console.log(Object.isExtensible(person)); //true

person.age = 20;
console.log(person); //{ name: 'Lee', age: 20 }

// 확장금지
Object.preventExtensions(person);
person.address = 'Seoul'; // 'use strict'; 코드를 상단에 추가하여 strict 모드를 사용해서 error를 발생시킬 수 있다.
console.log(person); //{ name: 'Lee', age: 20 }
// address가 새로운 property로 추가되지 않는다.

// 밀봉
Object.seal(person);
delete person.name;
console.log(person); //{ name: 'Lee', age: 20 }
// name property가 삭제되지 않는다.

// 동결
Object.freeze(person);
person.name = '아이유';
console.log(person); //{ name: 'Lee', age: 20 }
// name property의 값이 변경되지 않는다.

🐠 생성자 함수

💡 첫글자를 대문자로 쓰면 생성자 함수를 쓴다는 의미로 관용적으로 사용된다.
(소문자로 쓴다고 생성자 함수로 쓸 수 없는 것은 아니다.)
생성자 함수의 이름 식별자는 PascalCase(첫글자 대문자로 시작)

대표적인 생성자 함수(built-in)

  • Object()
  • String()
  • Number()
  • Boolean()
  • Function()
  • Array()

💡 JavaScript Engine이 코드를 실행하기 위해 기동하면
1. built-in 생성자 함수를 포함한 built-in 객체들을 생성한다.
2. 실행 환경에 맞는 global 객체를 생성한다.
- browser 환경 → window
- Node.js 환경 → global
(환경에 따라서 만들어지는 전역 객체가 다르다.)

function Person(){
    /* this */
    // this라는 keyword는 생성자 함수일 경우도 있고
    // 일반 함수인 경우에도 있다.
    // 생성자 함수에서의 this => 생성자 함수에 의해서 만들어질 instance를 가리키는 reference
    // 일반 함수에서의 this => window
    console.log(this);
}
Person();

browser 환경에서는 window라는 이름의 객체로 생성됨
Node.js 환경에서는 global이라는 이름의 객체로 생성됨

생성자 함수에 의한 객체 생성

  1. Object 생성자 함수를 이용한 객체 생성

    • new keyword를 이용해서 생성자 함수 호출 ⇒ instance(우리가 알고있는 일반적인 객체와 다름)
      • 생성자 함수에 의해 생성된 객체를 인스턴스(instance)라고 한다.
    /* 생성자 함수를 이용해서 객체 만들기 */
    const person1 = {};
    console.dir(person1);
    // 객체 literal을 이용한 객체 생성
    
    const person2 = new Object();
    console.dir(person2);
    // 생성자 함수를 이용한 객체 생성
    // => instance
  2. (User Define) 생성자 함수

    • 함수에 return 값이 없으면 undefined가 리턴된다.
    // 생성자 함수의 이름 식별자는 PascalCase(첫글자 대문자로 시작)
    function Person(){
    
    }
    
    // 일반 함수로 호출
    const person1 = Person();
    console.log(person1); //undefined (함수에서 return문이 없을 때)
    
    // 생성자 함수로 객체(instance) 만들기 -> new 키워드 사용
    const person2 = new Person();
    console.log(person2); //Person {} (빈 객체)
    // 생성자 함수는 기본적으로 return문을 쓰지 않는다.
    
    // 객체 literal로 객체 만들기
    var person3 = {};
    console.log(person3); //{}

    💡 브라우저로 확인해보면 생성자 함수로 객체(instance)를 생성했을 때와 객체 literal로 객체를 생성했을 때 내부적인 구조가 다름을 알 수 있다.

    💡 생성자 함수는 return 구문을 쓰지 않는다!!
    → 묵시적으로 생성된 instance reference인 this가 리턴된다.

    /* return값으로 객체를 넘겼을 때 */
    function Person(name){
        this.name = name;
        this.getName = function(){
            return `내 이름은 ${this.name}`;
        }
        return {}; // 생성자 함수로 사용할 경우는 return 구문이 있으면 안된다.
    		// 특히 객체를 return하게 되면 return값을 객체로 넘기게 되어 우리가 원하는 객체를 생성할 수 없다. -> Error
    }
    const person1 = new Person('아이유');
    const person2 = new Person('김연아');
    console.log(person1.getName()); //TypeError: person1.getName is not a function
    console.log(person2.getName());
    /* return값으로 primitive value를 넘겼을 때 */
    function Person(name){
        this.name = name;
        this.getName = function(){
            return `내 이름은 ${this.name}`;
        }
        return 100; // return값으로 primitive value가 들어오면 이 값을 무시하기 때문에 error가 발생하지 않는다.
    }
    const person1 = new Person('아이유');
    const person2 = new Person('김연아');
    console.log(person1.getName());
    console.log(person2.getName());

    관용적으로 생성자 함수는 return 구문을 쓰지않기로 약속🤗


this의 의미
1. 일반 함수 → window
2. 생성자 함수 → 생성될 instance
3. method에서 사용 → 현재 사용하는 객체
“함수는 그 자체로 객체”

    /* this의 의미 */
    function foo(){}
    
    // foo() 함수에 property 추가하기
    foo.myName = '홍길동';
    foo.getName = function(){
        console.log(this);
    }
    
    foo(); // 함수 호출
    new foo(); // 생성자 함수 호출
    foo.getName(); // method 호출 -> 여기서 this는 함수 객체 foo이다.

객체의 호출

  • 일반 객체는 호출(call, invoke)을 할 수 없다.
  • 함수 객체는 호출을 할 수 있다.
    • 이 기능을 위해 함수 객체는 내부 slot과 내부 method를 가지고 있다.
    • [[Call]], [[Constructor]] → 함수 객체에만 있는 내부 method
    • 함수를 호출하면 JavaScript Engind에 의해 실제적으로는 [[Call]]이라는 내부 method가 호출된다.
    • [[Call]]을 가지고 있어야만 호출 할 수 있다.
    • [[Call]]을 가지고 있는 객체를 callable 객체라고 한다. → 함수는 callable 객체
    • [[Constructor]]는 모든 함수 객체가 가지고 있지는 않다.
    • [[Constructor]]는 객체가 생성될 때 이용된다.
      - constructor : [[Constructor]]를 가지고 있음
      - 함수선언문
      - 함수표현식
      - class
    • non-constructor : [[Constructor]]를 가지고 있지 않음.
      - arrow function
      • ES6 함수 축약법(method)
    • 즉 모든 함수가 생성자 함수인 것은 아니다.
    /* 객체의 호출 */
    // 함수 선언문
    function foo() {}
    
    // 함수 표현식
    var bar = function(){};
    
    // 객체 literal로 만든 일반 객체
    const barx = {
        x: function(){} // 객체의 property로 함수가 할당
    }
    
    new foo(); // foo 함수 객체가 내부적으로 [[Constructor]] method를 가지고 있기 때문에 new 키워드로 instance를 만들어낼 수 있다.
    new bar(); // [[Constructor]]를 가지고 있다.
    new barx.x(); // [[Constructor]]를 가지고 있다.
    
    // arrow function
    const arrow = () => {}; // [[Constructor]]를 가지고 있지않다.
    new arrow(); //TypeError: arrow is not a constructor
    
    // ES6 함수 축약법 method
    const obj = {
        x(){} // [[Constructor]]를 가지고 있지않다.
    }
    new obj.x(); //TypeError: obj.x is not a constructor
    // 함수 선언문
    function add(x, y) {
        return x + y;
    }
    
    var inst = new add();
    console.log(inst); //add {}
    
    function createUser(name, role){
        return {name, role};
    }
    
    var inst = new createUser();
    console.log(inst); //{ name: undefined, role: undefined }
    
    // 생성자 함수
    function Circle(radius){
        this.radius = radius; // 여기서 가리키는 this는 window 
        // window 객체에 radius라는 property를 붙이는거임.
        this.getDiameter = function(){
            return 2 * this.radius;
        }
    }
    
    const circle = Circle(5); // 일반함수
    console.log(radius); //5 window 객체에 붙은 radius값
    console.log(circle); //undefined 함수에 return 구문이 없으니까

함수 객체의 property

일반 객체에 비해 더 가지고 있는 property

/* 일반 객체와 함수 객체의 차이 */
// 브라우저에서 확인하자
var obj = {
    name: '홍길동'
}

// 일반 객체
console.dir(obj); //Object

function square(number){
    return number * number;
}

// 함수 객체
console.dir(square); //f square(number)

  • arguments property
    • arguments는 property key
    • arguments 유사 배열 객체를 property value로 가진다.
      • 함수 내에서 지역변수처럼 사용된다.
  • caller property
    • 브라우저에는 등록되어있지만 ECMA 비표준이다. (표준화될 확률도 낮음)

    • 함수 자신을 호출한 함수에 대한 reference

      /* caller */
      function foo(f){
          return f();
      }
      
      function bar(){
          return 'caller: ' + bar.caller; // 여기서 쓴 bar는 함수 이름 bar를 지칭한다.
      }
      
      console.log(bar()); //caller: null 브라우저에서 확인
      
      console.log(foo(bar)); // foo의 caller가 호출된다.
      // 출력 결과
      // caller: function foo(f){
      //     return f();
      // }
  • length property
    • parameter의 개수
  • name property
    • 함수의 이름
  • ⭐️ prototype property
    • constructor만 가지고 있다.
    • 해당 함수(생성자 함수)가 생성하는 instance가 내부 slot [[Prototype]]로 참조하는 객체를 가리킨다.

다시 정리

JavaScript Engine이 시작하면

  1. built-in 객체 생성(Object, String..)
  2. 전역객체 생성(window)

모든 객체가 window의 property이므로 window.객체 라고 써야하지만 모~~든 객체에 window.이 붙으니까 생략하는 것.

let, const 키워드로 선언한 변수들은 전역 변수 scope를 가지지만 window 객체의 property로 붙지 않는다. 오직 var 키워드로 선언한 변수들만 window 객체의 property로 붙는다.

⭐ 모든 객체는 [[Prototype]] 내부 slot을 가지고 있다.
(상위) prototype (객체) → 객체 생성 방법에 따라 종류가 달라진다.

[[Prototype]] 을 사용해서 직접 접근이 불가능하므로 __Proto__ 접근자를 이용해서 접근할 수 있다.

// 생성자 함수로 사용할 목적으로 만들었어요!
// 함수 선언문
function Person(name){
    // 생성자 함수로 만들어질 instance가
    // 가지는 property를 설정
    this.name = name;
}

const person1 = new Person('홍길동');
const pereon2 = new Person('김길동');

💡 왜 이런 구조를 가질까???
Inheritance(상속)이라는 개념을 구현하기 위해서!! → 코드의 재활용을 높이기 위해
"JavaScript는 Prototype기반의 상속을 구현한다."

// 생성자 함수
function Circle(radius) {
    this.radius = radius;
    this.getDiameter = function(){
        return 2 * this.radius;
    }
}

const circle1 = new Circle(5);
const circle2 = new Circle(10);

console.log(circle1.getDiameter === circle2.getDiameter); //false
// instance마다 method를 각자 따로따로 가지므로 메모리가 낭비된다.
// instance가 생길 때마다 method도 새로 생기니까 메모리 낭비가 발생하는 것.

// 그러면 공통으로 사용하는 method를 (상위)Prototype에 넣어두자! -> "상속"
function Circle(radius) {
    this.radius = radius;
    // this.getDiameter = function(){
    //     return 2 * this.radius;
    // }
    Circle.prototype.getDiameter = function(){
        return 2 * this.radius;
    }
}

const circle1 = new Circle(5);
const circle2 = new Circle(10);

console.log(circle1.getDiameter === circle2.getDiameter); //true

자바스크립트는 상속에 대한 내용을 동적으로 바꿀 수 있다.

function Circle(radius) {
    this.radius = radius;
    Circle.prototype.getDiameter = function(){
        return 2 * this.radius;
    }

    // 공용변수를 만들어 쓸 수 있다.
    Circle.prototype.name = '홍길동'
}

const circle1 = new Circle(5);
const circle2 = new Circle(10);

console.log(circle1.name, circle2.name); //홍길동 홍길동

// name을 바꿔주려면??
circle1.name = '아이유'; // circle1에 name property를 새로 추가한다.
console.log(circle1.name, circle2.name); //아이유 홍길동
// 이렇게 바꿔주면 안된다.

// 이렇게 바꿔주거나
Circle.prototype.name = '아이유';
console.log(circle1.name, circle2.name); //아이유 아이유

// 이렇게 바꿔줘야 한다.
circle1.__proto__.name = '아이유';
console.log(circle1.name, circle2.name); //아이유 아이유

🐠 Rest parameter

Rest parameter(ES6)

  • arguments유사 배열 객체 대신 사용
  • Rest parameterArray로 사용
    • 점 3개로 만든다.
      function foo(...args){
          console.log(arguments); //[Arguments] { '0': 1, '1': 2, '2': 3, '3': 4, '4': 5 }
          console.log(args); //[ 1, 2, 3, 4, 5 ]
      		return args.pop(); // 배열은 pop과 같은 메서드 사용이 가능
      }
      
      foo(1,2,3,4,5);
      
      var result = foo(1,2,3,4,5);
      console.log(result); //5
    • parameter의 제일 마지막에만 위치할 수 있고, 2개 이상 나올 수 없다.
      function foo(...args1, ...args2){ // 불가능
          console.log(arguments);
          console.log(args);
      }

argumentsrest parameter 둘다 가변 인자 함수를 만들 때 사용한다.

💡 함수내에서 arguments와 rest parameter 둘 다 이용할 수 있다.
단, arrow function에서는 arguments를 사용할 수 없고, rest parameter만 사용가능하다.

2개의 댓글

comment-user-thumbnail
2022년 7월 6일

언니 잘 보고 갑니당 φ(゜▽゜*)♪

1개의 답글