데이터 타입은 크게 두가지로 나뉜다. 기본형과 참조형

비트
컴퓨터가 이해하는 가장 작은 단위
0과 1을 가지고 있는 메모리를 구성하기 위한 가장 작은 조각을 의미
바이트
0과 1만 표현하는 비트를 모두 찾기는 부담.
1~8개 (새로운 단위) 바이트
메모리
바이트 단위로 구성/ 바이트 단위 식별자인 메모리 주소값을 통해 서로 구분이 된다.
let a = 3;
a => 식별자
3 => 변수
변수 선언과 데이터 할당
var a = 8;
이라고 했을 때 변수에서 a/@5001 -> a/@5002 이렇게 된다.
데이터가 바뀐게 아니라 변수영역이 바뀐것임.
변수 vs 상수
변수: 변수영역 메모리 변경가능.
상수: 변수영역 메모리 변경불가.
변수a |1001|1002|...
메모리 |a/@5001 -> a/@5002| ...
데이터 | a5002 | a5003 |
메모리 | 8 | 9
불변하다: 데이터영역 메모리 변경불가
가변하다: 데이터영역 메모리 변경가능

변수 a |1001|1002
메모리 |a/@5002|..
데이터 |5002|5003|
메모리 |'abc'|'abcdef'
변수 a 가 불변하지 않기 때문에
변수 a |1001|1002
메모리 |a/@5003|..
변수 영역 메모리가 바뀜.
더 이상 사용하지 않는 데이터 영역 메모리 a/@5002는 가비지 컬렉터에 모여 메모리 관리가 된다.


var user = {
name: 'abc',
gender: 'man'
}
var changeName = function (user, newName) {
var newUser = user;
//객체에 접근해서 이름을 변경 -> 가변적
newUser.name = newName;
return newUser;
}
var changeName = function(user, newName) {
//새로운 객체를 반환
return {
name: newName,
gender: user.gender
}
}
var user2 = changeName(user, 'ddd')
console.log(user.name, user2.name)
//1. 객체에 접근하여 이름 변경 = 가변적
=> user.name과 user2.name과 같다.
//2. 새로운 객체를 반환할 때
=> user.name과 user2.name이 다르다.
하지만 위처럼 객체복사할때는 만약 속성이 100개라면 return도 동일하게 그렇게 해줘야하기에 하드코딩이 너무 많다.
=> 해결법
//얕은 복사 => 중첩 객체는 복사가 안된다.
const copyObject = (target) => {
var result= {};
//target 안의 property를 돌면서 새로운 객체 return
for (var prop in target) {
console.log("prop:", prop)
console.log('target[prop]:', target[prop])
result[prop] = target[prop];
console.log("result:", result)
}
return result;
}
중첩 객체는 복사가 안된다?
var user = {
name: 'abc',
gender: 'man',
sex: {
name: 'nam',
number: [1,2,3,4,5]
}
}
var user2 = copyObject(user);
user2.sex.name = 'aaa';
console.log(user.sex.name, user2.sex.name)
for ~ in구문이 한 depth만 돌면서 프로퍼티를 복사하기 때문임.
결과 값이 aaa aaa
객체의 프로퍼티 중 기본형 데이터는 그대로 복사, 참조형 데이터(중첩 객체)는 다시 그 내부의 프로퍼티를 복사 => 재귀적 수행이 필요하다 recursive
var copyObjectRecursive = function(target) {
var result = {};
console.log("target:", target)
if(typeof target == 'object' && target !== null) {
for(var prop in target) {
console.log("prop:", prop)
result[prop] = copyObjectRecursive(target[prop])
}
console.log('result:', result)
} else {
result = target;
}
return result;
}
var user = {
name: 'abc',
gender: 'man',
sex: {
name: 'nam',
number: [1,2,3,4,5]
}
}
var user2 = copyObjectRecursive(user);
user2.sex.name = 'aaa';
console.log(user.sex.name, user2.sex.name)
결과가 다른값임을 확인할 수 있다
동등 연산자 ==
일치 연산자 ===
콜 스택
function a (x) {
console.log(x);
var x;
console.log(x); //undefined 예상
var x =2;
console.log(x)
}
a(1);
실제 값
1
1
2

