함수는 하나의 단위로 실행할 수 있도록 코드! 즉, 명령문을 그룹화한 것이다.
함수를 생성하는 방법에는 두 가지 방식이 있다.
함수 선언식함수 표현식함수는 function 이라는 예약어와 함수이름(sayHello), 함수블록({ ... }) 으로 이루어져 있다.
function sayHello(name, age){
console.log(`이름은 ${name}이고, 나이는 ${age} 입니다`)
};
sayHello('이서연', 25);
자바스크립트에서 함수는 객체이다. 즉 함수를 변수에 담아 생성할 수 있다. 위에 sayHello 를 예시로 들어보자
const sayHello = function (name,age) {
console.log(`이름은 ${name}이고, 나이는 ${age} 입니다`)
};
sayHello('이서연', 25);
그럼 함수 선언식과 함수 표현식은 뭐가 다른 것일까 ?!
두 가지의 차이는 함수 선언식은 호이스팅의 영양을 받지만, 함수 표현식은 호이스팅의 영향을 받지 않는다.
괄호() 안에 파라미터(또는 매개변수)를 선언하여, 함수 호출시 값을 전달할 수 있다.function
sayHello(name, age){
console.log(`이름은 ${name}이고, 나이는 ${age} 입니다`)
};
sayHello('이서연', 25);
위의 코드와 같이 sayHello()함수에 name과 age를 매개변수로 받아서 출력하는 것을 확인할 수 있다.
ES6에서는 매개변수에 기본값(default value)를 지정하는 기능도 추가되었다.
function sum(num1, num2) {
const result = num1 + num2;
console.log('결과값: ', result);
}
위의 코드에서 sum(5); 를 출력하면 어떻게 될까?
--> sum(5);를 호출하면 num1에는 5가 할당되고, num2에는 undefined가 할당된다.
이 경우 num1 + num2는 NaN(Not a Number)이 됩니다. 그리고 결과값은 "결과값: NaN"으로 출력된다.
이에, 기본값을 지정하면 함수가 예기치 않은 NaN 결과를 방지할 수 있다.
function sum(num1, num2 = 10) {
const result = num1 + num2;
console.log('결과값: ', result);
}
이렇게 num2의 값에 10을 기본값으로 할당해두면,
sum(5); 를 출력하면 num1에 5, num2에 10이 지정되어 15가 출력되는 것을 확인할 수 있다.
return 키워드를 사용하여 함수에서 값을 반환할 수 있다.
function sum(number1, number2) {
return number1 + number2;
}
let result = sum(5, 3);
console.log('result: ', result); // result: 8
--> ES6에서 추가된 화살표 표기법(Arrow Function)이다. 자주 사용되니 꼭 익혀둘 것!
function sayHello(name, age){
console.log(`이름은 ${name}이고, 나이는 ${age} 입니다`)
};
위에서 작성했던 이 코드를 화살표 함수로 변경해보자
const sayHello = (name, age) =>{
consoe.log.log(`이름은 ${name}이고, 나이는 ${age} 입니다`)
};
로 변경할 수 있다.
function sum(num1, num2 = 10) {
const result = num1 + num2;
console.log('결과값: ', result);
}
이 코드도 화살표 함수로 변경해보자
const sum = (num1, num2 = 10) => {
return num1 + num2
}
sum에 함수를 할당하며, 화살표 함수로 간결하게 표현이 가능하다.
여기서 return문이 하나라면, 아래와 같이 더 간결하게 {} 중괄호를 제외하고 사용할 수 있다.
const sum = (num1, num2 = 10) => num1 + num2
매개변수가 하나일 때는?
const print = name => console.log(`${name} 님 안녕하세요}`)
코드에서처럼 매개변수가 하나이므로 소괄호를 생략하고, 함수 내용이 한 줄인 경우에는 중괄호와 return 키워드도 생략할 수 있다 :)
객체는 데이터의 집합으로, 다양한 유형의 정보를 포함할 수 있습니다. 객체는 중괄호 {}를 사용하여 정의되며, 속성(프로퍼티)과 메서드(함수)를 포함할 수 있다.
프리미티브 타입(Primitive Type)은 단 하나의 값만 나타낼 수 있고 불변이지만, 이와 달리 객체는 여러가지 값이나 복잡한 값을 나타낼 수 있으며, 값(내용물)이 변할 수도 있다.
객체는 키(key)와 값(value)으로 구성되어 있다. --> 키 : 값
const person = {
name: '이서연',
age: 25,
sayHello() {
console.log(`안녕하세요. ${this.name}님`);
}
};
여기서 name이 키, '이서연'이 값이 된다.
또한, 위와 같이 객체는 여러가지 값을 가질 수 있다. 그리고 객체가 가지고 있는 값을 프로퍼티(Property) 라고 하며, 프로퍼티가 함수인 경우 즉! 객체가 가지고 있는 함수를 메서드라고 한다.
sayHello () {
console.log(`안녕하세요. ${this.name}님`);
}
sayHello : function () {
console.log(`안녕하세요. ${this.name}님`);
}
두가지 방식으로 작성할 수 있다.
객체의 프로퍼티로는 숫자, 문자, 불리언 등과 같은 프리미티브 타입이 될 수도 있고, 메서드를 가질 수도 있고 배열, 객체와 같이 또 다른 객체를 프로퍼티로 가질 수 있다.
const parent = {
name: '이아빠',
age: 55
};
const child = {
name: '이서연',
age: 25,
parent: parent,
hobbies: ['헬스', '코딩'],
major: {
'아동학과': '보육교사',
'컴공': '프론트앤드 개발자'
}
};
위와 같이, 객체 안에는 다른 객체를 넣을 수 있으며, 배열을 넣을 수 있다.
console.log(child.parent.name); // '이아빠'
console.log(child.parent.age); // 55
와 같이 접근할 수 있다.
객체를 생성하는 방법에는 크게 객체 리터럴 방식과 객체 생성자 방식이 있습니다.
객체 리터럴 문법 (object literal syntax)
객체 리터럴 방식은 중괄호 {} 안에 key: value를 쉼표(,)로 구분하여 만들 수 있다.
const obj = {
name: '이서연', // name이 키(key)이고, '이서연'이 값(value) 이다.
age: 25
};
// 또는
const obj = {};
obj.name = '이서연';
obj.age = '25';
객체 생성자 문법 (object constructor syntax)
new 라는 연산자와 기본 내장 객체인 Object를 사용하여 객체를 생성할 수 있다.
const person = new Object();
person.name = '이서연';
person.age = 25;
person.job = '예비 개발자';
--> new 연산자는 사용자 정의 객체 타입 또는 내장 객체 타입의 인스턴스를 생성한다.
객체 안에 있는 프로퍼티나 함수에 접근하기 위에서는 크게 두 가지 방법이 있다.
const person = {
name: ['Lee', 'Pyo'],
age: 32,
gender: 'male',
interests: ['music', 'health'],
hello: function () {
alert('Hello!');
},
greeting: function () {
alert('Hi!');
},
hobby: {
name: 'programing',
alert: function () {
alert('programing');
},
},
};
점표기법 - 예) 객체.key
person.name
person.age
person.gender
괄호 표기법 - 예) 객체['key']
person['name']
person['age']
person['gender']
프로퍼티 생성
const person = {};
person.name = '이서연';
person.age = 20;
person.hobby = '헬스';
프로퍼티 제거
delete 연산자는 객체 프로퍼티를 제거한다.
delete person.name;
이런 식으로 제거하면 된다
함수 파라미터로 객체를 전달하면 코드를 클린하게 만들 수 있다.
함수 파라미터가 많아질 경우 코드가 복잡해 질 수 있습니다.
function printPerson(name, age, hobby) {
console.log(`제 이름은 ${name}이며, 나이는 ${age}살 입니다.그리고 취미는 ${hobby} 입니다.`);
}
printPerson('아기', 10, '헬스');
이런 함수가 있다고 가정해보자.
함수를 호출하기 위해, printPerson('아기', 10, '헬스') 와 같이 값을 할당해 함수를 호출했다.
함수 파라미터에 들어갈 값들이 많아진다면 ?
함수를 호출할 때, 넣어야 할 값도 많아진다
const person = {
name: '아기',
age: 10,
hobby: '헬스'
};
function printPerson(person) {
console.log(`제 이름은 ${person.name}이며, 나이는 ${person.age}살 입니다. 그리고 취미는 ${person.hobby}입니다.`);
}
printPerson(person);
함수 파라미터에 객체를 전달함으로써 코드를 클린하게 그리고 편리하게 전달할 수 있다.
구조 분해 할당 문법은 배열이나 객체의 속성을 해체하여 그 값을 개별 변수에 담을 수 있게 하는 자바스크립트 표현식이다.
const person = {
name: '아기',
age: 10,
hobby: '헬스'
};
const name = person.name;
const age = person.age;
const hobby = person.hobby;
const { name, age, hobby} = person;
요렇게 객체의 경우, 객체 타입으로 받으면 된다. (배열 타입일 경우 배열 타입으로 받으면 된다!)
const person = {
name: '아기',
age: 10,
hobby: '헬스'
};
const { name, age, hobby} = person;
function printPerson({name, age, hobby) {
console.log(`제 이름은 ${name}이며, 나이는 ${age}살 입니다. 그리고 취미는 ${hobby}입니다.`);
}
printPerson(person);
자바스크립트로 프로그래밍을 하다보면 같은 유형의 객체를 여러번 생성해야 하는 일이 생길 수 있다.
같은 유형의 객체를 반복해서 생성
const person1 = {
name: '짐코딩',
age: 10,
hobby: '헬스'
};
const person2 = {
name: '홍길동',
age: 20,
hobby: '축지법'
};
const person3 = {
name: '도깨비',
age: 30,
hobby: '도술'
};
이와 같이 person을 정의하려고 할 때, 사람마다 모두 이렇게 작성한다면, 코드가 엄청 길어지지 않을 까?
이렇게 객체 리터럴로 반복적으로 생성하다 보면 코드도 반복되는 코드가 많아져 가독성이 떨어지고, 타이핑도 많이 해야하는 번거로움이 생길 수 있는데 팩토리 함수를 사용하여 이러한 번거로움을 해결할 수 있다.
팩토리 함수 사용
function createPerson(name, age, hobby) {
return {
name : name,
age : age,
hobby : hobby
}
}
const person1 = createPerson('이서연', 25, '헬스');
const person2 = createPerson('표투식', 27, '헬스');
위의 코드를 살펴보면서 알 수 있다 시피, 팩토리 함수는 다른 함수나 객체를 반환하는 함수를 말한다.
주로 반복적으로 비슷한 객체를 생성할 때 사용된다. 팩토리 함수를 사용하면 객체 생성을 추상화하여 코드를 간결하고 재사용 가능하도록 만들 수 있다.
ES6 단축 속성명
객체에서 key와 value가 동일할 때는 Property shorthand(단축 속성명)을 사용할 수 있다. 단축 속성명은 객체를 정의 할 때 key값과 value값이 같으면 key와 value를 각각 표기하지 않고 한번만 표기하는 것을 말한다.
function createPerson(name, age, hobby) {
return {
name,
age,
hobby
};
};
함수 클래스를 사용한 객체 생성 (ES5)
자바스크립트에서는 함수로 만든 클래스를 사용하여 객체를 생성할 수 있다.
function Person(name, age, hobby) {
this.name = name;
this.age = age;
this.hobby = hobby;
}
const person = new Person('이서연', 25, '헬스');
console.log(person)
클래스를 사용한 객체 생성 (ES6)
class Person {
constructor(name, age, hobby) {
this.name = name;
this.age = age;
this.hobby = hobby;
}
}
const person = new Person('이서연', 25, '헬스');
console.log(person)
프리미티브 타입이 아닌 것들을 객체타입 또는 참조타입 이라고 한다.
프리미티브 타입 값을 다른 변수에 할당 하면 프리미티브 타입은 값 자체를 넘겨주기 때문에 값의 원형이 변경되지 않는다. (원시타입)
let username = '이서연';
let username2 = username;
username2 = '표원식';
console.log(username); // '이서연'
console.log(username2); // '표원식'
하지만 객체 타입을 다른 변수에 할당하면 참조값이 할당 되기 때문에 할당 받은 변수에서 속성을 변경하면 값의 원형이 변경된다. (참조타입)
let person = {
name : '이서연',
age : 25
};
let person1 = person;
person1.name = '투서연'
console.log(person);
consple.log(person1)
//출력값
{ name: '투서연', age: 25 }
{ name: '투서연', age: 25 }
person 변수는 객체를 참조한다. 그리고 person1 변수는 동일한 객체를 참조하게 된다. 그러므로 person과 person1은 같은 객체를 가리키고 있다. 이 때문에 person1에서 객체의 속성을 변경하면 person에서도 동일한 변경이 반영된다.
함수 호출시에 파라미터로 프리미티브 타입을 넘기는 것과 객체 타입을 넘기는 것은 어떠한 차이가 있을까요?
function changeName (username) {
username = '홍길동'
}
const name = '이서연';
changeName(name);
console.log(name);
함수에 인자로 전달되는 것은 변수의 값(또는 객체에 대한 참조)이므로, 함수 내에서 매개변수에 할당된 값은 외부 변수의 복사본을 의미한다.
Call By Value는 값 자체를 username에 할당하기 때문에 username값을 변경해도 함수밖의 name은 변경되지 않는다. 즉, 값이 복사된 것을 의미한다.
참조에 의한 호출"은 함수 호출 방식 중 하나로, 함수에 인자로 전달된 값이 변수의 참조(주소)를 전달하는 방식이다. 이는 함수 내에서 인자로 전달된 변수를 직접 조작할 수 있게 해준다.
const person = {
name: '이서연',
age: 25
};
function changeName(people) {
people.name = '표원식';
people.age = 27;
}
changeName(person);
console.log('name:', person.name); // '표원식' 출력
console.log('age:', person.age); // 27 출력
Spread는 펼치다라는 뜻으로, Spread operator는 객체를 할당할 때 참조값을 할당 하는게 아닌 객체안의 프로퍼티를 펼치는 방식으로 할당할 수 있다. ... 표기법을 사용하여 사용할 수 있다.
let obj = {
name: '토끼',
age: 2
};
let person = {
...obj,
hobby: '당근먹기'
};
console.log('person: ', person);
// [출력]
// person: {name: '토끼', age: 2, hobby: '당근먹기'}
이와 같이 person.name 값을 변경해도 obj.name의 값은 변하지 않는 것을 확인할 수 있다.
이처럼, 스프레드 구문을 사용하면, 참조갑 공유가 아닌, 새로운 객체가 생성되는 것을 확인할 수 있다!
값을 추가할 때에도
기존의 값은 변경되지 않는 것을 확인할 수 있다.
Call By Reference에서 봤었던 것처럼 객체는 쉽게 복사 되지 않는다.
참조값 복사가 아닌, 객체를 완전히 복사하는 방법은 무엇이 있을 까?
Spread operator를 이용한 복사
전개 구문은 참조값을 할당하는 방식이 아닌 객체에서 속성을 펼치는(Spread) 방식이기 때문에 위에서 확인했듯이 결론적으로 객체 복사가 가능하다.

Object.assign()
자바스크립트 내장 객체인 Object의 assign()메서드를 사용하여 복사할 수 있다.

const obj2 = Object.assign({}, obj1); : Object.assign() 메서드를 사용하여 빈 객체 {}에 obj1의 속성을 복사하여 새로운 객체 obj2를 생성한다. 이를 통해 obj2는 obj1과 동일한 속성을 가지지만, 완전히 별개의 객체이다.
Object.assign() 메서드를 사용하여 객체를 복사하면 복사된 객체와 원본 객체는 완전히 독립적인 객체가 된다. 복사된 객체를 변경하더라도 원본 객체에는 영향을 주지 않는다.
객체(object)에 있는 키 항목들을 반복적으로 반환한다.
const person = {
name: 'LEE',
age: 25,
job: '예비 개발자'
};
for (let key in person) {
console.log(`key: ${key}, value: ${person[key]}`);
}
///출력값
key: name, value: LEE
key: age, value: 25
key: job, value: 예비 개발자
대부분의 경우
this의 값은 함수를 호출한 방법에 의해 결정된다.
this는 호출한 놈 입니다.- 호출한 놈이 없을 경우에는 기본값으로
window객체 이다.- 예외
- 전역스코프에서
this는 window 객체 이다.- 화살표 함수(Arrow Function)에서
this가 조금 달라진다.- Strict Mode에서는
this가 조금 달라진다.
콘솔 창에
console.log(this === window);
출력되는 것을 확인할 수 있다.
전역 스코프에서의 this 는 window 이다!
this는 호출한 놈대부분의 경우 this의 값은 함수를 호출한 방법에 의해 결정된다.
이게 무슨 말이냐?!
function printThis() {
console.log(this);
}
printThis(); // window 객체
let person1 = {
name: '이서연',
whoIsThis: printThis,
};
let person2 = {
name: '투서연',
whoIsThis: printThis,
};
let person3 = {
name: '쓰리서연',
whoIsThis: printThis,
};
person1.whoIsThis(); // person1 객체
person2.whoIsThis(); // person2 객체
person3.whoIsThis(); // person3 객체

처음 printThis()만 출력했을 때에는 window객체를 출력하지만,
각각의 객체 안에서 출력하자, 각각의 객체를 가리키는 것을 확인할 수 있다.
bind 함수로 this를 설정할 수 있다.

사용자 정의 객체 타입(ES6에서는 클래스)을 만들때 this의 사용한다. 이때 생성자는 this를 자신의 인스턴스로 정의한다.
function Person (name, age){
this.name = name;
this.age = age;
}
let person1 = new Person('김씨', 20);
console.log(person1)
thisArrow Function 내부에서의 this는 함수가 정의된 스코프에서의 this를 가져온다. 이것은 Lexical Scope를 따르는 것으로, 함수가 정의된 위치에서의 this를 물려받는다.
let person = {
name: '이서연',
printThis: () => {
console.log(this); // window 객체
};,
};
let person = {
name: '이서연',
printThis: function () {
setTimeout(() => {
console.log(this); // person 객체
});
},
};
화살표 함수 내부에서의 this는 해당 함수가 정의된 스코프에서의 this를 가져오므로, setTimeout 함수 내부의 화살표 함수에서의 this는 printThis 메소드가 호출된 person 객체를 가리킨다.
따라서 printThis 메소드를 호출하면 setTimeout 함수의 콜백이 비동기적으로 실행되고, 이 콜백 함수 내부에서의 this는 person 객체를 가리키게 된다.
화살표 함수를 사용하면 안되는 경우
let person = {
name: '이서연',
printThis: () => {
console.log(this); // window 객체 출력
}
};
this → window 객체를 가리키게 된다.--> addEventListener를 사용할 때 화살표 함수보다 일반 함수를 사용하는 것이 더 선호한다.
this 바인딩 : 화살표 함수는 자신만의 this를 가지지 않고, 외부 스코프의 this를 가져다 쓴다. 이것은 화살표 함수를 사용할 때 이벤트 리스너 내에서 this가 원하는 대상을 가리키지 않을 수 있다는 것을 의미한다. 반면에 일반 함수를 사용하면 this는 이벤트를 발생시킨 요소를 가리킨다.this엄격 모드(Strict Mode)에서는 호출한 놈이 없을 경우 기본값을 window로 하지 않고 undefined로 합니다!
즉 , 더 엄격하게 검사한다.
'use strict';
function printThis() {
console.log(this);
}
printThis(); // undefined
만약, 'use strict'가 없다면, this는 window를 가리키게 된다.
HTML에서 자바스크립트 파일을 사용할 때 <script>태그를 이용하여 자바스크립트 파일을 불러올 수 있다.
하지만 브라우저에서는 HTML 파일의 위에서 부터 아래로 순차적으로 파싱하기 때문에 HTML이 파싱되지 않은 상태에서 자바스크립트를 파싱할 경우 문제가 생길 수도 있다.
그 문제는 아래와 같이 자바스크립트로 HTML DOM 요소에 접근하려는 경우 이다.

위 코드를 실행하게되면 btn이 null이기 때문에 btn.addEventListener메서드 호출시점에 에러가 발생하는 것을 확인할 수 있다.
그 이유는 브라우저가 HTML을 파싱한 후에 자바스크립트에서 HTML요소를 제어할 수 있도록 DOM 객체를 생성하게 되는데, 위 코드는 HTML파싱도 하기전에 DOM요소에 접근하려고 했기 때문에 발생한 문제 이다.
(과거에 해결했던 방법에 대한 정리는 하지 않는다.
최근에 사용하는 방법만 다루겠다)
HTML5 script 로드 해결방법
HTML5에서는 defer async 속성을 통해 비동기 script 로드가 가능해져 문제가 해결되었다.
defer 속성 - HTML 파싱과 함께, 비동기로 JavaScript 파일을 불러온다.

async 속성 - HTML 파싱과 함께, 비동기로 JavaScript 파일을 불러온다.
defer 를 그림으로 살펴보면, 파싱이 다 끝난 뒤에 스크립트 실행을 시작하게 된다.
async의 경우, 스크립트 파일을 만났을 때, html 파일과 같이 파싱을 하다가, 실행을 해야 할 때, html의 파일이 일정부분 멈추는 것을 확인할 수 있다.
이로써, 스크립트 파일을 로드할 때에는 defer를 사용한다고 생각하면 될 것 같다!