고차 함수

고차 함수는 함수형 프로그래밍 개념 중 하나로 함수를 인수로 받거나, 함수를 반환하는 함수를 말한다.

코드를 더 모듈화 하고 재사용 가능하게 만들며, 함수 조합과 같은 고급 프로그래밍 기술을 사용할 수 있게 해 준다.

 

함수를 인수로 받는 고차 함수

고차 함수는 다른 함수를 인수로 받아서 호출하거나, 인수로 받은 함수를 내부에서 사용하는 함수이다. 자바스크립트의 내장 메서드 중 'map', 'filter', 'reduce' 등이 이에 해당한다.

 

map

배열의 각 요소에 대해 제공된 함수를 호출하고, 그 결과를 새로운 배열로 반환한다.

const numbers = [1, 2, 3, 4, 5];

const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]

 

여기서 'map' 함수는 'num => num * 2' 함수를 인수로 받아, 배열 'numbers'의 각 요소에 대해 이 함수를 호출한다.

 

filter

배열의 각 요소에 대해 제공된 함수를 호출하고, 그 결과가 'true'인 요소만을 포함하는 새로운 배열을 반환한다.

const numbers = [1, 2, 3, 4, 5];

const evenNumbers = numbers.filter(num => num % 2 === 0);
console.log(evenNumbers); // [2, 4]

 

여기서 'filter' 함수는 'num => num % 2 === 0' 함수를 인수로 받아, 배열 'numbers'의 각 요소에 대해 이 함수를 호출하고, 짝수인 요소만을 포함하는 새로운 배열을 반환한다.

 

함수를 반환하는 고차 함수

고차 함수는 새로운 함수를 반환할 수도 있다. 이를 통해 함수 조합이나 부분 적용(Partial Application)을 쉽게 구현할 수 있다.

 

함수 생성기

function createMultiplier(multiplier) {
    return function(value) {
        return value * multiplier;
    };
}

const double = createMultiplier(2);
const triple = createMultiplier(3);

console.log(double(5)); // 10
console.log(triple(5)); // 15

 

여기서 'createMultiplier' 함수는 'multiplier'를 인수로 받아, 'value'를 인수로 받아 'value'와 'multiplier'의 곱을 반환하는 함수를 반환한다.

 

함수 조합기

function compose(f, g) {
    return function(x) {
        return f(g(x));
    };
}

const addOne = x => x + 1;
const square = x => x * x;

const addOneAndSquare = compose(square, addOne);

console.log(addOneAndSquare(2)); // 9

 

'compose' 함수는 두 함수를 받아, 먼저 'g' 함수를 호출하고 그 결과를 'f' 함수에 전달하는 새로운 함수를 반환한다.

 

함수를 실행하면 addOne(2)가 먼저 실행되어 3을 반환하게 된다.

square 함수에 위의 반환값 3으로 실행되어 최종 출력값은 9가 된다.

 

고차 함수의 장점

1. 재사용성

고차 함수는 인수로 전달된 함수에 따라 다른 동작을 수행할 수 있어, 더 유연하고 재사용 가능한 코드를 작성할 수 있다.

 

2. 모듈화

고차 함수는 코드의 기능을 작은 단위로 나누고, 이를 조합하여 더 복잡한 동작을 만들 수 있게 한다.

 

3. 가독성

고차 함수를 사용하면 반복적인 패턴을 추상화하여 코드의 가독성을 높일 수 있다.

 

이벤트 처리

function addEventListener(element, event, handler) {
    element.addEventListener(event, handler);
}

const button = document.querySelector('button');
addEventListener(button, 'click', () => alert('Button clicked!'));

 

여기서 'addEventListener' 함수는 'handler' 함수를 인수로 받아, 특정 이벤트가 발생했을 때 이를 처리한다.

 

데이터 변환

const data = [
    { name: 'Bak', age: 25 },
    { name: 'Kim', age: 30 },
    { name: 'Lee', age: 35 }
];

const names = data.map(person => person.name);
console.log(names); // ['Bak', 'Kim', 'Lee']

 

'map' 함수는 'person => person.name' 함수를 인수로 받아, 'data' 배열의 각 객체에서 이름을 추출하여 새로운 배열을 만든다.

728x90
반응형

'Program Language > JavaScript' 카테고리의 다른 글

Arrow Function  (0) 2024.08.26
this  (0) 2024.08.26
JavaScript #16 객체지향 프로그래밍(OOP)  (0) 2024.07.23
JavaScript #15 비동기 자바스크립트  (0) 2024.07.23
JavaScript #14 ES6+ 문법  (4) 2024.07.23

객체 지향 프로그래밍(OOP)

객체 지향 프로그래밍 (Object Oriented Programming)은 객체를 중심으로 프로그램을 설계하고 구현하는 방법론이다. 자바스크립트에서 OOP는 클래스와 인스턴스, 생성자 함수, 상속 그리고 프로토타입 개념을 통해서 구현된다.

 

클래스와 인스턴스

클래스는 객체를 생성하기 위한 블루프린트 또는 템플릿이다. 클래스는 속성과 메서드를 정의하며, 인스턴스는 클래스를 기반으로 생성된 객체를 의미한다.

 

클래스

ES6 이전에는 자바스크립트에서 클래스를 정의하기 위해 생성자 함수와 프로토타입을 사용했다. 이후에는 'class' 키워드가 추가되어 키워드를 사용하여 클래스를 정의할 수 있게 되었다.

 

클래스 정의

class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }

    greet() {
        console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
    }
}

// 인스턴스 생성
const bak = new Person('Bak', 30);
bak.greet(); // "Hello, my name is Bak and I am 30 years old."

 

생성자 함수

생성자 함수는 클래스를 정의하는 전통적인 방법으로 'new' 키워드를 사용하여 생성자 함수를 호출하면 새로운 인스턴스가 생성된다.

function Person(name, age) {
    this.name = name;
    this.age = age;
}

Person.prototype.greet = function() {
    console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
};

// 인스턴스 생성
const bak = new Person('Bak', 25);
bak.greet(); // "Hello, my name is Bak and I am 25 years old."

 

상속과 프로토타입

상속은 한 클래스가 다른 클래스의 속성과 메서드를 상속받아 사용하는 개념이다. 자바스크립트에서는 프로토타입 기반 상속을 사용한다. 자바스크립트의 모든 객체는 프로토타입 객체를 가지고 있으며, 다른 객체로부터 메서드와 속성을 상속받을 수 있다.

 

클래스 상속

ES6에서는 'extends' 키워드를 사용하여 상속을 구현할 수 있다.

class Animal {
    constructor(name) {
        this.name = name;
    }

    speak() {
        console.log(`${this.name} makes a noise.`);
    }
}

class Dog extends Animal {
    constructor(name, breed) {
        super(name); // 부모 클래스의 생성자를 호출
        this.breed = breed;
    }

    speak() {
        console.log(`${this.name} barks.`);
    }
}

const rex = new Dog('Rex', 'Labrador');
rex.speak(); // "Rex barks."

 

프로토타입 상속

ES6 이전에는 프로토타입을 사용하여 상속을 구현하였다.

function Animal(name) {
    this.name = name;
}

Animal.prototype.speak = function() {
    console.log(`${this.name} makes a noise.`);
};

function Dog(name, breed) {
    Animal.call(this, name); // 부모 생성자 호출
    this.breed = breed;
}

Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

Dog.prototype.speak = function() {
    console.log(`${this.name} barks.`);
};

const rex = new Dog('Rex', 'Labrador');
rex.speak(); // "Rex barks."

 

상속의 이점

상속의 특징을 제대로 이해하고 적절한 상황에 맞게 활용하는 것이 중요하다.

 

코드 재사용성

