[JS_memo] 변수정리, 함수정리, &&사용, 클래스와 콜백함수

Lina Hongbi Ko·2023년 2월 27일
0

JavaScript_memo

목록 보기
3/7
post-thumbnail

변수

우리는 데이터를 사용할때 이 데이터를 담는 그릇을 변수라고 생각하고 사용한다.
즉, 변수는 우리가 처리해야하는 데이터를 담고 있는 아이이고, 이 변수를 통해 우리는 접근하고 데이터를 업데이트 한다. 변수의 데이터타입에는 다양한 것들이 있지만, 원시타입(primitive)과 객체타입(object)으로 나눌 수 있다.
primitive타입은 단일데이터로 number, string, boolean, null, undefined, symbol이 있고 object타입은 복합데이터로 object, array, list, function ... 이 있다.
여기서 우리는 중요하게 생각해야할 것이

💡 원시타입은 값자체(copby by value)를 가리키고, 객체타입은 메모리주소(copy by reference)를 가르킨다.

ex)

let number = 2;
let number2 = number;
console.log(number); // 2
console.log(number2); // 2

위처럼 변수를 선언하면 number에 2를 담기 위한 공간이 생긴다. 그리고 number2에 number의 데이터가 복사되고, 마찬가지로 데이터를 저장하는 공간에 담긴다.

number2 = 3;
console.log(number); // 2
console.log(number2); // 3

number2를 3으로 업데이트하면 숫자 3이 number2의 저장공간에 다시 담긴다.
number에 있는 데이터가 복사되어서 number2에 담기기 때문에 number의 숫자는 바뀌지 않는다. 즉, number에 영향을 주지 않는다. primitive타입의 데이터들은 작은 단위로 메모리에 저장되기 때문에 복사되어 저장된다. 즉, 값을 복사해서 저장하는 타입인 원시타입(primitive type)이 이 개념에 해당된다.

ex)

let object = {
	name: 'korong',
    age: 3,
};
let object2 = object;
console.log(object.name); // korong
console.log(obejct2.name); // korong

위처럼 변수를 선언하면 object의 각 키들은 메모리가 할당되고 키와 값을 묶어서 하나의 heap이라는 데이터 공간에 저장하고 (object는 하나 이상의 데이터가 묶여서 뚱뚱하기 떄문에 값을 복사하지 않고 따로 저장한다) 그 저장한 곳의 주소 (reference)를 object 변수에 담는다.
즉, 주소만 변수에 담긴다는 것이다. 주소(reference)를 저장하는 타입인 객체타입(object ype)이 이 개념에 해당된다.
그리고 우리가 object의 데이터에 접근하면, object에 담긴 주소를 거쳐 주소 안에 있는 데이터들을 보며 그 값에 접근할 수 있다. object의 name도 이런 원리.
object2도 object 변수에 담긴 주소(reference)를 복사하므로 name이 똑같다.

object.name = 'jjang'
console.log(object.name); // jjang
console.log(obejct2.name); // jjang

object의 name을 jjang으로 업데이트하면 어떻게 될까? 위의 primitive타입의 데이터와는 달리 object타입은 언급한 것처럼 주소(reference)를 복사하므로 주소는 동일하더라도 그 안의 데이터가 바뀌면 똑같은 주소 안에 내용이 바뀐 것이므로 역시 똑같이 바뀐다. 그러므로 어떤 것을 업데이트해도 둘 다 변경된다.

let a = 2;
a = 5;
a = 8;

const b = 2;
b = 4;

let은 변수의 값을 변경할 수 있지만, const는 변수의 값을 변경할 수 없다는 사실은 우리는 잘 알고 있다. 그런데

const object = {
	name: 'korong',
    age: 3,
}

obejct = {
	name: 'lina',
    age: 4,
}

console.log(object) // error

object 자체를 변경하면 여기서도 error을 발견할 수 있다.
reference자체를 다른 reference로 바꾸는 것은 const로 선언했을 때 불가능하다.
하지만, object 안의 데이터만 바꾼다면?

object.name = 'lina';

object의 reference는 const에 의해서 잠기지만 그 공간 안에 있는 데이터들은 변경이 가능하다. 원시타입은 값을 복사하므로 const로 변수 선언을 하고 값을 바꾸면 바로 error가 생기지만 객체타입은 주소를 복사하므로 그 객체 자체를 변경하지 않는 이상 그 안의 데이터를 변경해도 상관없다.

함수

프로그램을 짤때 반복적으로 실행되어지는 코드들을 묶어서 우리는 함수로 만들 수 있다. 함수는 재사용성이 좋다:)

ex)

function add(a, b) {
	return a + b;
}

