JS-ES6 (자바스크립트 정리 3 )

짜스의 하루 ·2024년 5월 8일

함수

함수는 하나의 단위로 실행할 수 있도록 코드! 즉, 명령문을 그룹화한 것이다.

함수 생성

함수를 생성하는 방법에는 두 가지 방식이 있다.

  • 함수 선언식
  • 함수 표현식

함수 선언식

함수는 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

Arrow Function (중요!!)

--> ES6에서 추가된 화살표 표기법(Arrow Function)이다. 자주 사용되니 꼭 익혀둘 것!

Arrow Function 특징

  • function 예약어를 생략할 수 있다.
  • 함수에 매개변수(Parameter)가 단 하나 뿐이라면 괄호(())도 생략할 수 있다.
  • 함수 바디가 표현식 하나라면 중괄호와 return 문도 생략할 수 있다.
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 키워드도 생략할 수 있다 :)


Object

객체는 데이터의 집합으로, 다양한 유형의 정보를 포함할 수 있습니다. 객체는 중괄호 {}를 사용하여 정의되며, 속성(프로퍼티)과 메서드(함수)를 포함할 수 있다.

프리미티브 타입(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);

함수 파라미터에 객체를 전달함으로써 코드를 클린하게 그리고 편리하게 전달할 수 있다.

구조 분해 할당 (Distructuring assignment)

구조 분해 할당 문법은 배열이나 객체의 속성을 해체하여 그 값을 개별 변수에 담을 수 있게 하는 자바스크립트 표현식이다.

  • 일반 - 객체 속성을 개별 변수에 담을 때
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);
  • 구조 분해 할당을 사용하여 person 객체의 속성을 개별 변수로 가져온다. 이것은 중괄호({}) 안에 원하는 속성들을 명시하고, 할당할 객체(person)를 지정하여 수행된다. name, age, hobby 속성을 가져오고 있다.
  • 그 다음 함수 printPerson을 정의할 때, 매개변수로 객체를 받고 있다. 이 함수 내에서도 구조 분해 할당을 이용하여 함수의 매개변수로 전달된 객체의 속성들을 개별 변수로 가져오고 있다.

팩토리 함수

자바스크립트로 프로그래밍을 하다보면 같은 유형의 객체를 여러번 생성해야 하는 일이 생길 수 있다.

같은 유형의 객체를 반복해서 생성

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)

참조 타입 (Reference type)

프리미티브 타입이 아닌 것들을 객체타입 또는 참조타입 이라고 한다.

원시 타입(Primitive type) 참조 타입(Reference type)

프리미티브 타입 값을 다른 변수에 할당 하면 프리미티브 타입은 값 자체를 넘겨주기 때문에 값의 원형이 변경되지 않는다. (원시타입)

let username = '이서연';
let username2 = username;

username2 = '표원식';
console.log(username); // '이서연'
console.log(username2); // '표원식'
  • let username2 = username; 코드에서 username 변수의 값이 username2 변수에 복사된다. 이때 값의 복사가 일어나므로 username와 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에서도 동일한 변경이 반영된다.

Call By Value vs Call By Reference

함수 호출시에 파라미터로 프리미티브 타입을 넘기는 것과 객체 타입을 넘기는 것은 어떠한 차이가 있을까요?

  • Call By Value (값에 의한 호출)
function changeName (username) {
	username = '홍길동'
}

const name = '이서연';
changeName(name);

console.log(name);
  • changeName을 호출할 때 name 변수의 값을 전달하고 있다. 그러나 함수 내부에서 username 매개변수에 새로운 값을 할당하고 있지만, 이 값의 변경이 외부 변수인 name에 영향을 주지 않다. 이는 함수 내에서 매개변수에 새로운 값을 할당하더라도 해당 변수가 함수 외부에서 선언된 변수의 복사본이기 때문이다.

함수에 인자로 전달되는 것은 변수의 값(또는 객체에 대한 참조)이므로, 함수 내에서 매개변수에 할당된 값은 외부 변수의 복사본을 의미한다.