상속을 통해 기존 클래스의 기능을 재사용할  수 있기 때문에 이를 통해 중복 코드를 줄이고 새로운 기능을 쉽게 추가할 수 있다.

class Animal {
    constructor(name) {
        this.name = name;
    }

    speak() {
        console.log(`${this.name} makes a noise.`);
    }
}

class Dog extends Animal {
    speak() {
        console.log(`${this.name} barks.`);
    }
}

const dog = new Dog('Rex');
dog.speak(); // "Rex barks."

 

'Dog' 클래스는 'Animal' 클래스의 'name' 속성과 'speak' 메서드를 재사용하여 기능을 확장한다.

 

코드의 가독성과 유지보수성 향상

상속으로 클래스 간의 관계를 명확히 하고, 코드를 더 구조적이고 일관성 있게 작성하여 코드의 가독성을 높이고 유지보수를 쉽게 할 수 있다.

class Shape {
    constructor(color) {
        this.color = color;
    }

    draw() {
        console.log('Drawing a shape');
    }
}

class Circle extends Shape {
    constructor(color, radius) {
        super(color);
        this.radius = radius;
    }

    draw() {
        super.draw();
        console.log(`Drawing a circle with radius ${this.radius}`);
    }
}

const circle = new Circle('red', 5);
circle.draw();
// "Drawing a shape"
// "Drawing a circle with radius 5"

 

'circle' 클래스는 'shape' 클래스를 상속받아 'color' 속성과 'draw' 메서드를 재사용하고 필요한 기능을 추가한다. 이를 통해서 코드가 더 구조적이고 명확해지도록 할 수 있다.

 

다형성(Polymorphism)

다형성은 같은 메서드가 다양한 클래스에서 다른 방식으로 동작하도록 하는 것으로 코드의 유연성을 높일 수 있다.

class Animal {
    speak() {
        console.log('Animal makes a noise');
    }
}

class Dog extends Animal {
    speak() {
        console.log('Dog barks');
    }
}

class Cat extends Animal {
    speak() {
        console.log('Cat meows');
    }
}

function makeAnimalSpeak(animal) {
    animal.speak();
}

const dog = new Dog();
const cat = new Cat();

makeAnimalSpeak(dog); // "Dog barks"
makeAnimalSpeak(cat); // "Cat meows"

 

'makeAnimalSpeak' 함수는 'Animal' 클래스를 상속받는 객체를 인수로 받아 다형성을 통해 다양한 동작을 수행할 수 있다.

 

클래스 간의 계층 구조 정의

상속 구조는 클래스 간의 계층 구조를 정의할 수 있어 복잡한 시스템을 더 쉽게 이해하고 관리할 수 있다.

class Vehicle {
    constructor(brand) {
        this.brand = brand;
    }

    start() {
        console.log('Starting the vehicle');
    }
}

class Car extends Vehicle {
    constructor(brand, model) {
        super(brand);
        this.model = model;
    }

    start() {
        super.start();
        console.log(`Starting the car ${this.brand} ${this.model}`);
    }
}

const car = new Car('Toyota', 'Corolla');
car.start();
// "Starting the vehicle"
// "Starting the car Toyota Corolla"

 

'vehicle' 클래스는 기본적인 차량의 속성과 메서드를 정의하고, 'Car' 클래스는 이를 상속받아 구체적인 차량 모델을 정의한다. 이는 클래스 간의 관계를 명확히 하고 계층 구조를 쉽게 이해할 수 있다.

 

객체 간의 관계 모델링

상속을 사용하면 객체 간의 관계를 모델링하여 복잡한 시스템을 설계하고 구현하는 데 유용하다.

class Employee {
    constructor(name, position) {
        this.name = name;
        this.position = position;
    }

    work() {
        console.log(`${this.name} is working as a ${this.position}`);
    }
}

class Manager extends Employee {
    constructor(name, department) {
        super(name, 'Manager');
        this.department = department;
    }

    manage() {
        console.log(`${this.name} is managing the ${this.department} department`);
    }
}

const manager = new Manager('Bak', 'Sales');
manager.work(); // "Bak is working as a Manager"
manager.manage(); // "Bak is managing the Sales department"

 

'manager' 클래스는 'Employee' 클래스를 상속받아 관리자의 속성과 메서드를 정의하여 객체 간의 관계를 명확히 모델링할 수 있다.

 

상속은 객체 지향 프로그래밍에서 매우 유용한 개념이지만 과도하게 또는 잘못된 방식으로 사용하게 되면 오히려 코드의 복잡성을 증가시켜 유지보수를 어렵게 만들 수 있다.

728x90
반응형

'Program Language > JavaScript' 카테고리의 다른 글

this  (0) 2024.08.26
JavaScript #17 고차 함수(High-order Function)  (2) 2024.07.24
JavaScript #15 비동기 자바스크립트  (0) 2024.07.23
JavaScript #14 ES6+ 문법  (4) 2024.07.23
JavaScript #13 DOM(Document Object Model)  (0) 2024.07.23

비동기

비동기는 시간이 걸리는 네트워크 요청, 파일 읽기 등을 수행할 때 코드의 실행을 차단하지 않고 비동기적으로 처리할 수 있게 해주는 중요한 개념이다. 자바스크립트의 비동기와 관련된 기능들은 웹 애플리케이션을 더 빠르고 반응성이 좋게 동작하게 한다.

 

콜백 함수

콜백 함수는 다른 함수의 인수로 전달되는 함수이다.

비동기 작업이 완료되면 호출되며 비동기 작업을 처리하는 가장 기본적인 방법이다.

function fetchData(callback) {
    setTimeout(() => {
        const data = { id: 1, name: 'John Doe' };
        callback(data);
    }, 1000); // 1초 후에 콜백 함수 호출
}

fetchData((data) => {
    console.log('Data received:', data);
});

 

여기서 fetchData 함수는 1초 후에 데이터를 콜백 함수에 전달한다. 이 방식은 간단하지만, 여러 비동기 작업이 중첩될 경우 콜백 지옥이라 불리는 가독성이 떨어지는 코드가 될 위험이 있다.

 

프로미스

프로미스는 비동기 작업의 완료 또는 실패를 처리하는 객체이다. then, catch, finally 메서드를 사용하여 비동기 작업의 결과를 처리한다.

function fetchData() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            const data = { id: 1, name: 'John Doe' };
            resolve(data); // 성공적으로 데이터를 반환
        }, 1000);
    });
}

fetchData()
    .then((data) => {
        console.log('Data received:', data);
    })
    .catch((error) => {
        console.error('Error:', error);
    });

 

프로미스를 사용하면 코드의 가독성이 개선되고, 여러 비동기 작업을 체인으로 연결하여 처리할 수 있다. 프로미스가 성공적으로 완료될 때 resolve가 호출되며 'value'에는 성공적으로 완료된 후 전달되는 값이다. 실패할 경우 reject가 호출되며 'reason'은 프로미스가 실패한 이유를 나타내는 값이다.

 

프로미스는 대기(pending), 이행(fulfilled), 거부(rejected) 세 가지 상태를 가질 수 있다.

 

대기

프로미스가 생성된 초기 상태로 'then', 'catch', 'finally' 메서드가 아직 호출되지 않은 상태이다.

 

이행

프로미스가 성공적으로 완료된 상태로 'then' 메서드가 호출된다.

 

거부

프로미스가 실패한 상태로 'catch' 메서드가 호출된다.

 

프로미스가 이행, 거부 상관없이 작업이 완료되었을 때 항상 'finally'가 호출된다. 보통 프로미스의 상태에 상관없이 반드시 실행되어야 하는 리소스를 해제, 로딩 상태를 해제하는 등의 작업 같은 코드를 작성할 때 사용된다.

