primitive data type
변수에 값이 그대로 저장되는 요소
숫자나 문자가 대표적
reference data type
변수에 reference 가 저장되는 요소
쉽게 말해 변수에 데이터를 저장하면 값이 아니라 값이 저장된 장소를가리키는
요소
array, object 가 대표적
두 데이터 타입의 차이는 복사를 해보면 가장 명확하게 알 수 있다.
var name1 = 'kim';
var name2 = name1;
name1 = 'soo';
console.log(name1);
console.log(name2);
//
soo
kim
primitive 데이터 타입은 변수에 할당된 값이 각자의 값을 가지고 있으므로
name1 변수를 변경하더라도 name2 변수에는 영향을 주지 않는다.
var name1 = { name: 'kim' };
var name2 = name1;
name1.name = 'soo';
console.log(name1)
console.log(name2)
//
{name: 'soo'}
{name: 'soo'}
reference 데이터 타입은 복사할 때 값
을 복사하는 것이 아닌, 값이 저장된 주소
를 복사하게 된다.
몇번을 복사하든 메모리에 저장된 똑같은 주소를 가리키게 되므로 primitive 데이터 타입처럼 복사를 하면 안된다.
전에 공부했던 깊은, 얕은 복사의 개념이다.
예제를 하나 더 보자
name1 = 'kim'
name2 = 'kim'
name3 = {name: 'kim'}
name4 = {name: 'kim'}
console.log(name1 === name2); // true
console.log(name3 === name4); // false
name1과 name2는 서로 같은 값을 가지고 있으므로 당연히 true 를 반환한다.
그러나 name3와 name4는 분명하게 서로 같은 값임에도 불구하고 false 를 반환한다.
왜 그럴까?
reference 데이터 타입은 아까 언급했다시피 변수에 주소
를 저장하게 된다.
{ } 하나에 하나의 주소를 할당하게 되므로 내부의 값이 같다고 해도 같은 주소를 가진게 아니다.
따라서 false 를 반환하게 된다.
그러면 이런 경우는 어떨까 ?
var name1 = {name: 'kim'}
function func(obj) {
obj.name = 'soo'
}
func(name1);
console.log(name1);
//
{name: 'soo'}
오브젝트를 파라미터로 넣으면 name의 value를 ‘soo’로 변경하는 function 을 하나 만들었다.
문제가 없어 보인다.
그렇다면,
var name2 = {name: 'soo'}
function func(obj) {
obj = {name: 'kim'}
}
func(name2);
console.log(name2);
//
{name: 'soo'}
오브젝트를 파라미터로 넣으면 해당 오브젝트에 아예 {name: ‘kim’} 이라는 오브젝트를 재할당 하는 function.
말로만 들으면 문제가 없을 것 같다.
하지만 실제로는 제대로 작동하지 않는다.
왜 그럴까 ?
함수의 파라미터는 변수의 생성과 할당을 하는 역할을 한다.
위 함수의 파라미터 obj 는 var obj = {name: ‘kim} 의 역할을 하고 있는것이다.
따라서 파라미터의 변수 obj에 새로운 할당이 되는데,
새로 할당된 obj 변수에 어떤 값을 할당한다고 해서 name2 변수에 영향을 주지는 않는다.
따라서 name2 오브젝트는 그대로 name: soo 를 출력하게 된다.
오브젝트 생성 기계.
비슷한 오브젝트를 여러개 만들고 싶을 때 쉽게 할 수 있다.
reference 데이터 타입은 복사에 제약이 있다.
위에서 공부했던 내용처럼, 비슷한 오브젝트를 만들고 싶다고 함부로 복사할 수 없다.
그럴 때 쓸 수 있는 문법.
function Human() {
this.name = 'kim';
this.age = 20;
this.job = 'soldier';
}
var man1 = new Human();
//
Human {name: 'kim', age: 20, job: 'soldier'}
관습적으로 constructor의 첫 글자의 스펠링은 대문자로 표기한다.
constructor 의 this 는 새로 생성될 오브젝트를 의미한다. instance 라고도 한다.
예를들어, this.name = ‘kim’ 을 보면
Human constructor 로 생성할 오브젝트의 name 이라는 key에 ‘kim’이라는 value 를 할당시키는 것이다.
function Human(name,age) {
this.name = name;
this.age = age;
this.sayHi = function() {
console.log(`hello! my name is ${this.name}. i'm ${this.age} year's old!`)
}
}
var man1 = new Human('soo',19);
man1.sayHi();
//
hello! my name is soo. i'm 19year's old!
이런식으로 파라미터를 넣어서 가변적으로 할당할 수도 있고,
constructor 안에 함수를 만들어서 생성되는 오브젝트마다 함수를 사용할 수 있게 만들수도 있다.
이런 작업을 어려운말로 상속
이라고 한다고 한다.
상속의 개념은 자바스크립트에만 있다.
상속을 구현하는 또 다른 용법을 알아보자.
var arr = [1,2,3]
console.log(arr)
//
(3) [1, 2, 3]
0: 1
1: 2
2: 3
length: 3
[[Prototype]]: Array(0)
간단하게 배열을 하나 만들었다.
그 배열을 console.log로 찍어보면 prototype 이라는 이상한 키워드가 보인다.
prototype 은 뭘까 ?
prototype 그 요소의 유전자라고 볼 수 있다.
유전자는 성격, 성질을 결정짓는 요소이다.
인간을 예로 들어보자.
환경적 요인을 배제한다면,
키가 얼마나 클 것이고 생김새가 어떨것이며 어떤 재능을 가질 수 있는지에 대한 결정요인이다.
arr 의 prototype 은 Array 라고 출력이 되는 듯 하다.
arr는 Array 라는 유전자를 가진 요소라는 뜻.
또 한가지, 인간이나 동물이 그러하듯
모든 자식들이 물려받을 속성을 의미한다.
인간의 유전자에 어떤 특정한 값을 추가한다는 개념은 잘 모르지만,
자바스크립트의 prototype 속성에 어떤 값을 추가한다면 모든 자식들이 물려받을 수 있다.
예를들어, 위의 constructor 에 prototype 을 추가한다면
function Human(name,age) {
this.name = name;
this.age = age;
this.sayHi = function() {
console.log(`hello! my name is ${this.name}. i'm ${this.age} year's old!`)
}
}
Human.prototype.gender = 'man'
var man1 = new Human('soo',30);
console.log(man1);
console.log(man1.gender);
//
Human {name: 'soo', age: 30, sayHi: ƒ}
man
분명 constructor 에 gender 라는 key 값이 없음에도
man1.gender 는 ‘man’ 값을 출력한다.
생성한 오브젝트에 유전자로써 존재하는 것이다.
이 prototype 기능의 핵심은
부모의 유전자, 그 부모의 유전자, 그 부모의 유전자 를 거슬러 올라가 찾아내는 것에 있다.
어떤 배열을 생성할 때,
var arr = [1,2,3]
이런식으로 생성한다.
그런데 자바스크립트가 받아들이는 방식은 조금 다르다.
var arr = new Array(1,2,3)
Array 라는 constructor 에 1,2,3 을 담아서 변수 arr 에 담아주세요. 한다는 것이다.
Array, Object, Number 같은 prototype 을 자바스크립트에서 미리 내장함수로 정의해놓은 것이다.
생성된 모든 배열은 prototype: Array 라는 유전자를 가지고 태어난다.
arr 이라는 배열이 담긴 변수에
따로 sort(), forEach() 같은 함수가 담긴 key 를 따로 정의해준적이 없음에도,
Array 라는 prototype 을 가진 arr 은 해당 메소드를 상속
받아서 쓸 수 있다는 것이다.
반대로 모든 배열에 적용시킬 수 있는 함수를 prototype 을 이용해서 만들 수 있다.
Array.prototype.oddNumOnly = function() {
for(i=0; i < this.length; i++) {
if(this[i] % 2 === 0){
this.splice(i,1);
}
}
}
let arr = [1,2,3,4,5,6,7,8,9]
arr.oddNumOnly();
console.log(arr);
잘 되는 모습.
constructor 의 ES6 신 문법.
class Human {
constructor(name){
this.name = name;
}
}
let man1 = new Human();
얼핏보면 다른게 없어보인다.
그런데,
class Human {
constructor(name){
this.name = name;
}
sayHi(){
console.log(`hello! my name is ${this.name}!`)
}
}
let man1 = new Human('soo');
man1.sayHi();
//
hello! my name is soo!
이런식으로 함수를 constructor 와 분리할 수 있다.
자식 오브젝트에 추가되지 않고 prototype 으로 추가된다.
추가로
man1.__proto__;
Object.getPrototypeOf(man1);
Human.prototype.sayName = function(){
console.log(this.name);
}
__proto__
나 getPrototypeOf
메소드로 부모의 prototype 을 확인해볼 수 있다.
또한 .prototype 메소드를 이용해 추가로 내장함수를 정의해줄 수도 있다.