<script>
// use strict를 사용하면, 의도적으로 window를 띄우려던 경우가 아니라면 undefined나 에러가 출력된다.
// 사실상 window를 this로 사용하는 경우는 없다. 그냥 window 객체를 사용하면 되기 때문이다.
// 따라서 이러한 경우에는 개발자에게 오류임을 알려줄 수 있다.
// 'use strict'
// 1. window
// 일반 함수 실행 방식으로 함수가 실행된다면, this는 단순히 window 객체를 참조한다.
console.log(this);
function func() {
console.log(this);
}
func(); // undefined
window.func(); // window
// 1-1. window
var age = 20;
const jun = {
age: 27,
logAge: function () {
console.log(this.age);
},
};
var junAge = jun.logAge;
// 이 때 this는, 일반 함수 실행 방식에 의해 불려지기 때문에 this는 window이다.
// 따라서 this.age는 20이므로 20이 출력된다.
junAge(); // window
// 2. dot notation
var obj = {
data: "Kim",
func: function () {
console.log(this);
},
};
obj.func(); // obj
// 2-1.
var obj2 = {
data: {
func: function () {
console.log(this);
},
},
};
obj2.data.func(); // data
// 2-2.
var obj3 = {
data: {
func: () => {
console.log(this);
},
},
};
obj3.data.func(); // arrow function은 this를 재설정하지 않는다. 따라서 window가 뜬다.
var obj4 = {
data: {
func() {
console.log(this);
},
},
};
obj4.data.func(); // data
</script>
<script>
// object를 많이 생성하고 싶을 때, object를 찍어내는 함수를 생성자(constructor)라고 한다.
// 3. 이 때 this는, 생성자에 의해 새로 생겨나는 instance object를 뜻한다.
function Machine() {
this.name = "Kim";
}
var obj = new Machine();
console.log(obj);
// 4. 이벤트 리스너 안에서, 이 때 this는 event.currentTarget과 동일하다.
// 이 this는 addEventListener에 의해 불려지는데, 결국 #button1에서 dot notation으로 불려진다.
// 즉 여기서 this는 id=button1인 태그이다.
document.querySelector("#button1").addEventListener('click', function(event) {
console.log(this); // event.currentTarget
console.log(event.currentTarget); // 어떤 요소를 클릭하여 이 event가 발생하게 되는지
});
// 4-1.
// forEach는 밑의 for문과 똑같이 동작한다.
// 따라서 결국엔 this는 일반 함수 실행 방식에 의해 실행된다.
// 즉, this는 window가 출력된다.
document.querySelector("#button2").addEventListener('click', function(event) {
let arr = [1, 2, 3];
arr.forEach(function (element) {
console.log(element);
console.log(this);
})
// for (let i = 0; i < arr.length; i++) {
// function func (element) {
// console.log(arr[i]);
// console.log(this);
// }
// func(i);
// }
})
// 4-2.
var obj2 = {
names: ['Kim', 'Lee', 'Park'],
func: function() {
console.log(this); // obj2
obj2.names.forEach(function() {
console.log(this); // window
})
obj2.names.forEach(() => {
console.log(this); // 일반 함수가 아니라 arrow function이다. console.log(this)는 arrow function에 의해 실행된다.
// 이 this는 func에 의해 실행되고 이 func는 obj2 소속이므로 상위 단계는 obj2이다.
})
}
}
obj2.func();
</script>
// object를 많이 생성하고 싶을 때, 생성자(constructor)를 이용한다.
function Machine() {
// 3. 이 때 this는, 생성자에 의해 새로 생성되는 instance object를 뜻하게 된다.
this.name = "Kim";
}
var obj = new Machine();
console.log(obj);
document
.querySelector("#button1")
.addEventListener("click", function (event) {
// 4. (이벤트 리스너 안에서) 이 때 this는 event.currentTarget과 동일하다.
console.log(this);
// event.currentTarget : 지금 event(클릭 이벤트)가 동작하고 있는 곳을 뜻한다. 지금 클릭 이벤트가 일어나고 있는 곳을 가르키고 있게 된다.
console.log(event.currentTarget);
// 즉 여기서 document.querySelector('#button1')과 this, event.currentTarget은 같은 곳을 가르키고 있다.
});
// 특수한 경우
document
.querySelector("#button2")
.addEventListener("click", function (event) {
let arr = [1, 2, 3];
// 함수 안에 들어가는 함수를 callback함수라고 보면 된다.
arr.forEach(function (element) {
console.log(element);
// 일반 함수 안에서 사용했기 때문에 window가 출력된다. 이 함수를 담고 있는 것은 window라고 보면 된다.
console.log(this);
});
});
var obj2 = {
names: ["Kim", "Lee", "Park"],
func: function () {
// 이 때 this는 obj2를 출력시킨다. func는 obj2에 담겨져 있기 떄문이다.
console.log(this);
obj2.names.forEach(function () {
// 이 함수도 일반 함수이다. 그냥 일반 함수라서 이 함수를 포함하는 window 객체가 출력되게 된다.
console.log(this);
})
obj2.names.forEach(() => {
// 지금은 arrow function을 사용하였다. 이 arrow function은 this를 재설정시키지 않는다. 상위 단계(부모)로부터 오는 this를 그대로 사용한다.
// 따라서 상위 단계인 function(), 즉 func는 obj2 소속이므로 이 this는 obj2를 출력시킨다.
console.log(this);
})
},
};
obj2.func();
<script>
// 'use strict';
// 기존엔 var만 존재했다.
// ES6에서 let과 const가 추가되었다.
// 변수의 특징 : 선언 / 할당 / 범위
// var : 재선언 O, 재할당 O, 범위 : function
// var는 밑에와 같이 재선언이 가능하다.
var nameVar = "Kim";
var nameVar = "Park";
nameVar = "Shin"; // 재할당이 가능하다.
// let : 재선언 X, 재할당 O, 범위 : '중괄호' {}
// Identifier 'ageLet' has already been declared 해당 경고 문구가 뜬다.
// let ageLet;
// let ageLet;
let age = 20;
age = 30; // 재할당이 가능하다.
// const : 재선언 X, 재할당 X, 범위 : '중괄호' {}
// const는 선언 시 초깃값이 필요하다.
// const whatConst; // 초깃값을 선언해달라는 경고 문구, Missing initializer in const declaration이 뜬다.
const nameConst = "Kim";
// nameConst = 'Shin'; // 재할당이 불가능하다. Assignment to constant variable 라는 경고 문구가 뜨게 된다.
const person = {
name: "Kim",
};
console.log(person);
person.name = "Shin"; // object 안의 값은 변경될 수 있다. person 자체가 재할당 된 것이 아니라, 그저 그 안의 내용이 바뀌었을 뿐인 것이다.
console.log(person);
Object.freeze(person); // person이 변경되는 것을 막는다. 만약 use strict 모드에서,
person.name = "Park"; // 이렇게 person을 변경하려는 코드가 실행된다면 에러를 출력한다.
console.log(person);
function func() {
var nameFunc = 'kim'; // var는 function 안에서만 존재한다.
}
console.log(nameFunc); // function 밖에서는 유효하지 않다.
if (true) {
let nameIf = 'Park'; // let은 중괄호안에서만 유효하다. const 또한 마찬가지이다.
}
console.log(nameIf); // 에러
</script>
<script>
// 변수의 Hoisting 현상 : 변수의 선언을 변수 범위의 맨 위로 끌고 오는 현상이다.
var age = 30;
// 위는 실제로 아래와 같다.
var age; // 맨 위로 변수 선언을 올린다.
age = 30;
// 전역 변수 : 모든 곳에서 쓸 수 있는 변수이다. 지금은 age가 전역 변수이다.
function func() { // 함수 또한 hoisting이 일어난다.
console.log(age);
}
func();
window.name = 'Kim'; // 이 또한 전역 변수이다.
// 자바스크립트 기본 함수를 담은 object이다. alert, DOM, getElementById(), console.log() 등이 담겨져 있는 아주 큰 객체이다.
// 전역 변수를 만들 때는 이와 같이 window를 더 많이 사용한다. 전역 변수라는 느낌을 더 주기 때문이다.
console.log(window.name);
// 예제
if (true) {
let a = 1;
// var b = 2;
if (true) {
let b = 3; // 위의 var b = 2;가 없어지면, 이 if 문의 b는 이 if 문에서만 유효하기 때문에 console.log('b', b);는 b가 선언되지 않은 것으로 생각하고 에러를 발생시킨다.
console.log('a', a);
}
console.log('b', b);
}
</script>
<script>
// Q1 - 에러 출력
// func(); // 이것 자체는 상관없다. func는 hoisting되기 때문에 이 코드 자체는 문제가 없다.
// function func() {
// console.log(hi); // let, const는 hoisting 시 undefined가 자동으로 할당되지 않는다. 반면에 var는 undefined가 자동으로 할당된다.
// let hi = "Hello"; // 이는 실수를 없애기 위함이다. 변수 선언 및 초기화 전에 console.log를 찍는 것은 실수이기 때문이다.
// }
// Q2 - undefined
// func(); // 변수 선언만 위로 올라왔을뿐, 실제로 func에 function을 대입하는 것은 밑의 코드이다. 따라서 func()로 ()를 붙여 실행시키려고 하면, 아직 function이 아니기 때문에 에러가 출력된다.
// var func = function() { // 변수 선언 부분만 위로 올라간다.
// console.log(hi);
// var hi = 'Hello';
// }
// Q3
// let a = 1;
// var func = function() {
// a = 2;
// }
// console.log(a); // 함수를 선언만 했을 뿐, 실행은 한 적이 없다.
// Q4
// let a = 1;
// var b = 2;
// window.a = 3;
// window.b = 4;
// console.log(a); // 가장 가까운 값을 출력시키려고 한다. 따라서 window.a보다는 let a를 출력시킨다.
// console.log(a + b);
// Q5
// // var i = 5; // 밑의 for문을 돌고 나면 var i는 전역변수로서 밖에 이렇게 5로 남아있다. for문을 다 돌면 var i는 5에서 멈추기 때문이다.
// 이 상태에서 console.log(i)가 실행되면 var i = 5를 찾게 되어서 5가 계속해서 출력된다.
// for (var i = 1; i < 6; i++) {
// setTimeout(function () {
// console.log(i); // 이 때 console.log(i)는 바로 실행되지 않는다. 1 ~ 5초 뒤에 실행되는 코드이다.
// }, i * 1000);
// }
// for (let i = 1; i < 6; i++) {
// // let의 범위는 블록이다. 따라서 이 블록 내에서 let i = 1, let i = 2처럼 남게 된다.
// // let i = 1 ~ let i = 5;
// // 이후 console.log(i)가 실행되면 해당 블록 내에서 i를 찾았을 때 전역 변수 var i = 5가 아니라 let i = 1 ~ let i = 5를 찾게 될 것이다.
// setTimeout(function () {
// console.log(i);
// }, i * 1000);
// }
// Q6
var buttons = document.querySelectorAll("button");
var modals = document.querySelectorAll("div");
// 반복문이 도는 시점과, for문 안의 코드가 실행되는 시점이 다르기 때문에 에러가 출력된다.
// var는 전역변수로서 결국엔 3이 된다.
// 버튼을 클릭할 때 실행되는, 즉 반복문보다 늦게 실행이 된다.
for (var i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', function() {
modals[i].style.display = 'block';
})
}
// 만약 let을 사용했다면 해당 블록 내에 let i = 0, let i = 1, let i = 2이 남게 된다.
// 반복문 안에 참조할 수 있도록 let i가 생기는 것이다.
// for (let i = 0; i < buttons.length; i++) {
// buttons[i].addEventListener('click', function() {
// modals[i].style.display = 'block';
// })
// }
</script>
<script>
var letter = `손흥민`; // 백틱을 사용하고 있다.
// "", ''를 쓰는 것이 아니라 백틱을 쓰는 이유
// 1. 엔터키를 눌러도 글자가 깨지지 않는다.
// 실제로 출력해도 엔터만 될 뿐 깨지지는 않는다.
// var letter2 = "손흥
// 민";
// 이렇게 하면 빨간 에러 줄이 뜨면서 깨지게 된다.
// 2. 변수를 중간에 넣기 쉽다.
// var letter3 = "안녕하세요 저는 " + letter + "입니다."; // 원래 방식
// console.log(letter3);
var letter4 = `안녕하세요 저는 ${letter}입니다.`; // 백틱을 사용하면 중간에 ${}를 사용해서 변수를 넣을 수 있다. +를 쓸 필요가 없어진다.
// console.log(letter4);
// 이는 밑에와 같이 HTML을 작성할 때 유용해진다.
// var temp = `<div>${letter4}</div>`;
// console.log(temp);
// tagged literal
// 소괄호 () 대신에 백틱을 붙이는 문법이 있다. 그냥 함수를 실행시켜준다.
// 따라서 밑의 console.log에서 10이 출력된다. 백틱은 그저 함수를 실행시키는 용도일 뿐이다.
// function func() {
// return 10;
// }
// console.log(func`안녕하세요`);
// 사용하는 이유
function split(letters, variable1) {
console.log(letters); // 백틱 안의 문자들을 Array에 담고 있다.
console.log(variable1); // 변수를 담고 있다.
console.log(letters[1] + variable1 + letters[0]); // 이렇게 원하는대로 순서를 바꿔서 출력해줄 수 있다.
// console.log(variable2);
}
var letter5 = '신승준';
split`안녕하세요 저는 ${letter}입니다.`;
// // 위 백틱 안의 내용이 나누어졌다는 것을 알 수 있다.
// // 즉 문자를 해체해서 단어 순서를 변경하거나, 단어를 제거하거나, ${변수} 위치를 옮기는 등의 작업이 손쉽게 가능해진다.
</script>
<script>
// Q1
// var pants = 20;
// var socks = 100;
// function split(letters, variable1, variable2) {
// console.log(letters[0] + variable2 + letters[1] + variable1);
// }
// split`바지${pants} 양말${socks}`;
// Q2
var pants = 0;
var socks = 100;
function split(letters, variable1, variable2) {
if (variable1 === 0) {
variable1 = ' 다 팔렸어요.';
}
console.log(letters[0] + variable1 + letters[1] + variable2);
}
split`바지${pants} 양말${socks}`;
</script>
<script>
// // ... spread operator
// // 기본 활용 방도
// // 대괄호 []를 제거해준다.
// var arr = ["hello", "world"];
// console.log(arr);
// console.log(...arr);
// console.log("hello", "world");
// // 문자
// // 마찬가지로 대괄호가 다 제거되었다고 보면 된다.
// var letter = "hello";
// console.log(letter);
// console.log(...letter);
// console.log("h", "e", "l", "l", "o");
// console.log(letter[0]); // 문자는 배열처럼 인덱싱이 가능하다.
// console.log(letter[0], letter[1], letter[2], letter[3], letter[4]);
// ... 활용 방안
// var a = [1, 2, 3];
// var b = [4, 5];
// var c = [...a]; // 1, 2, 3으로 만들고 그것을 []로 감쌌다.
// var d = [...a, ...b];
// // var e = ...a; // 1, 2, 3이 되므로 오류가 난다.
// console.log(a);
// console.log(c);
// console.log(d);
// deep copy할 때 ...이 유용하다.
// var a = [1, 2, 3];
// // var b = a; // reference data type, a와 b가 [1, 2, 3]을 공유하게 된다.
// var b = [...a]; // b가 단독으로 [1, 2, 3]을 가진다. 풀어헤쳤다가 1, 2, 3을 가지게 된다. 이제 a와 b는 독립적으로 [1, 2, 3]을 가지게 된다.
// // 이러한 복사, 독립적으로 값을 가지게 되는 상황을 deepcopy라고 한다.
// a[3] = 4; // deepcopy하지 않으면 이렇게 할 때 a와 b가 모두 바뀐다.
// console.log(a);
// console.log(b);
// object를 복사할 때도 마찬가지이다. deepcopy
var obj1 = { a: 1, b: 2 };
var obj2 = { ...obj1, c: 3 };
var obj3 = { ...obj1 };
var obj4 = { a: 2, ...obj1 }; // 값 중복이 발생한다. 이 때는 뒤에 온 것이 그 중복된 key 값의 주인이 된다. 즉 key의 value는 뒤에 오는 것이 된다.
var obj5 = { ...obj1, a: 2 };
console.log(obj2); // obj1과 합쳐졌다는 것을 알 수 있다.
console.log(obj3);
console.log(obj4);
console.log(obj5);
// ...obj1 // 이것은 에러가 난다. 함수 - (), 객체 - {}, 배열 - []안에서 사용이 가능하다.
</script>
<script>
// 함수를 실행할 때 인자 전달 용도로 ... spread operator를 사용할 수 있다.
function sum(a, b, c) {
console.log(a + b + c);
}
var arr = [10, 20, 30];
console.log(sum(arr[0], arr[1], arr[2]));
console.log(sum(...arr));
// 옛날 방식
sum(arr[0], arr[1], arr[2]);
sum.apply(undefined, arr);
// 요즘 방식
sum(...arr); // 대괄호 []가 제거된 자료들이 인자로 들어가게 된다.
sum(10, 20, 30);
// apply
var person1 = {
greeting: function () {
console.log(this.name + "hi");
},
};
// person1의 greeting을 person2에도 적용하고 싶다.
// 하지만 person2를 변경할 수 없을 때
var person2 = {
name: "shin seung jun",
};
// person의 greeting을 person2에 적용해서 실행해주세요.
person1.greeting.apply(person2); // 이 때 this는 person2이다.
// person2.greeting()과 같으나, person2에는 greeting이 없다.
person1.greeting(); // undefined hi
// call
// apply와 비슷하다.
// greeting에 인자를 넣고 싶을 때
// apply는 배열 형태로 넣을 수 있다.
// call은 그럴 수 없다. 이것이 유일한 차이이다.
person1.greeting.apply(person2, [1, 2]);
person1.greeting.call(person2, 1, 2)
sum.apply(undefined, arr); // sum을 하는데, undefined에 적용해서 arr인자를 바탕으로 sum을 실행해주세요. undefined가 아니더라도 상관없다. 큰 의미가 없다 여기서는.
// 여기서 굳이 apply를 쓴 이유는 인자를 늘어놓기보다는 그냥 배열로 넣으면 편하니까. 만약 call을 썼다면 인자를 막 썼어야 한다.
// 어쨋든 요즘엔 apply나 call보다는 ...을 많이 쓴다.
</script>