const myPromise = new Promise((resolve, reject) => {
  let success = true; // 작업 성공 여부를 결정하는 변수
  
  setTimeout(() => {
    if (success) {
      resolve("작업이 성공적으로 완료되었습니다!"); // 이행 상태로 전환
    } else {
      reject("작업이 실패했습니다."); // 거부 상태로 전환
    }
  }, 1000); // 1초 후에 상태 결정
});

myPromise
  .then((value) => {
    console.log("이행됨:", value); // 프로미스가 이행되면 실행
  })
  .catch((reason) => {
    console.log("거부됨:", reason); // 프로미스가 거부되면 실행
  })
  .finally(() => {
    console.log("프로미스가 완료됨."); // 프로미스가 완료되면 항상 실행
  });

 

async, await

async와 await 키워드는 ES2017에서 도입된 비동기 프로그래밍의 최신 방법이다.

async 함수는 항상 프로미스를 반환하며, await 키워드는 프로미스가 처리될 때까지 함수의 실행을 일시 중지한다. 이를 통해 비동기 코드를 동기식 코드처럼 작성할 수 있게 된다.

function fetchData() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            const data = { id: 1, name: 'John Doe' };
            resolve(data); // 성공적으로 데이터를 반환
        }, 1000);
    });
}

async function getData() {
    try {
        const data = await fetchData(); // 프로미스가 처리될 때까지 대기
        console.log('Data received:', data);
    } catch (error) {
        console.error('Error:', error);
    }
}

getData();

 

동기식처럼 처리되는 코드처럼 보이면서도 async, await 키워드를 사용해서 비동기적으로 동작하게 한다.

 

콜백 지옥 (Callback Hell)

콜백 지옥은 자바스크립트에서 비동기 작업을 중첩된 콜백 함수로 처리할 때 발생하는 문제로, 코드의 가독성과 유지보수성이 크게 떨어지는 현상을 말한다.

function authenticateUser(username, password, callback) {
    setTimeout(() => {
        console.log('User authenticated');
        callback(null, { userId: 1, username: username });
    }, 1000);
}

function fetchUserData(userId, callback) {
    setTimeout(() => {
        console.log('User data fetched');
        callback(null, { userId: userId, data: 'Some user data' });
    }, 1000);
}

function processData(data, callback) {
    setTimeout(() => {
        console.log('Data processed');
        callback(null, { processedData: 'Processed data' });
    }, 1000);
}

authenticateUser('user1', 'password123', (authError, authData) => {
    if (authError) {
        console.error('Authentication error:', authError);
        return;
    }
    fetchUserData(authData.userId, (fetchError, userData) => {
        if (fetchError) {
            console.error('Fetch error:', fetchError);
            return;
        }
        processData(userData.data, (processError, processedData) => {
            if (processError) {
                console.error('Processing error:', processError);
                return;
            }
            console.log('All tasks completed successfully:', processedData);
        });
    });
});

 

'authenticateUser' 함수는 사용자를 인증하고 결과를 콜백 함수에 전달한다.

인증이 성공하면 'fetchUserData' 함수가 호출되어 사용자 데이터를 가져온다. 

사용자 데이터가 성공적으로 가져오면, 'processData' 함수가 호출되어 데이터를 처리한다.

 

위 코드는 각 단계별로 성공 여부를 대기하고 성공 시 다음 단계로 넘어가는 구조이다. 이러한 구조를 중첩된 콜백 함수라고 하며 코드가 길어지고 복잡해져 가독성이 떨어진다는 것이 확인된다. 가독성이 떨어지면 유지보수와 에러 처리에도 어려움이 발생할 수밖에 없다.

 

이를 예방하는 방법으로는 위에서 서술된 프로미스 사용, async, await 키워드를 사용하는 방법들이 있고 그 밖에도 몇 가지 방법들이 존재한다.

 

이름이 있는 함수로 콜백 분리

익명 함수 대신 이름이 있는 함수를 사용하여 콜백을 분리하면 코드의 가독성과 재사용성이 향상된다.

function fetchData() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            const data = { id: 1, name: 'John Doe' };
            resolve(data);
        }, 1000);
    });
}

fetchData()
    .then(data => {
        console.log('Data received:', data);
        return fetchData(); // 다음 비동기 작업
    })
    .then(data => {
        console.log('Next data received:', data);
    })
    .catch(error => {
        console.error('Error:', error);
    });

 

모듈화 및 함수 분리

비동기 작업을 여러 작은 함수로 나누고 모듈화 하면 코드가 더 관리하기 쉬워진다.

function fetchData() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            const data = { id: 1, name: 'John Doe' };
            resolve(data);
        }, 1000);
    });
}

async function handleFirstData() {
    try {
        const data = await fetchData();
        console.log('Data received:', data);
        return data;
    } catch (error) {
        console.error('Error in handleFirstData:', error);
    }
}

async function handleSecondData() {
    try {
        const data = await fetchData();
        console.log('Next data received:', data);
        return data;
    } catch (error) {
        console.error('Error in handleSecondData:', error);
    }
}

async function processAllData() {
    await handleFirstData();
    await handleSecondData();
}

processAllData();

 

비동기 제어 라이브러리 사용

비동기 제어 흐름을 관리하기 위해 'async.js'와 같은 비동기 작업을 관리하는 데 유용한 유틸리티를 제공하는 라이브러리를 사용할 수 있다.

 

async.js 등 라이브러리를 사용하기 위해서는 설치가 필요하다.

<script src="https://cdnjs.cloudflare.com/ajax/libs/async/3.2.0/async.min.js"></script>

 

node.js 환경에서는 npm install async를 사용하여 설치할 수 있다.

npm install async

 

async.js 사용법

시퀀스 작업 처리

'async.series'를 사용하여 비동기 작업을 순차적으로 처리할 수 있다.

const async = require('async');

function fetchData(callback) {
    setTimeout(() => {
        console.log('Data fetched');
        callback(null, { id: 1, name: 'John Doe' });
    }, 1000);
}

function processData(data, callback) {
    setTimeout(() => {
        console.log('Data processed:', data);
        callback(null, { processedData: true });
    }, 1000);
}

async.series([
    function(callback) {
        fetchData(callback);
    },
    function(callback) {
        fetchData((err, data) => {
            if (err) return callback(err);
            processData(data, callback);
        });
    }
], function(err, results) {
    if (err) {
        console.error('Error:', err);
    } else {
        console.log('All tasks completed:', results);
    }
});

 

병렬 작업 처리

'async.parallel'을 사용하여 비동기 작업을 병렬로 처리할 수 있다.

const async = require('async');

function fetchData1(callback) {
    setTimeout(() => {
        console.log('Data1 fetched');
        callback(null, { id: 1, name: 'John Doe' });
    }, 1000);
}

function fetchData2(callback) {
    setTimeout(() => {
        console.log('Data2 fetched');
        callback(null, { id: 2, name: 'Jane Doe' });
    }, 500);
}

async.parallel([
    function(callback) {
        fetchData1(callback);
    },
    function(callback) {
        fetchData2(callback);
    }
], function(err, results) {
    if (err) {
        console.error('Error:', err);
    } else {
        console.log('All tasks completed in parallel:', results);
    }
});

 

각 작업 처리

'async.each'를 사용하여 배열의 각 요소에 대해 비동기 작업을 수행할 수 있다.

const async = require('async');

const items = [1, 2, 3, 4, 5];

function processItem(item, callback) {
    setTimeout(() => {
        console.log('Processed item:', item);
        callback(null, item);
    }, 1000);
}

async.each(items, processItem, function(err) {
    if (err) {
        console.error('Error:', err);
    } else {
        console.log('All items processed');
    }
});

 

728x90
반응형

ES6+

ECMAScript 6부터의 버전에서는 자바스크립트 언어에 여러 유용한 기능들이 추가되었다. 

 

이 중에서도 특히 중요한 템플릿 리터럴, 디스트럭처링 할당, 스프레드 연산자와 나머지 매개변수, 모듈화에 대해서 정리한다.

 