add라는 함수는 우리가 정의한 기능(코드블럭)이고, function을 붙임으로써 '함수'라고 말해준다.
함수의 이름은 의미있게 짓고, 짧고 의미 있는 이름을 짓는게 중요하다.
add함수는 a와 b를 받아와서 'a + b'를 리턴한다. 여기서 a와 b는 인자(parameter)이고 변수처럼 우리가 사용하고 싶은 이름을 적는데 이 인자의 데이터타입은 호출하는 사람이 결정한다.
함수를 작성할 때는 어떤 데이터가 오는지 알 수 없지만, 호출할때 인수에서 어떤 타입의 데이터를 호출하느냐에 따라 달라진다는 말이다.

💡 key point : 함수의 인수를 어떤 데이터타입으로 호출하느냐에 따라 함수의 인자는 달라질 수 있다.

const sum = add(3, 4);

add함수를 호출하면 3과 4를 인수로 호출했기 때문에 number타입의 a,b가 되고 return값이 7이 된다. 그리고 7이라는 숫자는 sum의 변수값이 된다.

➕ add라는 이름은 함수를 가르키는데 이 함수는 object타입의 변수이므로 코드블록이 어딘가의 메모리(heap)에 만들어진다. 그리고 reference(시작주소)로 뚱뚱한 함수블록을 가르키게 되고, add라는 이름의 함수는 다시 그 reference를 담고있다.

const doSomething = add;
const result = doSomething(2, 3);
consolelog(result); // 5

즉, doSomething은 add의 reference를 똑같이 가르킨다. 그러므로 doSomething(2, 3)으로 호출하면 add(2, 3)으로 호출한 것과 동일하다.

💡 key point : 함수의 이름을 다른 변수에 할당하면 함수가 가르키고 있는 주소가 할당되어서 똑같은 함수의 기능을 수행하게 된다.

function print() {
	console.log('print');
}

print(8, 33); // print

위의 코드에서 print함수를 인수를 넣어서 호출하면 print라고만 콘솔창에 출력될 뿐이지 인수의 숫자들로 아무것도 할 수 없다.
그렇다면 이렇게 바꾸면?

function print(a, b) {
	conosle.log(`${a} ${b}`);
}
print (8, 33); // 8 33

다시 코드를 수정하고 콘솔창을 보자. 8과 33을 확인할 수 있다. 이처럼 함수에 인자를 의미 있는 이름으로 넣고 사용하고 우리는 인자를 통해 원하는 데이터를 호출 할 수 있다.

function surprise(operator) {
	const result = operator(2, 3);
    console.log(result);
}

surprise(add); // 5

위의 operator은 surprise 함수의 인자이고, 인수를 add 함수로 전달하므로 operator은 콜백함수가 되는 것이다. 즉, operator에 add의 reference를 복사되어서 전달된다. 따라서 operator(2, 3)은 add(2, 3) 함수가 실행되는 것과 동일하다.

💡 key point : 함수의 이름을 전달하는 것은 이름이 가르키고 있는 함수의 reference를 전달하는 것이다

function divide(a, b) {
	return a / b;
}
surprise(divide);

그리고 다른 함수를 정의하고 호출함으로써 우리는 함수를 재사용하고 또 편리하게 사용할 수 있다.

연산자 &&

if는 ()안에 true이면 {코드블럭}이 실행되는 아이이고, false이면 {코드블럭}이 실행되지 않는다.
그렇다면 if문 안의 ()안에 어떤 아이들을 써야 true이고 false로 받아들일까?

✏️ false : 0, -0, '', null, undefined, NaN ...
✏️ true : -1, 'hello', [], {} ...

ex)

let num; // undefined
if (num) {
	console.log('true!');
}

num && console.log(num);

위의 if문과 &&이 사용된 코드 한 줄은 같은 의미이다. true면 console.log를 출력한다는 내용. if문은 num이 undefined이므로 ()안에 false가 되어서 콘솔로그에 true를 출력하지 않고, &&이 사용된 문장은 왼쪽 조건이 true이면 오른쪽 내용이 실행. 여기서는 num이 undefined이기 때문에 뒷내용이 실행되지 않는다.

let obj;
if (obj) {
	console.log(obj);
}
console.log(obj.name);

콘솔창을 보면 error가 난다. 오류 내용 : Uncaught TypeError: Cannot read property 'name' of undefined at ~ . 프로그램을 실행할때 없는 데이터에 접근하면 프로그램이 죽게 되니까(error) 이 때, && 연산자를 사용하면 유용하다.

let obj = {
	name: 'jjang',
};
obj && console.log(obj.name); // jjang

클래스와 콜백함수

클래스와 콜백함수를 알아보자.

class Counter {
	constructor() {
    	this.counter = 0;
    }
    increase() {
    	this.counter++;
        console.log(this.counter);
        if(this.counter % 5 === 0) {
        	console.log('hey');
        }
    }
}

const coolCounter = new Counter();
coolCounter.increase(); // 1
coolCounter.increase(); // 2
coolCounter.increase(); // 3
coolCounter.increase(); // 4
coolCounter.increase(); // 5 hey