Call By Value는 값 자체를 username에 할당하기 때문에 username값을 변경해도 함수밖의 name은 변경되지 않는다. 즉, 값이 복사된 것을 의미한다.

  • Call By Reference (참조에 의한 호출)

참조에 의한 호출"은 함수 호출 방식 중 하나로, 함수에 인자로 전달된 값이 변수의 참조(주소)를 전달하는 방식이다. 이는 함수 내에서 인자로 전달된 변수를 직접 조작할 수 있게 해준다.

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 출력
  • changeName 함수는 person 객체를 인자로 받아서 해당 객체의 name 속성을 변경한다. 그리고 함수 외부에서 person 객체의 name 속성이 변경되었다. 이는 함수에 객체의 참조가 전달되어 함수 내부에서 객체가 직접 수정되기 때문에 발생한다.

Spread operator(전개 구문)

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의 값은 변하지 않는 것을 확인할 수 있다.

이처럼, 스프레드 구문을 사용하면, 참조갑 공유가 아닌, 새로운 객체가 생성되는 것을 확인할 수 있다!

  • 배열인 경우
    let fruits = ['딸기', '수박', '참외', '사과'];
    let fruitsStore = [...fruits, '바나나', '블루베리'];

값을 추가할 때에도
기존의 값은 변경되지 않는 것을 확인할 수 있다.

객체 복사

Call By Reference에서 봤었던 것처럼 객체는 쉽게 복사 되지 않는다.
참조값 복사가 아닌, 객체를 완전히 복사하는 방법은 무엇이 있을 까?

Spread operator를 이용한 복사
전개 구문은 참조값을 할당하는 방식이 아닌 객체에서 속성을 펼치는(Spread) 방식이기 때문에 위에서 확인했듯이 결론적으로 객체 복사가 가능하다.

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

  • const obj2 = Object.assign({}, obj1); : Object.assign() 메서드를 사용하여 빈 객체 {}에 obj1의 속성을 복사하여 새로운 객체 obj2를 생성한다. 이를 통해 obj2는 obj1과 동일한 속성을 가지지만, 완전히 별개의 객체이다.

  • Object.assign() 메서드를 사용하여 객체를 복사하면 복사된 객체와 원본 객체는 완전히 독립적인 객체가 된다. 복사된 객체를 변경하더라도 원본 객체에는 영향을 주지 않는다.

for ... in

객체(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의 값은 함수를 호출한 방법에 의해 결정된다.

  • this호출한 놈 입니다.
  • 호출한 놈이 없을 경우에는 기본값으로 window 객체 이다.
  • 예외
    • 전역스코프에서 thiswindow 객체 이다.
    • 화살표 함수(Arrow Function)에서 this가 조금 달라진다.
    • Strict Mode에서는 this가 조금 달라진다.

전역 스코프에서 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 함수

bind 함수로 this를 설정할 수 있다.

  • print.bind(person1)을 호출하여 bindPrint라는 새로운 함수를 생성한다. 이때, bind() 메서드를 사용하여 print 함수를 person1 객체에 바인딩한다. 이렇게 하면 bindPrint 함수는 항상 person1 객체를 가리키는 this를 갖게 된다.
  • bindPrint()를 호출하여 바인딩된 함수를 실행한다. 이때, bindPrint 함수는 항상 person1 객체를 가리키는 this를 가지므로 this와 this.fullname이 모두 person1 객체를 출력한다.

사용자 정의 객체

사용자 정의 객체 타입(ES6에서는 클래스)을 만들때 this의 사용한다. 이때 생성자는 this를 자신의 인스턴스로 정의한다.

function Person (name, age){
	this.name = name;
    this.age = age;
    }
    
let person1 = new Person('김씨', 20);
console.log(person1)
  • this.name = name;과 this.age = age; 여기서 this는 현재 생성되는 객체를 가리킨다. 생성자 함수가 호출되어 새로운 객체를 만들 때, this는 해당 객체를 참조한다.
  • new 키워드를 사용하여 Person 생성자 함수를 호출하여 person1이라는 새로운 객체를 생성한다. 이 객체는 name 속성이 '김씨'로, age 속성이 20으로 설정된다.

Arrow Function this

Arrow Function 내부에서의 this는 함수가 정의된 스코프에서의 this를 가져온다. 이것은 Lexical Scope를 따르는 것으로, 함수가 정의된 위치에서의 this를 물려받는다.

let person = {
  name: '이서연',
  printThis: () => {
	  console.log(this); // window 객체
	};,
};
  • printThis 함수는 객체의 메소드로 사용되고 있으므로, 일반적으로는 메소드가 호출된 객체를 가리키는 것이 기대된다. 하지만 Arrow Function 내부에서의 this는 함수가 정의된 스코프에서의 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 객체 출력
	}
};
  • addEventListener 함수의 콜백 함수에서 사용하면 this가 상위 컨텍스트를 가리킨다.
    • 화살표 함수로 등록하면 thiswindow 객체를 가리키게 된다.