템플릿 리터럴 (Template Literals)

템플릿 리터럴은 백틱을 사용하여 문자열을 작성하며, 문자열 내에 표현식을 포함할 수 있는 기능을 제공한다.

 

기본 사용법

const name = 'Bak';
const greeting = `Hello, ${name}!`; // 표현식 ${}을 사용
console.log(greeting); // "Hello, Bak!"

 

${} 표현식을 사용해서 문자열 내에서 직접 변수의 값에 바로 접근할 수 있다.

 

여러 줄 문자열

템플릿 리터럴을 사용하면 여러 줄에 걸쳐 문자열을 작성할 수 있다.

const multiline = `This is
a string
that spans multiple lines.`;
console.log(multiline);

 

ES6 이전에는 문자열을 쓸 때 큰따옴표를 사용해야 했으며 백틱을 사용하게 되면 구문 오류(Syntax Error)가 발생한다.

 

그리고 이전의 자바스크립트는 한 줄로 작성된 문자열을 인식하기 때문에 여러 줄에 걸쳐 문자열을 작성하기 위해서는 줄 끝에 백슬래시를 사용하여 줄 바꿈을 나타내야 했다.

 

var multiLineString = "This is the first line \
This is the second line \
This is the third line";
console.log(multiLineString);
// "This is the first line This is the second line This is the third line"

// 또는 

var multiLineString = "This is the first line\n" +
                      "This is the second line\n" +
                      "This is the third line";
console.log(multiLineString);
// This is the first line
// This is the second line
// This is the third line

 

이런 방법들은 코드의 가독성을 떨어뜨릴 여지가 충분했다.

 

디스트럭처링 할당 (Destructuring Assignment)

디스트럭처링 할당은 배열이나 객체의 값을 개별 변수로 쉽게 추출할 수 있게 해주는 문법이다. 이는 코드를 더 간결하고 가독성 좋게 만들며, 특히 복잡한 데이터 구조를 다룰 때 유용하다. 

 

const numbers = [1, 2, 3, 4];
const [first, second, ...rest] = numbers; // 배열의 첫 두 요소를 추출하고 나머지는 rest에 할당
console.log(first); // 1
console.log(second); // 2
console.log(rest); // [3, 4]

 

객체 디스트럭처링

const person = {
    name: 'Bob',
    age: 30,
    job: 'developer'
};
const { name, age, job } = person; // 객체의 프로퍼티를 개별 변수로 추출
console.log(name); // 'Bob'
console.log(age); // 30
console.log(job); // 'developer'

 

중첩된 구조 디스트럭처링

const person = {
    name: 'Charlie',
    address: {
        city: 'New York',
        zip: '10001'
    }
};
const { name, address: { city, zip } } = person; // 중첩된 구조에서 프로퍼티 추출
console.log(name); // 'Charlie'
console.log(city); // 'New York'
console.log(zip); // '10001'

 

함수 파라미터에서 디스트럭처링

함수 파라미터에서 디스트럭처링을 사용하면 코드가 더 직관적이고 명확해진다.

function displayPerson({ name, age }) {
  console.log(`Name: ${name}, Age: ${age}`);
}

const person = { name: 'Bak', age: 25 };
displayPerson(person); // Name: Bak, Age: 25

 

디스트럭처링을 잘 활용하면 코드의 유지보수성을 높이고, 가독성을 개선하며, 실수를 줄이는 데 도움이 된다. 

 

스프레드 연산자 (Spread Operator)

스프레드 연산자('...')는 세 개의 점으로 표현된다.

 

배열이나 객체를 펼치거나, 함수의 나머지 매개변수로 사용할 수 있어 배열이나 객체를 보다 간결하고 직관적으로 다룰 수 있게 해 준다.

 

배열에서 스프레드 연산자

// 배열 복사
const arr1 = [1, 2, 3];
const arr2 = [...arr1];
console.log(arr2); // [1, 2, 3]

// 배열 결합
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2]; // 배열을 펼쳐서 결합
console.log(combined); // [1, 2, 3, 4, 5, 6]

 

객체에서의 스프레드 연산자

// 객체 복사
const obj1 = { a: 1, b: 2 };
const obj2 = { ...obj1 };
console.log(obj2); // { a: 1, b: 2 }

// 객체 결합
const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };
const combinedObj = { ...obj1, ...obj2 }; // 객체를 펼쳐서 결합
console.log(combinedObj); // { a: 1, b: 2, c: 3, d: 4 }

 

배열이나 객체를 단순히 변수에 대입하는 것과 스프레드 연산자를 사용하여 복사하는 것은 중요한 차이가 있다.

변수 대입과 참조

자바스크립트에서 배열이나 객체를 변수에 대입할 때 실제로는 해당 배열이나 객체의 참조가 복사된다. 이는 두 변수가 같은 배열이나 객체를 참조하게 된다는 의미로 한 변수를 통해 배열이나 객체를 변경하면, 다른 변수에도 그 변경 사항이 반영된다.

 

 

나머지 매개변수 (rest parameters)

함수에서 가변 인자를 처리할 수 있도록 도와주는 기능이다. 이 기능은 보다 인자들을 유연하게 다룰 수 있게 된다.

function sum(...args) { // 나머지 매개변수로 모든 인수를 배열로 받음
    return args.reduce((total, current) => total + current, 0);
}
console.log(sum(1, 2, 3)); // 6
console.log(sum(4, 5, 6, 7)); // 22

 

나머지 매개변수를 사용하면 함수가 전달받는 인자의 수에 제한이 없어 특히 가변 인자를 처리해야 하는 상황에서 유용하다.

function concatenate(separator, ...strings) {
  return strings.join(separator);
}

console.log(concatenate(', ', 'Hello', 'World', 'JavaScript')); // "Hello, World, JavaScript"
console.log(concatenate(' - ', 'Learn', 'Code', 'Enjoy')); // "Learn - Code - Enjoy"

 

배열 메서드를 직접 사용할 수 있어 가변 인자를 처리하는 로직이 명확해져 가독성이 높아지고 가변 인자를 처리하기 위한 별도의 코드를 작성할 필요가 없어 중복 코드를 줄일 수 있다.

 

arguments 

전통적으로 자바스크립트에서는 arguments 객체를 사용하여 함수의 모든 인자를 접근할 수 있다. 하지만 arguments 객체는 배열이 아니기 때문에 배열 메서드를 직접 사용할 수 없어 추가적인 변환 작업을 필요로 한다.

function oldSum() {
  const args = Array.prototype.slice.call(arguments);
  return args.reduce((total, num) => total + num, 0);
}

console.log(oldSum(1, 2, 3)); // 6

 

반면 나머지 매개변수는 배열로 제공되기 때문에 배열 메서드를 바로 사용할 수 있어 더 직관적이고 간편하다.

 

모듈화 (Modules)

모듈화를 통해 코드를 여러 파일로 나눠서 관리할 수 있어 코드의 재사용성과 유지보수성을 높여준다.

 

모듈 내보내기/가져오기 (Export/Import)

모듈에서 함수를 내보내는 두 가지 방법이 있다. 

 

name exports

모듈에서 여러 개의 변수를 내보낼 수 있으며, 각 변수를 특정 이름으로 내보낸다. 가져올 때는 내보낸 이름을 사용하여 모듈을 가져와야 한다.

// math.js
export const pi = 3.14;
export function add(x, y) {
  return x + y;
}
export class Calculator {
  subtract(x, y) {
    return x - y;
  }
}

 

import

// main.js
import { pi, add, Calculator } from './math.js';

console.log(pi); // 3.14
console.log(add(2, 3)); // 5
const calc = new Calculator();
console.log(calc.subtract(5, 3)); // 2

 