클래스 Counter을 생성하고, coolCounter 인스턴스를 하나 만든다. 그리고 5배수가 되는 숫자가 나올때마다 표시해주고 싶어서 if문을 increaes 메서드에 추가했다. 그럼 위처럼 콘솔창에 출력되는 것을 확인할 수 있다. 하지만 문제점이 있다. Counter 클래스 자체에서 5배의 숫자가 나오면 hey가 나오게 설정해놓았기 때문에 coolCounter을 쓰는 사용자입장에서 hey 말고 다른 내용을 입력하고 싶거나 또는 다른 기능을 넣고 싶다면 그것을 전혀 컨트롤 할 수 없다.

그럼 아래처럼 하면 될까?

class Counter {
	constructor() {
    	this.counter = 0;
    }
    
    increase(runIf5times) {
    	this.counter++;
        console.log(this.counter);
        if(this.counter % 5 === 0) {
        	runIf5times(this.counter);
        }
    }
}

const coolCounter = new Counter();

function printSomething(num) {
	console.log('${num} hey');
}

coolCounter.increase(printSomething); // 1
coolCounter.increase(printSomething); // 2
coolCounter.increase(printSomething); // 3
coolCounter.increase(printSomething); // 4
coolCounter.increase(printSomething); // 5 5 hey

if문에 runIf5times 함수를 따로 넣어서 기능을 추가했고, increse메서드를 사용할 때 콜백함수로 printSomething 함수를 받아와서 실행시키는 것을 볼 수 있다. 우리는 이런식으로 콜백함수를 전달함으로써 우리가 원하는 기능을 수행할 수 있다. 그렇다면 우리는 호출할때마다 콜백함수를 불러줘야할까?
이건 귀찮다..

그래서 보통은 클래스에 우리가 원하는 콜백함수를 생성자 함수의 인자에 넣어서 만든다(포인터로 전달됨).
위의 예제를 가지고 보면, 우리는 Counter 클래스의 constructor 안에서 콜백함수를 전달해 줄 수 있도록 만들 수 있다. (this.callback = runEveryFiveTimes)

class Counter() {
	constructor(runEveryFiveTimes) {
    	this.counter = 0;
        this.callback = runEveryFiveTimes;
    }
    
    increase() {
    	this.counter++;
        console.log(this.counter);
        if(this.counter % 5 === 0) {
        	this.callback && this.callback(this.counter);
        };
    }
}

function printSomething (num) {
	console.log('Wow! ${num}');
}

function alertNum (num) {
	alert(`Wow! ${num}`);
}

const coolCounter = new Counter(printSomething);
coolCounter.increase(); // 1
coolCounter.increase(); // 2
coolCounter.increase(); // 3
coolCounter.increase(); // 4
coolCounter.increase(); // 5 Wow! 5

인스턴스를 만들려고 생성자함수를 쓸 때, 원하는 콜백함수를 전달해준다. 이 말은 우리가 만드는 인스턴스 coolCounter은 counter = 0이고 this.callback = printSomething이라는 초기값을 가지고 오브젝트가 생성된다는 말이다. 그리고 increase가 호출될때마다 숫자가 콘솔창에 찍히고, 또 5배의 숫자가 될때마다 this.callback을 실행한다. 여기서 this.callback이 printSomething이기 때문에 5배의 숫자가 나오면 'Wow! ${num}'이 출력된다.
만약 coolCounter을 만들때 printSomething이 아니라 alertNum함수를 콜백함수로 전달해주면 5배의 숫자가 나올때, 경고창이 나오고 'Wow ${num}'이 창에 나타난다.

💡 key point : 클래스에 우리가 원하는 콜백함수를 전달하면서 만들고, 우리가 필요할 때마다 들으면 된다.

.

✏️ 만약 우리가 생성자함수 new Counter()에 아무런 콜백함수도 전달하지 않으면 어떻게 될까?

: Counter이라는 클래스는 클래스를 만들때마다 콜백함수를 전달받는데 인자가 undefined되기 때문에 type error가 발생한다. 따라서 프로그램이 중단되기 때문에 this.callback이 있으면(undefined가 아닌 상태) this.callback이 실행되도록 만들기 위해서 중간에 "this.callback && this.callback(this.counter)"이라는 코드를 넣어준다.

📍 클래스에 우리가 원하는 기능을 일일이 다 정의하면 사용하는 사람이 컨트롤할 수 없고 재사용하기 어렵다. 하지만 콜백함수를 이용해서 클래스를 만들면 자기가 원하는대로 만들 수 있다. 하나의 클래스로 다양한 오브젝트를 만들어서 서로 다른 기능을 수행하도록 만들 수 있다. >> 클래스의 재사용성을 높여준다.

print기능을 하는 오브젝트를 만들거나 alert기능을 하는 오브젝트를 만들거나 다양하게 만들 수 있다.

const prinCounter = new Counter(printSomething);
const alertCounter = new Counter(alertNum);

출처
드림코딩 아카데미 브라우저 101

profile
프론트엔드개발자가 되고 싶어서 열심히 땅굴 파는 자

0개의 댓글