--> addEventListener를 사용할 때 화살표 함수보다 일반 함수를 사용하는 것이 더 선호한다.

  • this 바인딩 : 화살표 함수는 자신만의 this를 가지지 않고, 외부 스코프의 this를 가져다 쓴다. 이것은 화살표 함수를 사용할 때 이벤트 리스너 내에서 this가 원하는 대상을 가리키지 않을 수 있다는 것을 의미한다. 반면에 일반 함수를 사용하면 this는 이벤트를 발생시킨 요소를 가리킨다.

Strict Mode this

엄격 모드(Strict Mode)에서는 호출한 놈이 없을 경우 기본값window로 하지 않고 undefined로 합니다!
즉 , 더 엄격하게 검사한다.

'use strict';
function printThis() {
	console.log(this);
}
printThis(); // undefined

만약, 'use strict'가 없다면, this는 window를 가리키게 된다.


load, defer, async 속성 알아보기

HTML에서 자바스크립트 파일을 사용할 때 <script>태그를 이용하여 자바스크립트 파일을 불러올 수 있다.
하지만 브라우저에서는 HTML 파일의 위에서 부터 아래로 순차적으로 파싱하기 때문에 HTML이 파싱되지 않은 상태에서 자바스크립트를 파싱할 경우 문제가 생길 수도 있다.

그 문제는 아래와 같이 자바스크립트로 HTML DOM 요소에 접근하려는 경우 이다.


위 코드를 실행하게되면 btnnull이기 때문에 btn.addEventListener메서드 호출시점에 에러가 발생하는 것을 확인할 수 있다.

그 이유는 브라우저가 HTML을 파싱한 후에 자바스크립트에서 HTML요소를 제어할 수 있도록 DOM 객체를 생성하게 되는데, 위 코드는 HTML파싱도 하기전에 DOM요소에 접근하려고 했기 때문에 발생한 문제 이다.

script 로드 해결방법

(과거에 해결했던 방법에 대한 정리는 하지 않는다.
최근에 사용하는 방법만 다루겠다)

HTML5 script 로드 해결방법

HTML5에서는 defer async 속성을 통해 비동기 script 로드가 가능해져 문제가 해결되었다.

  • defer 속성 - HTML 파싱과 함께, 비동기로 JavaScript 파일을 불러온다.

    • HTML 파싱이 완료된 후, JavaScript 코드를 실행한다.
  • async 속성 - HTML 파싱과 함께, 비동기로 JavaScript 파일을 불러온다.

    • HTML파싱이 완료되지 않았더라도먼저 로딩되는 JavaScript파일부터 실행이 시작된다.
    • JavaScript 파일을 실행할 때는 HTML 파싱이 중단된다.

defer 를 그림으로 살펴보면, 파싱이 다 끝난 뒤에 스크립트 실행을 시작하게 된다.

async의 경우, 스크립트 파일을 만났을 때, html 파일과 같이 파싱을 하다가, 실행을 해야 할 때, html의 파일이 일정부분 멈추는 것을 확인할 수 있다.

이로써, 스크립트 파일을 로드할 때에는 defer를 사용한다고 생각하면 될 것 같다!

profile
2024. 01. 02 ~ 백앤드 공부 시작, 2024. 04.01 ~ 프론트 공부 시작

0개의 댓글