name export의 경우 같은 이름을 가진 변수가 이미 사용 중이라면 이름 충돌이 발생하기도 한다. 이를 해결하기 위해서 as 키워드를 사용하여 별칭을 붙일 수 있다.

import { add as addNumbers } from './math.js';

 

default exports

모듈에서 하나의 기본 값을 내보낸다. 모듈당 하나의 default export만 가질 수 있으며 이름 없이 내보낼 수 있다.

// utils.js
export default function greet(name) {
  return `Hello, ${name}!`;
}

 

import

// main.js
import greet from './utils.js';

console.log(greet('Bak')); // Hello, Bak!

 

혼합 사용

하나의 모듈에서 named exports와 default export를 동시에 사용할 수 있다.

// module.js
export const pi = 3.14;
export function add(x, y) {
  return x + y;
}
export default function subtract(x, y) {
  return x - y;
}

// main.js
import subtract, { pi, add } from './module.js';

console.log(pi); // 3.14
console.log(add(2, 3)); // 5
console.log(subtract(5, 3)); // 2
728x90
반응형

DOM

DOM은 HTML 문서의 구조화된 표현이다. 웹 페이지의 각 요소는 객체로 표현되며, 이 객체들은 트리 구조를 형성한다. DOM을 통해 자바스크립트는 문서의 구조, 스타일, 콘텐츠를 동적으로 변경할 수 있다.

 

요소 선택

DOM 요소를 조작하기 위해서는 먼저 해당 요소를 선택해야 한다.

자주 사용되는 두 가지 방법으로 getElementById와 querySelector가 있다.

 

getElementById

getElementById 메서드는 문서에서 특정 ID를 가진 요소를 선택한다. ID는 문서 내에서 고유해야 한다.

 

const element = document.getElementById('myElement');
console.log(element); // 해당 ID를 가진 요소를 출력

 

querySelector

querySelector 메서드는 제공된 CSS 선택자에 매칭되는 첫 번째 요소를 반환하며 더 유연하게 요소를 선택할 수 있다.

const element = document.querySelector('.myClass');
console.log(element); // 해당 클래스를 가진 첫 번째 요소를 출력

const anotherElement = document.querySelector('#myElement');
console.log(anotherElement); // 해당 ID를 가진 요소를 출력

 

요소 조작

선택한 요소의 콘텐츠와 스타일을 변경할 수 있다. 

getElementById나 querySelector는 요소를 선택하는 방법에만 차이가 있을 뿐 선택한 요소는 동일한 프로퍼티를 통해 조작할 수 있다.

 

innerHTML 

innerHTML 프로퍼티는 요소의 HTML 콘텐츠를 설정하거나 가져온다. 요소 내부의 HTML 구조를 동적으로 변경할 수 있다.

const element = document.getElementById('myElement');
element.innerHTML = '<p>새로운 콘텐츠</p>';

 

style

style 프로퍼티는 인라인 스타일을 설정한다. 특정 요소의 CSS 스타일을 직접 변경할 때 유용하다.

const element = document.getElementById('myElement');
element.innerHTML = '<p>새로운 콘텐츠</p>';

 

classList

classList 프로퍼티는 요소의 클래스 목록을 조작하는 메서드를 제공한다. 이를 통해 클래스를 추가, 제거, 토글 할 수 있다.

const element = document.getElementById('myElement');
element.classList.add('newClass');
element.classList.remove('oldClass');
element.classList.toggle('activeClass');

 

이벤트 처리

DOM 요소에 이벤트를 처리하기 위해서는 addEventListener 메서드를 사용한다. 

const button = document.getElementById('myButton');
button.addEventListener('click', function() {
    alert('버튼이 클릭되었습니다!');
});

 

HTML 예제

간단한 HTML을 작성하고 요소를 가져와서 조작해 본다.

 

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Element Selection Example</title>
    <style>
        .highlight {
            background-color: yellow;
            color: red;
            font-weight: bold;
        }
    </style>
</head>
<body>
    <div id="content">
        <h1 id="header">Hello!</h1>
        <p class="paragraph">This is a paragraph.</p>
    </div>
    <button id="changeContent">Change Content</button>
    <button id="changeStyle">Change Style</button>
    <button id="toggleClass">Toggle Class</button>
    <script src="script.js"></script>
</body>
</html>

 

script.js

// 요소 선택
const header = document.getElementById('header');
const paragraph = document.querySelector('.paragraph'); // 클래스 선택자를 사용
const changeContentButton = document.getElementById('changeContent');
const changeStyleButton = document.getElementById('changeStyle');
const toggleClassButton = document.getElementById('toggleClass');

// 콘텐츠 변경
changeContentButton.addEventListener('click', function() {
    header.innerHTML = 'Content has been changed!';
    paragraph.innerHTML = 'The paragraph content has been changed!';
});

// 스타일 변경
changeStyleButton.addEventListener('click', function() {
    header.style.color = 'blue';
    paragraph.style.backgroundColor = 'lightgray';
    paragraph.style.padding = '10px';
});

// 클래스 토글
toggleClassButton.addEventListener('click', function() {
    paragraph.classList.toggle('highlight');
});

 

페이지 상태

HTML

 

 

Change Content 버튼을 클릭 시

OnClick Change Content Button

 

Change Style 버튼 클릭 시

OnClick Change Style

 

Toggle Class 버튼 클릭 시

Onclick Toggle Class

 

728x90
반응형

'Program Language > JavaScript' 카테고리의 다른 글

JavaScript #15 비동기 자바스크립트  (0) 2024.07.23
JavaScript #14 ES6+ 문법  (4) 2024.07.23
JavaScript #11 함수  (0) 2024.07.23
JavaScript #10 조건문과 반복문  (1) 2024.07.23
JavaScript #9 데이터 타입  (9) 2024.07.22

객체 (Object)

객체는 자바스크립트에서 데이터를 구조화하고 저장하는 방식 중 하나로 키와 값 쌍의 집합으로 구성된다. 이 키-값 쌍을 통해서 다양한 테이터와 기능을 하나의 단위로 묶어 관리할 수 있다.

 

특징

키-값

키는 문자열 또는 심볼이어야 하며 값은 어떠한 데이터 타입도 가능하다. 만약 키에 숫자를 입력할 경우 자동으로 문자로 변환된다.

 

중첩 가능

객체의 값으로 또 다른 객체를 가질 수 있다. 이를 통해 복잡한 데이터 구조를 표현하는 것이 가능하다.

 

동적 속성 추가/제거

객체는 생성된 이후에도 속성을 동적으로 추가하거나 삭제할 수 있다.

 

생성 방법

1. 객체 리터럴

가장 일반적인 객체 생성 방법으로 중괄호를 사용하여 키-값 쌍을 정의한다.

const person = {
    name: 'Bak',
    age: 25,
    greet: function() {
        console.log('Hello, my name is ' + this.name);
    }
};

 

2. 객체 생성자

new Object() 구문을 사용하여 객체를 생성할 수 있다. 이후 점 표기법 또는 대괄호 표기법을 사용하여 속성을 추가한다.

const person = new Object();
person.name = 'Bak';
person.age = 25;
person.greet = function() {
    console.log('Hello, my name is ' + this.name);
};

 

3. 생성자 함수

특정 구조를 가진 객체를 반복해서 생성할 때 유용하다.

function Person(name, age) {
    this.name = name;
    this.age = age;
    this.greet = function() {
        console.log('Hello, my name is ' + this.name);
    };
}

const alice = new Person('Bak', 25);

 

4. 클래스(ES6 이후)

클래스를 사용하면 객체 지향 프로그래밍의 패턴을 쉽게 구현할 수 있다.

class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    greet() {
        console.log('Hello, my name is ' + this.name);
    }
}

const alice = new Person('Alice', 25);

 

객체 사용법 