var a = 1;
var outer = function() {
var inner = function() {
console.log(a); // var a; -> console -> a = 3; 즉 a는 inner에서 undeifned 외부환경이 outer인데 a 값이 없음.
var a = 3;
};
inner();
console.log(a); // console -> a = 3; 즉 a는 outer에서 1 외부환경이 전역인데 a 값이 1.
};
outer();
console.log(a); // a = 1
각각의 실행 컨텍스트는 LE안에 record와 outer를 가지고 있고 outer 안에는 그 실행 컨텍스트가 실행될때의 LE 정보가 다들어있으니 scope chain에 의해 상위 컨텍스트의 record를 읽어올 수 있다.
함수 vs 메서드
엄연한 차이가 존재한다.
기준은 독립성
함수는 그 자체로 독립적인 기능을 수행
함수명();
메서드는 자신을 호출한 대상 객체에 대한 동작을 수행
객체.메서드명();
// CASE1 : 함수
// 호출 주체를 명시할 수 없기 때문에 this는 전역 객체
var func = function (x) {
console.log(this, x);
};
func(1); // Window { ... } 1
// CASE2 : 메서드
// 호출 주체를 명시할 수 있기 때문에 this는 해당 객체(obj)
// obj는 곧 { method: f }를 의미하죠?
var obj = {
method: func,
};
obj.method(2); // { method: ƒ } 2
함수로서의 호출과 메서드로서의 호출 구분 기준 : . []
var obj1 = {
outer: function() {
console.log(this); // (1) 호출의 주체 obj1이 찍힘
var innerFunc = function() {
console.log(this); // (2), (3)
}
innerFunc(); // 전역 객체가 this이다.
var obj2 = {
innerMethod: innerFunc
};
obj2.innerMethod(); // 메서드이다. 호출주체는 obj2
}
};
obj1.outer();
화살표함수와 일반 함수의 가장 큰 차이점은?
: this 바인딩 여부
화살표함수는 this 바인딩 과정 자체가 없어서 전역 객체를 바라보는 문제가 없어짐.
콜백 함수는 무조건 this를 하면 함수이기에 객체를 잃어버리게 된다. <메소드가 아니다.>
단, 예외는 있다. 콜백 함수에 별도로 this를 지정한 경우.
// 별도 지정 없음 : 전역객체
setTimeout(function () { console.log(this) }, 300);
// 별도 지정 없음 : 전역객체
[1, 2, 3, 4, 5].forEach(function(x) {
console.log(this, x);
});
// addListener 안에서의 this는 항상 호출한 주체의 element를 return하도록 설계되었음
// 따라서 this는 button을 의미함
document.body.innerHTML += '<button id="a">클릭</button>';
document.body.querySelector('#a').addEventListener('click', function(e) {
console.log(this, e);
});
call
var func = function (a,b,c) {
console.log(this, a,b,c)
}
func.call({x:1}, 4,5,6,)
//바인딩 하고픈 객체를 {x:1} 설정
var obj = {
a: 1,
method: function (x, y) {
console.log(this.a, x, y);
}
};
//method 의 호출주체는 obj이다. 따라서 this는 항상 obj
obj.method(2, 3); // 1 2 3
//call으로서 명시적 this 바인딩이 가능하다.
obj.method.call({ a: 4 }, 5, 6); // 4 5 6
apply 는 call과 완전 같은데 다만 뒤에 매개변수를 배열로 묶어줘야함
Array.from(유사배열)
var obj = {
0: 'a',
1: 'b',
2: 'c',
length: 3
}
var arr = Array.from(obj)
console.log(arr)
call 사용 전 코드
function Student(name, gender, school) {
this.name = name;
this.gender = gender;
this.school = school;
}
function Employee(name, gender, company) {
this.name = name;
this.gender = gender;
this.company = company;
}
call 사용 후 코드
function Person(name, gender) {
this.name = name;
this.gender = gender;
}
function Student(name, gender, school) {
//this 가 Student인 이유 : 함수니까.
Person.call(this, name, gender); // 여기서 this는 student 인스턴스!
this.school = school;
}
function Employee(name, gender, company) {
Person.apply(this, [name, gender]); // 여기서 this는 employee 인스턴스!
this.company = company;
}
var kd = new Student('길동', 'male', '서울대');
var ks = new Employee('길순', 'female', '삼성');
this를 바인딩한다. call,apply와 달리 즉시 호출하지 않는다.
목적 : 함수에 this를 미리 적용, 부분 적용 함수
var func = function(a,b,c,) {
console.log(this, a,b,c)
}
func(1,2,3)
var bindFunc = func.bind({x:1})
bindFunc(5,6,7)
부분적용
var bindFunc2 = func.bind({x:1}, 4, 5);
bindFunc2(1) // 결과는 {x:1}, 4,5,1