const game = {
    company: 'Bethesda',
    title: 'Fallout',
    year: 1997
};

// 속성 접근
console.log(game.company); // 'Bethesda'
console.log(game['title']); // 'Fallout'

// 속성 수정
game.year = 2008;
console.log(game.year); // 2008

// 속성 추가
game.series = 'Fallout : New Vegas';
console.log(game.series); // 'Fallout : New Vegas'

// 속성 삭제
delete company.title;
console.log(company.title); // undefined

 

배열 (Array)

배열은 자바스크립트에서 여러 개의 데이터를 순차적으로 저장할 수 있는 자료구조이다. 동일한 타입 또는 서로 다른 타입의 데이터를 모두 저장할 수 있으며 각 데이터는 인덱스로 접근할 수 있다.

 

특징

인덱스 기반

배열의 각 요소는 0부터 시작하는 인덱스를 통해 접근할 수 있다.

 

동적 크기

자바스크립트의 배열은 동적 크기를 가지며, 요소를 추가하거나 제거할 수 있다.

 

다양한 데이터 타입

배열의 요소는 숫자, 문자열, 객체, 다른 배열 등 어떤 데이터 타입도 될 수 있다.

 

배열 생성 방법

1. 배열 리터럴 

가장 일반적인 배열 생성 방법으로 대괄호를 사용하여 배열 요소를 정의한다.

const numbers = [1, 2, 3, 4, 5];
const mixedArray = [1, 'two', {three: 3}, [4, 5]];

 

2. 배열 생성자

new Array() 구문을 사용하여 배열을 생성할 수 있다.

const numbers = new Array(1, 2, 3, 4, 5);
const emptyArray = new Array(10); // 길이가 10인 빈 배열 생성

 

배열 사용법

const fruits = ['apple', 'banana', 'cherry'];

// 요소 접근
console.log(fruits[0]); // 'apple'
console.log(fruits[1]); // 'banana'

// 요소 수정
fruits[1] = 'blueberry';
console.log(fruits[1]); // 'blueberry'

 

인덱스로 배열의 요소에 접근하여 사용할 수 있다.

 

 

배열 메서드

자바스크립트 배열은 다양한 메서드를 통해 요소를 추가, 제거, 탐색, 변형할 수 있다.

 

1. push

push 메서드는 배열의 끝에 하나 이상의 요소를 추가하고 배열의 새로운 길이를 반환한다.

const fruits = ['apple', 'banana'];
let len = fruits.push('orange');
console.log(len); // 3
console.log(fruits); // ['apple', 'banana', 'orange']

.

2. pop

pop 메서드는 배열의 마지막 요소를 제거하고 그 요소를 반환한다.

const fruits = ['apple', 'banana', 'orange'];
const lastFruit = fruits.pop();
console.log(fruits); // ['apple', 'banana']
console.log(lastFruit); // 'orange'

 

push & pop 사용 예제

const fruits = ['apple', 'banana'];

// push: 배열 끝에 요소 추가
fruits.push('cherry');
console.log(fruits); // ['apple', 'banana', 'cherry']

// pop: 배열 끝의 요소 제거
const lastFruit = fruits.pop();
console.log(fruits); // ['apple', 'banana']
console.log(lastFruit); // 'cherry'

 

3. map

map 메서드는 배열 내의 모든 요소에 대해 제공된 함수를 호출한 결과로 새로운 배열을 생성한다.

const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
const numbers = [1, 2, 3, 4, 5];
const squared = numbers.map(num => num * num);
console.log(squared); // [1, 4, 9, 16, 25]

 

4. filter

filter 메서드는 주어진 함수의 테스트를 통과하는 모든 요소를 모아 새로운 배열로 반환한다.

const numbers = [1, 2, 3, 4, 5];
const evens = numbers.filter(num => num % 2 === 0);
console.log(evens); // [2, 4]

 

5. reduce

reduce 메서드는 배열의 각 요소에 대해 주어진 reducer 함수를 실행하여 단일 값을 반환한다.

const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
console.log(sum); // 15

 

reduce는 배열의 각 요소를 순회하며, 콜백 함수의 결과를 누산 하여 단일 값을 생성한다. 여기서 

(acc, curr) => acc + curr는 콜백함수로 두 개의 주요 인수를 받는다.

 

acc : 이전 콜백에서 반환된 값으로 이전 인덱스까지 누산 된 값

curr : 현재 순회 중인 인덱스의 요소

 

(콜백함수), 0 : 0이 초기값으로 이 값을 시작으로 요소의 값들이 누산 되기 시작한다.

 

공식 문법은 다음과 같다.

arr.reduce(callback(accumulator, currentValue, currentIndex, array), initialValue)

 

실제 필요한 인수들이 다양한 방식으로 표기가 가능한데 그 이유는 자바스크립트의 함수 인수 처리 방식 때문이다.

 

자바스크립트에서는 함수가 호출될 때 정의된 인수보다 더 많은 인수가 전달되면 무식하고, 더 적은 인수가 전달되면 undefined로 처리한다.

 

따라서 reduce 메서드에서 필요한 인수만이 알아서 사용된다.

 

따라서 생략형 콜백에서는 첫 번째와 두 번째 인수만 사용하며 두 값을 합한 결과만 리턴하기로 한다.

 

이 과정이 반복되면서 전체 요소의 합산이 되는 것이다.

 

모든 인수를 적용해서 호출하면 다음과 같다.

const numbers = [1, 2, 3, 4, 5];

const sum = numbers.reduce((accumulator, currentValue, currentIndex, array) => {
    console.log('Accumulator:', accumulator);
    console.log('Current Value:', currentValue);
    console.log('Current Index:', currentIndex);
    console.log('Array:', array);
    return accumulator + currentValue;
}, 0);

console.log('Sum:', sum); // Sum: 15

 

인수 (accumulator, currentValue, currentIndex, array) => {콜백 함수} : 요소의 누산 값을 반환

auccumulator : 누산값

currentVlaue : 현재 요소값

currnetIndex : 현재 인덱스

array : 참조 배열

 

 

 

728x90
반응형

함수

함수는 자바스크립트에서 재사용 가능한 코드 블록이자 일종의 객체이다. 따라서 속성도 가질 수 있으며 그 속성의 값이 변수라면 property, 함수라면  method가 된다.

 

함수는 선언, 호출, 매개변수, 반환값으로 구성된다.

 

함수 선언

// 선언
function greet(name) { // name : 매개변수
    return `Hello, ${name}!`; // 반환값
}
console.log(greet('Alice')); // "Hello, Alice!" // 호출

 

함수 표현식

함수 선언과 달리 변수에 할당되는 방식으로 정의되는 함수이다.

let greet = function(name) {
    return `Hello, ${name}!`;
};
console.log(greet('Bob')); // "Hello, Bob!"

 

let greet : let 변수로 greet라는 이름의 변수를 선언한다.

function(name)  : 매개변수 name을 받는 함수를 정의한다.

{ return ~ } : 함수의 본문으로 'Hello, ${name}!'을 반환한다. 템플릿 리터럴을 사용하여 매개변수 name의 값을 포함한 문자열로 반환한다.

 

화살표 함수

ES6부터 도입된 것으로 함수를 간결하게 표현하는 문법이다.

let greet = (name) => `Hello, ${name}!`;
console.log(greet('Charlie')); // "Hello, Charlie!"

 

함수 키워드와 이름이 생략되고 전달받을 매개변수만 괄호 안에 표기한다.

함수의 본문을 작성하는 중괄호와 반환 키워드가 생략되고 본문을 바로 표기할 수 있다.

728x90
반응형

'Program Language > JavaScript' 카테고리의 다른 글

JavaScript #14 ES6+ 문법  (4) 2024.07.23
JavaScript #13 DOM(Document Object Model)  (0) 2024.07.23
JavaScript #10 조건문과 반복문  (1) 2024.07.23
JavaScript #9 데이터 타입  (9) 2024.07.22
JavaScript #8 클로저(Closure)  (0) 2024.07.22

조건문 (Conditional Statements)

조건문은 특정 조건에 따라 코드 블록을 실행하거나 건너뛰는 방식으로 동작한다.

 

if, else if, else 

let x = 10;

if (x > 5) {
    console.log("x는 5보다 크다.");
} else if (x === 5) {
    console.log("x는 5이다.");
} else {
    console.log("x는 5보다 작다.");
}

 

switch

하나의 변수를 다양한 값과 비교하여 코드 블록을 실행한다.

let day = 3;
let dayName;

switch (day) {
    case 0:
        dayName = 'Sunday';
        break;
    case 1:
        dayName = 'Monday';
        break;
    case 2:
        dayName = 'Tuesday';
        break;
    case 3:
        dayName = 'Wednesday';
        break;
    case 4:
        dayName = 'Thursday';
        break;
    case 5:
        dayName = 'Friday';
        break;
    case 6:
        dayName = 'Saturday';
        break;
    default:
        dayName = 'Invalid day';
}

console.log(dayName); // "Wednesday"

 

break는 해당 조건에 해당하면 더 이상 다음 조건을 확인하지 않고 조건문을 종료시키는 기능을 한다.

case는 순서대로 모든 케이스를 확인하기 때문에 break가 없다면 swich의 모든 조건을 확인한다.

반복문 (Loops)

반복문은 특정 코드 블록을 여러 번 실행하는 데 사용된다. 

 

for

일반적인 반복문이다.

for (let i = 0; i < 5; i++) {
    console.log(i); // 0, 1, 2, 3, 4
}

 

 

while

조건이 참인 동안 코드 블록을 반복한다.

let i = 0;
while (i < 5) {
    console.log(i); // 0, 1, 2, 3, 4
    i++;
}

 

do-while

코드 블록을 최소 한 번 실행하고, 그 후 조건이 참인 동안 반복한다.

let i = 0;
do {
    console.log(i); // 0, 1, 2, 3, 4
    i++;
} while (i < 5);

 

break

break는 반복문을 종료시키는 기능을 한다.

for (let i = 0; i < 10; i++) {
    if (i === 5) {
        break;
    }
    console.log(i); // 0, 1, 2, 3, 4
}

 

continue

continue 문은 현재 반복을 종료하고 다음 반복으로 넘어간다.

for (let i = 0; i < 10; i++) {
    if (i === 5) {
        continue;
    }
    console.log(i); // 0, 1, 2, 3, 4, 6, 7, 8, 9
}

 

728x90
반응형

'Program Language > JavaScript' 카테고리의 다른 글

JavaScript #13 DOM(Document Object Model)  (0) 2024.07.23
JavaScript #11 함수  (0) 2024.07.23
JavaScript #9 데이터 타입  (9) 2024.07.22
JavaScript #8 클로저(Closure)  (0) 2024.07.22
JavaScript #7 var 변수에 대해서  (0) 2024.07.22

기본 데이터 타입

숫자(Number) 

정수 및 부동 소수점 숫자를 포함한다.

let integer = 42;
let float = 3.14;

 

숫자 조작

산술 연산자를 사용해 숫자를 조작할 수 있다.

let sum = 10 + 5; // 15
let difference = 10 - 5; // 5
let product = 10 * 5; // 50
let quotient = 10 / 5; // 2
let remainder = 10 % 5; // 0

 

문자열(String)

텍스트 데이터를 나타낸다.

let singleQuoteString = 'Hello, world!';
let doubleQuoteString = "Hello, world!";
let templateString = `Hello, ${name}!`;

 

문자열 조작

연결 연산자를 사용해 문자열을 조작할 수 있다.

let greeting = 'Hello' + ' ' + 'world'; // "Hello world"

 

템플릿 리터럴

let name = 'Alice';
let message = `Hello, ${name}!`; // "Hello, Alice!"

 

불리언(Boolean)

참 또는 거짓 값을 가진다.

let isTrue = true;
let isFalse = false;

 

값과 조건 판단

let isAdult = true;
if (isAdult) {
    console.log('You are an adult.');
} else {
    console.log('You are not an adult.');
}

 

null

값이 비어 있음을 나타낸다.

let emptyValue = null;

 

undefiend

값이 정의되지 않았음을 나타낸다.

let undefinedValue;

 

null과 undefined의 차이

null : 명시적으로 값이 없음을 나타내기 위해 사용된다.

undefined : 변수가 선언되었지만 값이 할당되지 않았을 때의 초기 상태를 나타낸다.

 

let a = null;
let b;

console.log(a); // null
console.log(b); // undefined

 

객체(Object)

키-밸류 쌍의 집합, 배열도 객체의 일종이다.

let person = {
    name: 'John',
    age: 30
};
let array = [1, 2, 3];

 

객체와 배열의 초기화 및 조작

객체 초기화

let car = {
    make: 'Toyota',
    model: 'Camry',
    year: 2020
};

 

배열 초기화

let numbers = [1, 2, 3, 4, 5];

 

객체 속성 접근 및 조작

console.log(car.make); // "Toyota"
car.year = 2021;
console.log(car.year); // 2021

 

데이터 타입 확인

typeof 연산자를 사용하여 데이터 타입을 확인할 수 있다.

console.log(typeof 42); // "number"
console.log(typeof 'Hello'); // "string"
console.log(typeof true); // "boolean"
console.log(typeof null); // "object" (자바스크립트의 역사적 이유로)
console.log(typeof undefined); // "undefined"
console.log(typeof {name: 'John'}); // "object"

 

데이터 타입 간의 변환

자바스크립트에서는 데이터 타입 간의 변환이 자주 발생한다. 이를 명시적 변환과 암시적 변환으로 나눌 수 있다.

 

명시적 변환(Explicit Conversion)

숫자로 변환 

Number(), parseInt(), parseFloat()

let str = "123";
let num = Number(str); // 123
let int = parseInt("123.45"); // 123
let float = parseFloat("123.45"); // 123.45

 

문자열로 변환

String()

let num = 123;
let str = String(num); // "123"

 

불리언으로 변환

Boolean()

let str = "";
let bool = Boolean(str); // false

 

암시적 변환(Implicit Conversion)

숫자와 문자열의 연산에서 발생

let result = 123 + "456"; // "123456" (숫자가 문자열로 변환됨)

 

728x90
반응형

'Program Language > JavaScript' 카테고리의 다른 글

JavaScript #11 함수  (0) 2024.07.23
JavaScript #10 조건문과 반복문  (1) 2024.07.23
JavaScript #8 클로저(Closure)  (0) 2024.07.22
JavaScript #7 var 변수에 대해서  (0) 2024.07.22
JavaScript #6 호이스팅(Hoisting)  (0) 2024.07.21

클로저

클로저는 자바스크립트의 중요한 개념 중 하나로 함수와 그 함수가 선언된 렉시컬 환경(Lexical Environment)의 조합을 의미한다. 클로저는 함수가 선언될 때 그 함수의 스코프에 있는 변수들을 기억하고, 함수가 호출될 때에도 그 변수를 참조할 수 있게 한다.

 

렉시컬 환경(Lexical Environment)

자바스크립트의 실행 컨텍스트에서 변수와 함수 선언의 스코프를 관리하는 내부 구조를 의미한다. 렉시컬 환경은 코드가 작성된 위치에 따라 스코프를 결정하는데, 이는 코드가 실행될 때가 아니라 작성될 때의 구조에 따라 스코프가 결정된다는 점이 중요하다.

 

렉시컬 환경의 구성 요소

렉시컬 환경은 두 가지 구성 요소로 이루어져 있다.

 

1. 환경 레코드(Environment Record)

현재 스코프에서 정의된 모든 변수와 함수 선언을 저장한다.

 

2. 외부 렉시컬 환경 참조(Outer Lexical Environment Reference)

외부 스코프에 대한 참조를 가진다. 이는 중첩된 함수에 내부 함수가 외부 함수의 변수에 접근할 수 있게 한다.

 

렉시컬 환경의 동작

function outerFunction() {
  let outerVariable = "I am an outer variable";

  function innerFunction() {
    let innerVariable = "I am an inner variable";
    console.log(outerVariable); // "I am an outer variable"
    console.log(innerVariable); // "I am an inner variable"
  }

  return innerFunction;
}

const closureFunction = outerFunction();
closureFunction();

 

outerFunction 실행 시

환경 레코드 : { outerVariable: "I am an outer variable" }

외부 렉시컬 환경 참조 : 전역 환경

 

innerFunction 실행 시

환경 레코드 : { innerVariable : "I am an inner variable" }

외부 렉시컬 환경 참조 : outerFunction의 렉시컬 환경

 

렉시컬 환경의 예제

let globalVariable = "I am a global variable";

function exampleFunction() {
  let localVariable = "I am a local variable";

  function innerFunction() {
    console.log(globalVariable); // "I am a global variable"
    console.log(localVariable); // "I am a local variable"
  }

  innerFunction();
}

exampleFunction();

 

전역 렉시컬 환경

환경 레코드 : { globalVariable : "I am a global variable" }

외부 렉시컬 환경 참조 : 없음

 

exampleFunction 실행 시 

환경 레코드 : { localVariable : "I am a local variable" }

외부 렉시컬 환경 참조 : 전역 환경

 

innerFunction 실행 시 

환경 레코드 : 비어 있음 (현재 함수에서 변수를 선언하지 않음)

외부 렉시컬 환경 참조 : exampleFunction의 렉시컬 환경

 

렉시컬 환경은 자바스크립트의 변수와 함수 스코프를 관리하는 내부 구조로 코드가 작성된 위치에 따라 스코프가 결정된다. 렉시컬 환경은 환경 레코드와 외부 렉시컬 환경 참조로 구성되어 있으며 이를 통해 함수가 선언된 위치의 변수에 접근할 수 있다. 클로저는 이러한 렉시컬 환경을 활용하여 함수가 실행된 이후에도 외부 변수에 접근할 수 있게 하는 개념이다.

 

 

클로저의 동작 원리

클로저는 함수가 정의된 시점의 외부 스코프의 변수에 접근할 수 있는 기능을 제공하는 함수이다. 

즉, 클로저는 함수가 생성될 때 외부 환경의 변수를 캡처하여 함수가 실행될 때에도 그 변수에 접근할 수 있게 한다.

 

자바스크립트에서 함수는 선언될 때마다 자신이 선언된 환경을 기억한다. 이 환경은 함수가 생성된 시점의 스코프와 변수를 포함하며 클로저는 함수가 실행된 이후에도 해당 환경을 유지하므로, 함수가 반환된 이후에도 그 환경에 접근할 수 있다.

 

function outerFunction() {
  let outerVariable = "I am an outer variable";

  function innerFunction() {
    console.log(outerVariable); // "I am an outer variable"
  }

  return innerFunction;
}

const closureFunction = outerFunction();
closureFunction(); // "I am an outer variable"

 

innerFunction은 outerFunction 내부에서 선언되었으며 outerFunction의 스코프에 접근할 수 있다. outerFunction이 반환된 이후에도 innerFunction은 outerVariable에 접근할 수 있으며 이 경우 innerFunction은 클로저를 형성한다.

 

클로저의 주요 특징

함수가 생성될 때의 스코프를 기억

함수가 정의된 위치의 외부 변수를 기억하고, 함수가 호출될 때 해당 변수에 접근할 수 있다.

 

은닉화(Encapsulation)

클로저를 사용하여 함수 외부에서 접근할 수 없는 프라이빗 변수를 만들 수 있다.

 

고차 함수

클로저는 함수를 반환하거나 함수의 인수로 함수를 받을 수 있는 '고차 함수'와 함께 자주 사용된다.

 

프라이빗 변수(Private Variable)

변수를 캡슐화하여 외부에서 변수에 직접 접근할 수 없도록 만드는 패턴이다.

예제 1)

function createCounter() {
  let count = 0;

  return {
    increment: function() {
      count++;
      console.log(count);
    },
    decrement: function() {
      count--;
      console.log(count);
    },
    getCount: function() {
      return count;
    }
  };
}

const counter = createCounter();
counter.increment(); // 1
counter.increment(); // 2
counter.decrement(); // 1
console.log(counter.getCount()); // 1

 

count 변수는 createCounter 함수 내에 선언되어 있으며, createCounter 함수 외부에서도 직접 접근할 수 없으며, increment, decrement, getCount 메서드만이 count 변수에 접근할 수 있다.

 

예제 2)

function factory_game(title) {
  return
   {
   
    get_title : function () {
      return title;
    },
    
    set_title : function(_title) {
      title = _title 
    }
  }
}


factorty_game 함수는 내부에 get_title, set_title 두 개의 내부 함수를 가지고 있다.


외부함수의 인수 title은 함수 안에서 지역변수로 사용되며 첫 번째 속성인 get_title 메서드가 반환하는 title은 인수의 값이 된다.

 

set_title은 매개변수로 _title을 가지며 이 매개변수를 가지고 title = _title로 저장하여 외부함수의 인수가 title이 된다.

 

elder_scroll = factory_movie('The Elder Scrolls V: Skyrim');
fallout = factory_movie('Fallout: New Vegas');

console.log(elder_scroll.get_title());
// 'The Elder Scrolls V: Skyrim' 출력
console.log(fallout.get_title());
// 'Fallout: New Vegas' 출력

 

 

부분 적용 함수(Partial Application)

클로저를 사용하여 함수의 일부 인수를 고정한 새로운 함수를 생성할 수 있으며, 이는 함수의 재사용성을 높이고, 코드의 간결성을 유지하는 데 유용하다.

 

function multiply(a) {
  return function(b) {
    return a * b;
  };
}

const double = multiply(2);
console.log(double(5)); // 10

const triple = multiply(3);
console.log(triple(5)); // 15

 

주의사항

메모리 관리

클로저는 함수가 참조하는 변수들을 계속 유지하므로, 과도하게 사용하면 메모리 누수가 발생할 수 있다. 불필요한 클로저 사용을 피하고 필요하지 않은 참조는 명시적으로 제거하는 것이 좋다.

 

성능

클로저를 사용할 때는 성능에 주의가 필요하다. 특히 큰 데이터 구조를 포함하는 클로저는 메모리 사용량을 증가시킬 수 있다.

 

중첩 함수와 클로저

function makeCounter() {
  let count = 0;

  return function() {
    count++;
    return count;
  };
}

const counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3

 

위 렉시컬 환경은 다음과 같이 동작한다.

 

makeCounter 실행 시

환경 레코드 : { count : 0 }

외부 렉시컬 환경 참조 : 전역 환경

 

반환된 함수(클로저) 실행 시

환경 레코드 : 비어 있음

외부 렉시컬 환경 참조 : makeCounter의 렉시컬 환경 

 


 

클로저는 자바스크립트의 강력한 기능 중 하나로 함수가 선언된 렉시컬 환경을 기억하고, 함수가 호출될 때에도 그 환경에 접근할 수 있게 한다. 이를 통해 변수 은닉화, 부분 적용 함수, 콜백 함수 등 다양한 활용이 가능하다.

 

클로저의 주의사항을 유념하여 적절히 사용하면 코드의 재사용성과 유지보성을 높일 수 있다.

728x90
반응형

+ Recent posts