https://blog.bitsrc.io/understanding-higher-order-functions-in-javascript-75461803bad

 

Understanding Higher-Order Functions in JavaScript

Learn What Higher-Order Functions are and how to use them in JavaScript

blog.bitsrc.io

 

고차함수 ( High Order Function )

JS를 함수형 프로그래밍에 어울리도록 하는 것이 고차함수이다. JS로 코드를 짜봤으면 자신도 모르게 썼을 가능성이 있다. 흔히 쓰인단다.

고차함수를 이해하기 전에 먼저 함수형 프로그래밍이 뭔지 그리고 일급함수( First-Class Functions )을 먼저 알아야 한다.

 

함수형 프로그래밍이 뭔가 ?

함수를 다른 함수의 파라미터 값으로 보내는 것 그리고 리턴도 함수로 하는것 이라고 간단하게 설명할 수 있다.

함수형 프로그래밍을 지원하는 언어는 JavaScript, Haskell, Clojure, Scala, Erlang 등이 있단다.

 

그럼 일급 함수는 뭔가 ?

JS는 함수들을 일급 시민( First-Class citizens )으로 취급한다. 왜냐면 함수는 객체이기 때문이다. 다른 언어도 같은 이유이다.

JS에서 함수는 그냥 객체가 아니라 함수객체이다. 예를 들면

// 함수 선언
function greeting() {
  console.log('Hello World');
}

// 함수 호출
greeting();  //  'Hello World'

// 객체에 하는 것 처럼 함수에 프로퍼티를 추가할 수 있다.
greeting.lang = 'English';

console.log(greeting.lang);  //  'English'

위의 코드의 경우 그렇게 추천하는 방식은 아니다. 설명하려고 만든 코드이다. 객체를 다뤄야 할 일이 있으면 그냥 객체를 따로 만들어 다뤄야 한다.

object, string, number 등으로 뭔가를 하는 모든것은 함수와 함께 할 수 있다. 이것들을 함수의 파라미터로 보낼 수 있다. 변수에 할당 할 수 도 있다. 이게 함수가 일급 함수인 이유이다.

 

고차함수는 다른 함수에서 인자로 또는 리턴 값으로 작동하는 함수라고 할 수 있다. 

예를 들어 Array.prototype.map, Array.prototype.filter, Array.prototype.reduce는 고차함수의 일부이다.

고차함수를 사용하는 것과 사용하지 않는 예제를 보자

 

- Array.prototype.map

map 메소드는 새로운 배열을 만든다. map은 콜백 함수로부터 모든 리턴 값을 받고 이 값들로 새로운 배열을 만든다.

// 고차함수를 사용하지 않은 코드
const arr1 = [1, 2, 3];
const arr2 = [];
for(let i = 0; i < arr1.length; i++) {
  arr2.push(arr1[i] * 2);
}

console.log(arr2); //  [ 2, 4, 6 ]


// 고차함수를 사용한 코드
const arr1 = [1, 2, 3];
const arr2 = arr1.map(function(item) {
  return item * 2;
});

console.log(arr2); // [ 2, 4, 6 ]

 map, reduce, filter 메서드는 언어 안에 내장된 고차함수이다.

 

고차함수를 직접 만들어보자

const strArray = ['JavaScript', 'Python', 'PHP', 'Java', 'C'];
function mapForEach(arr, fn) {
  const newArray = [];
  for(let i = 0; i < arr.length; i++) {
    newArray.push(
      fn(arr[i])
    );
  }
  return newArray;
}
const lenArray = mapForEach(strArray, function(item) {
  return item.length;
});

console.log(lenArray); //  [ 10, 6, 3, 4, 1 ]

 

 

결론적으로 고차함수는 함수를 인자로 받고 함수를 리턴하는 함수이다. 

https://medium.com/dailyjs/i-never-understood-javascript-closures-9663703368e8

 

I never understood JavaScript closures

Until someone explained it to me like this …

medium.com

 

JS에서 코드를 실행한다면 코드가 실행된 범위는 거의 둘 중 하나이다. Global or Function 거의 여기에서 실행된다. 거의

우리가 프로그램을 실행시킨다고 가정하자. 우선 우리는 global 실행 문맥( context, 이거 뭐라해야됨.. ) 안에서 실행하게 될거다. 뭐 몇 개의 변수들이 global안에 선언 되어있을거고, 우린 이걸 호출할거다. 변수 호출을 그렇다 치지만 함수를 실행하게 되면 어떻게 될까?

1. JS는 새로운 실행문맥( 함수 객체 )을 만들거다. 이건 local 실행 문맥이된다.

2. 이 local 실행 문맥은 자신의 몇 개의 변수들을 갖고 있을거고 이 변수들 또한 실행 문맥에 따라 local 변수가 될거다.

3. 만들어진 실행 문맥은 실행 스텍( execution stack )에 들어가게 된다. 

 

스텍이 끝나서 함수가 끝나면 ?

1. 스텍에서 local 실행문맥이 빠져나온다.

2. 함수는 자신의 리턴 값을 자신을 실행한 문맥으로 보낸다. 그 문맥이 실행 문맥이고 이게 global이나 local 실행 문맥일 수 있다. 리턴값이 없다면 undefined가 리턴된다.

3. 다음으로 local 실행 문맥이 박살난다. 그 안에 있던 로컬 변수들 까지 다 박살난다. 없어져버린다. 이게 중요하다. 사라진다 완전.

 

간단한 예시를 보자

let a = 3

function addTwo(x) {
	let ret = x + 2
	return ret
}

let b = addTwo(a)

console.log(b)

1. 우린 글로벌 실행 문백에 변수 a를 선언했다. 그리고 3을 할당했다.

2. 그리고 우린 글로벌 실행 문맥에 새로운 변수 addTwo를 선언했다. 그리고 함수를 addTwo에 할당했다. 함수의 괄호 안에 무엇이 들어있든지 일단 변수에 다 저장한다.

3. 그리고 우린 b라는 변수를 글로벌 실행 문맥에 선언했다. b라는 변수가 선언 되자마자  변수는 일단 undefined 값을 갖게 된다. 

4. 변수 b의 = 연산자 다음에 있는 문법이 함수를 호출한다. 함수가 리턴값으로 뭘 뱉어내던지 그걸 변수 b에 할당한다.

5. 근데 함수가 호출되기 전에 JS는 글로벌 실행 문맥의 메모리에 먼저 가서 addTwo라는 변수가 있는지 확인한다. 있네? 심지어 함수네? 오키 실행.

6. 다음으로 파라미터로 받은 a를 글로벌 실행 문맥의 메모리에서 찾는다. 찾았다. 까보니까 3이 할당 되어 있다. 그럼 3을 들고 addTwo 함수에 넣는다. 이제 실행할 준비가 되었다.

7. 지금 이 순간 실행 문맥이 전환된다. 새로운 local 실행 문맥이 만들어졌다. 이 local 실행 문맥의 이름은 addTwo가 된다. addTwo 실행 문맥은 콜 스텍에 넣어진다. 

8. addTwo 실행문맥은 이제 뭘 하게 될까? 제일 먼저 function addTwo( x )의 x를 로컬 실행 문맥에 변수로 선언한다. 그리고 파라미터로 받은 3을 변수 x에 넣는다. 

9. 다음으로 ret 변수를 선언한다. 그리고 x를 찾기위해 로컬 실행 문백을 뒤진다. 3을 찾으면  x자리에 갖다 놓는다. 그리고 2를 더한다. 5가된다. 이 5를 변수 ret에 할당한다.

10. 그 다음으로 변수 ret의 값을 리턴한다. 리턴 하려면 ret을 찾아야 한다. 로컬 실행 문맥에서 찾는다. 찾았다. 이게 5를 갖고 있다. 찾은 5를 리턴한다. 그리고 함수가 종료된다.

11. 함수가 종료되고 로컬 실행 문맥 역시 박살난다. 사라졌다. 변수 x와 ret도 없어졌다. 콜 스텍에서 빠져나오고 리턴 값이 addTwo 실행 문맥을 호출한 실행 문맥으로 던져진다. 

12. 그래도 박살난 addTwo 실행 문맥이 남긴 유산은 있다. 바로 5. 이걸 변수 b에 넣는다. 

13. 콘솔 로그가 b를 콘솔에 찍어낸다.

 

여기까지가 저 간단한 코드를 JS가 실행하는 방법이다. 내가 참조한 블로그에서는 클로저를 알기 위해서 더 많은 예제를 봐야 한다고 했다.

오키 묻고 더블로 간다.

 

Lexical scope .. 이걸 도대체 한국말로 뭐라 해야 하는지 모르겠다. 그냥 렉시칼 스코프라고 한다.

아래 예제이다.

1: let val1 = 2

2: function multiplyThis(n) {
3:   let ret = n * val1
4:   return ret
5: }

6: let multiplied = multiplyThis(6)

7: console.log('example of scope:', multiplied)

JS의 복잡함은 이루 말할수 없다. this 때문인거 같다. 

로컬과 글로벌 실행 문맥에 변수가 있다. 그냥 이렇게 가정을 해보자. 만약에 로컬 실행 문맥 안에서 변수를 못 찾는다면( 없으니까 못찾는거다. 숨어있어서 못찾는거 그런거 아니다 ) JS는 이 실행 문맥을 호출한 실행 문맥으로 거슬러 올라간다. 찾을 때 까지 계속 올라간다. 만약 계속 못 찾았다 ? 그럼 undefined를 뱉어낸다.

이걸 알고 위의 예제를 보자

1. val1이라는 변수를 글로벌 실행 문맥에 선언한다. 그리고 숫자 2를 할당한다.

2. multiplyThis라는 새로운 변수를 선언한다. 그리고 정의한 함수를 변수에 할당한다.

3. multiplied라는 새로운 변수를 글로벌 실행 문맥에 선언한다.  

4. 그리고 글로벌 실행 문맥의 메모리에서 multitplyThis라는 변수를 꺼낸다. 꺼낸게 함수다. 파라미터로 6을 던져주면서 실행한다.

5. 새로운 실행 문맥이 만들어진다. 이 문맥의 이름은 multitplyThis이다.

6. 로컬 실행 문맥을 실행하면 처음으로 변수 n을 선언한다. 그리고 파라미터 값 숫자 6을 할당한다.

7. 다음으로 ret이 이름인 변수를 선언한다. 

8.9.  로컬 실행문맥의 메모리에서 변수 n과 val1을 찾는다. n은 6이라고  찾았는데 val1을 못 찾았다. 어찌해야되나? 문맥을 타고 올라간다. 올라가니 val1이 있다. 찾았다. 찾은 변수의 값을 곱한다. 12가 됬다. 12를 ret에 넣는다.

10. ret을 리턴한다. 로컬 실행 문맥이 박살난다. val1은 안 박살난다. 로컬 실행 문맥이 아니기 때문이다.

11. 리턴된 숫자 12가  mltipied 변수에 할당된다.

12. 12를 콘솔로 찍어낸다.

 

위의 과정중에서 변수를 찾기 위해 문맥을 타고 올라갔었다. 이 현상의 공식적인 이름이 바로 Lexical scope이다.

 

 

함수는 뭐든지 리턴 할 수 있다. 배열이든 함수든 변수들 뭐시기든 리턴할 수 있다.

아래 예제를 보자

 1: let val = 7
 
 2: function createAdder() {
 3:   function addNumbers(a, b) {
 4:     let ret = a + b
 5:     return ret
 6:   }
 7:   return addNumbers
 8: }
 
 9: let adder = createAdder()
 
10: let sum = adder(val, 8)

11: console.log('example of function returning a function: ', sum)

1. 글로벌 실행 문맥에 val 변수를 선언하고 7을 할당한다.

2. 글로벌 실행 문맥에 변수 createAdder이름의 변수를 선언한다. 그리고 함수를 변수에 할당한다. 근데 여기서 { } 안에 있는 걸 건드지 않는다. 

3. 글로벌 실행 문맥에 adder 변수를 선언한다. 이 순간 일시적으로 undefined가 adder 변수에 할당된다.

4. 그리고 createAdder()를 호출 하기 위해 글로벌 실행 문맥 안의 메모리를 뒤진다. 찾았으면 꺼내온다.

5. 함수를 실행한다. 이 때 새로운 로컬 실행 문맥이 만들어진다. js엔진은 이 로컬 문맥을 콜 스텍에 집어 넣는다. 함수는 받은 파라미터가 없다. 그럼 바로 함수 내부로 들어간다.

6. 우린 또 addNumbers 변수를 로컬 실행 문맥에 생성하고 저장한다. addNumbers 변수는 바로 이 createAdder 실행 문맥에만 존재한다.

7. createAdder 함수는 addNumbers 변수를 리턴한다.그리고 로컬 실행 문맥이 없어진다.

8. 원래 addNumbers 변수는 없어진다. 그런데 정의된 함수는 여전히 존재한다. 이게 adder 변수에 할당된다. 

9. 새로운 변수 sum을 글로벌 실행 문맥에 선언한다. 그리고 일시적으로 undefined가 할당된다.

10. sum 변수에 adder 변수에 있는 함수를 실행해야 한다. 먼저 글로벌 실행 문맥의 메모리를 찾아본다. 정의된 함수가 있다. 가져온다.

11. 함수를 실행하기 전 2개의 파라미터를 보낸다. 7 과 8이다.

12. 이제 진짜 함수를 실행한다. 새로운 로컬 실행 문맥이 생성된다. 이 로컬 실행 문맥에 a, b 2개의 변수가 만들어진다. 각각 7과 8이 할당된다.

13. ret 이름의 새 변수가 선언된다. 

14. a와 b를 더한다. 결과는 15이고 이걸 ret변수에 할당한다.

15. 함수로부터 ret 변수가 리턴된다. 그리고 이 로컬 실행 문맥이 파괴된다. 그리고 콜 스텍에서 빠진다. 변수 a, b, ret은 이제 없어진다.

16. 리턴된 값은 sum 변수에 할당된다.

17. sum을 콘솔에 찍는다.

 

위의 과정에서 우리가 알 수 있는건 정의된 함수는 변수에 저장된다라는 점, 정의된 함수는 호출되기 전 까지는 프로그램에 보이지 않는다는 점이다.  또한 함수가 실행되는 매 순간 새로운 로컬 실행 문맥이 생성된다는 것이다. 이 로컬 실행 문맥은 함수가 종료되면 사라진다. 

 

 

이제 드디어 클로저다.

 1: function createCounter() {
 2:   let counter = 0
 3:   const myFunction = function() {
 4:     counter = counter + 1
 5:     return counter
 6:   }
 7:   return myFunction
 8: }
 
 9: const increment = createCounter()
 
10: const c1 = increment()

11: const c2 = increment()

12: const c3 = increment()

13: console.log('example increment', c1, c2, c3)

1. 글로벌 실행 문맥에 createCounter 변수를 만든다. 그리고 함수 정의를 할당한다.

2. 새로운 변수 increment를 선언한다.

3. 글로벌 실행 문맥 메모리에서 createCounter 변수를 찾는다. 변수에 정의 된 함수가 있다.

4. createCounter 로컬 실행 문맥이 생성된다. 

5. 로컬 실행 문맥에 counter 변수를 선언하고 0을 할당한다.

6. 새로운 변수 myFunction을 선언한다. 변수의 내용은 또 다른 함수 정의이다. 지금 우리는 클로저를 만들었다. 클로저는 현재 실행문맥( 지금은 createCounter 실행 문맥 )의 변수들을 갖고있다. 지금의 경우 counter 변수를 갖고 있다.

7. myFunction 변수를 리턴하면서 createCounter 실행 문맥이 삭제된다. myFunction과 counter 변수는 존재하지 않는다. 

8. increment 변수에 createCounter의 리턴 값이 할당된다. increment는 함수 정의( myFunction이 갖고 있던 함수 정의, 이게 클로저가 됨 )를 갖고 있다. 

9. 새로운 변수 c1을 선언한다. 

10. 글로벌 실행 문맥에서 increment 변수를 찾는다. 찾았다. 이거 함수다. 

11. 새로운 실행 문맥을 만든다. 함수에 파라미터가 없다. 함수를 실행한다.

12. counter = counter + 1 //// 이라는게 있다. counter를 찾는다. 근데 글로벌이나 로컬 실행 문맥을 찾기 전에 먼저 클로저( myFunction였던 함수 정의를 포함한 그 스코프의 모든 내용) 내부를 체크한다. 잉? 클로저가 변수 counter를 갖고 있네 ? 값이 0 이네? 그럼 1을 더한다.  이제 클로저의 counter 변수는 1이된다. 

13. 다음으로 counter 값 즉 1을 리턴한다.

14. 리턴된 값 1 이 c1에 할당된다.

15. 나머지 c2, c3 반복 그리고 로그 찍힘

 

클로저 내부에 저장된 정보는 로컬 실행 문맥처럼 없어지지 않기 때문에 ( 뭔가는 그 정보를 계속 갖고 있으니까 ) counter에 1을 계속 추가하는게 가능하다. 

즉 클로저는 함수 생성당시 같은 스코프에 존재하는 모든 변수의 수집( collection )이다. 함수가 함수를 리턴하면서 실행 문맥이 생성되고 파괴되는  그 점을 이용하여 클로저를 사용한다. 

 

https://hackernoon.com/javascript-and-functional-programming-pt-3-pure-functions-d572bb52e21c

 

Javascript and Functional Programming — Pt. 3: Pure Functions

Purity Note: This is part of the “Javascript and Functional Programming” series on learning functional programming techniques in JavaScript ES6+. Check out the Part 4 , on currying function in JS here.To start from the ground up check out Damn, this feels 

hackernoon.com

 

프로그램 버그의 상당수는 I/O, data 변형에서 비롯된다. 이러한 버그들을 최소화 하고싶지 않나?

복잡하고 휘발성 짙은 코드들을 지양하고, 단순하고 직관적이며 결과가 투명한 코드를 위해 탄생한 개념이 바로 Pure Function이다.

같은 인풋을 넣으면 항상 같은 아웃풋을 제공한다. 깔끔하다. 투명하다. pure하다.

const add = (x, y) => x + y // This is a pure function

 

아무튼 pure함수는 테스트에 용이해야하고 가독성이 있어야 하며 항상 투명한 결과를 뱉어야 하고 불변해야 한다.

 

https://medium.com/poka-techblog/simplify-your-javascript-use-map-reduce-and-filter-bd02c593cc2d

 

Simplify your JavaScript – Use .map(), .reduce(), and .filter()

If you haven’t heard of .map(), .reduce(), and .filter() in JavaScript, you might want to learn to use it!

medium.com

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

 

Array.prototype.reduce()

reduce() 메서드는 배열의 각 요소에 대해 주어진 리듀서(reducer) 함수를 실행하고, 하나의 결과값을 반환합니다.

developer.mozilla.org

 

- .map()

map함수는 배열 안의 요소들을 사용하여 새로운 배열을 만들어낸다.

 

예제1

const numbers = [2, 4, 8, 10];
numbers.map( x => x * 2 );

multiple // [4, 8, 16, 20]

예제2

// What you have
var officers = [
  { id: 20, name: 'Captain Piett' },
  { id: 24, name: 'General Veers' },
  { id: 56, name: 'Admiral Ozzel' },
  { id: 88, name: 'Commander Jerjerrod' }
];

// What you need
[20, 24, 56, 88]

const officersIds = officers.map(officer => officer.id); // [20, 24, 56, 88]

map을 사용한 배열의 길이와 리턴된 배열의 길이가 같아야한다.

 

 

- reduce()

배열의 각 요소들을 체크하여 배열이 아니라 하나의 결과값을 반환한다.

array.reduce( (accumulator, current, index, source) => {
  // return logic here
}, initialValue)

// accumulator -> 리턴된 값을 저장한다.
// current -> 현재 배열 요소
// index -> 현재 배열의 인덱스 값 / 잘 안씀
// source -> 원재 주어진 배열 / 잘 안씀

 

예제1

const euros = [1, 2, 3];

const sum = euros.reduce((total, amount) => total + amount); 

sum // 6

이 예제에서 리듀스는 2개 total과 amount 파라미터를 받았다. 리듀스는 반복문 처럼 각 배열 값을 돈다.

리듀스 함수가 실행되면 total값은 배열의 가장 왼쪽에 있는 값으로 할당된다. 그리고 amount는 그 다음 순서의 값이 된다.

여기 예제에서는 최초 total은 1이고 amount는 2이다. 그래서 둘이 합쳐 3을 리턴한다. 다시 반복이 시작될 때 total은 리턴된 3 값으로 되어있고 amount는 다음 배열로 넘어가 3이된다. 여기서 리턴하면 6이된다. 오른쪽 배열의 값이 이제 없으므로 리듀스함수는 종료한다.

예제2

const euros = [1, 2, 3, 4];

const multi = euros.reduce( ( total, amount ) => {
		total.push( amount * 2 );
        return total;        
    }, []); 

multi // [2, 4, 6, 8]

euros배열의 값들에 2를 곱해 다시 배열로 뱉었다. multi에 표현된 리듀스함수의 끝 인자 initialvalue는 total의 초기값을 정한다. 여기서 빈 배열 [ ] 을 초기값으로 줘서 total은 [ ]이 된거다. amount는 주어진 배열의 처음 값인 1 부터 시작하는거다.

이런식으로 리듀스 내부에 조건을 줘서 이것저것 할 수 있다.

( total, amount, initialvalue 파라미터의 이름들은 그냥 예제에서 쓴 이름이다 순서로 기억해야한다. )

주어진 값이 배열이라고 배열로만 뱉는건 아니다. 객체로 만들어서 뱉을 수도 있고 객체를 받을 수 도 있다.

 

 

- filter()

필터 메서드는 조건을 통과한 요소들을 대상으로 새로운 배열을 반환한다.

let numbers = [1,2,3,4,5]
numbers = numbers.filter(numb=> numb % 2 == 0);

numbers // [2, 4]

 

http://adripofjavascript.com/blog/drips/basic-inheritance-with-object-create.html

 

Basic Inheritance with Object.create - A Drip of JavaScript

Basic Inheritance with Object.create Originally published in the A Drip of JavaScript newsletter. A few issues back we looked at how to implement basic inheritance with constructors. In this issue, we'll look at how to do the same with the newer Object.cre

adripofjavascript.com

전 포스팅에 생성자 함수를 이용하여 상속 기능을 하는걸 봤었다.

// 함수를 만든다
function SuperHuman (name, superPower) {
    this.name = name;
    this.superPower = superPower;
}

// 생성자 함수의 prototype기능을 사용하여 SuperHuman함수에 usePower메서드를 만든다.
SuperHuman.prototype.usePower = function () {
    console.log(this.superPower + "!");
};

// banshee가 SuperHuman함수를 상속(?) 받았다.
var banshee = new SuperHuman("Silver Banshee", "sonic wail");

// 상속받은 속성인 usePower() 메서드를 쓸 수 있다.
banshee.usePower();  //  sonic wail!

 

위의 코드를 Object.create()를 통해 구현하면

// 메서드를 포함한 함수를 만든다.
var superHuman = {
    usePower: function () {
        console.log(this.superPower + "!");
    }
};

// Object.create()를 사용하여 superHuman()함수를 상속 받는다.
var banshee = Object.create(superHuman, {
    name: { value: "Silver Banshee" },
    superPower: { value: "sonic wail" }
});

// 상속 받음.
banshee.usePower(); // sonic wail!

모든 객체는 Prototype으로서 사용될수 있기 때문에 Object.create()를 사용하면 체인처럼 상속을 계속 할 수 있다.  

 

생성자 함수와의 차이점은 Object.create()는 instanceOf에서 발견되지 않는다는 점이다. 대신에 isPrototypeOf 메서드를 써야한다.

superHuman.instaceOf( superHero ) // false
superHuman.isPrototypeOf( superHero )  // true

superHuman은 superHero의 상속 고리기 때문이다. ( part of superHero's prototype chain ) 

Object.create()는 IE8에서 동작하지 않는다. 이상 버전에서만 작동한다. 크롬은 되겠지 아마도..

 

 

- Objcet.assign() 

이건 타겟 객체에 기존의 객체들을 병합한다.

// 더미 객체 생성
var obj1 = {a:1};
var obj2 = {b:2};
var obj3 = {c:3};

// newObj에 객체들 할당
var newObj = Object.assign(obj1, obj2, obj3);


console.log(newObj); // {a: 1, b: 2, c: 3}
console.log(obj1); // {a: 1, b: 2, c: 3}


----------------------------------------------
var obj1 = {a:1};
var obj2 = {b:2};
var obj3 = {c:3};

var newObj = Object.assign({}, obj1, obj2, obj3);

console.log(newObj); // {a: 1, b: 2, c: 3}

첫 번째 인자에 빈 객체를 넣으면 newObj에 인자로 받은 객체들을 복사한다. 

첫 번째 인자의 객체가 값이 있다면 해당 객체의 값도 바뀐다.

 

참조 사이트

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

 

Object.assign()

Object.assign() 메소드는 열거할 수 있는 하나 이상의 출처 객체로부터 대상 객체로 속성을 복사할 때 사용합니다. 대상 객체를 반환합니다.

developer.mozilla.org

 

 

https://www.digitalocean.com/community/tutorials/understanding-prototypes-and-inheritance-in-javascript

 

How To Work with Prototypes and Inheritance in JavaScript | DigitalOcean

JavaScript is a prototype-based language, meaning object properties and methods can be shared through generalized objects that have the ability to be cloned and extended. This is known as prototypical inheritance and differs from class inheritance. Am

www.digitalocean.com

JS는 prototype기반 언어이다. 

이게 뭔 말이냐면 객체의 프로퍼티나 메소드가 공유될 수 있다는 얘기이다. 상속같이 사용된다.

이걸 prototypical inheritance이라 부르고 이건 class inheritance와는 다르다. JS는 php, java, python등의 클래스 기반 언어와는 다르다. ( 클래스 문법은 있지만 기본 컨셉이 다르다. )

 

- JavaScript Prototypes

모든 JS 객체는 [[Prototype]]이라 부르는 내부 프로퍼티를 갖고 있다. 

[[ ]] 는 코드에 직접적으로 접근할 수 없는 내부 프로퍼티를 나타낸다.

let x = {};

위의 코드가 우리가 보통 새로운 객체를 만드는 방법이다. 그런데 또 다른 방법으로 새로운 객체를 만들 수 있다.

let x = new Object();

 

객체 x의 내부 프로토타입이 뭐가 있는지 확인해보자

// .getPrototypeOf()를 사용하여 확인 할 수 있다.
Object.getPrototypeOf( x );

// 결과 값
// {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, …}

위의 코드를 보면 여러가지 함수들이 존재한다는 것을 알 수 있다.

또 다른 방법으로 확인해보자. __proto__ 라는 프로퍼티를 통해 확인 할 수 있다. 이건 객체의 내부 [[Prototype]]을 보여주는 프로퍼티이다.

( __proto__는 레거시이다. 상업용 프로그램에는 포함되면 안된다. 모든 브라우저의 엔진안에 존재하는 건 아니다. )

// __proto__ 를 통해 내부 프로퍼티 확인
x.__proto__;

// 결과 값
// {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, …}

 

[[Prototype]]은 다른 객체들과의 연결고리가 된다는 점에서 중요하다. 이건 Data, Array 처럼 객체 안에 내장되어 있다.

 

- Prototype Inheritance

만약 우리가 어느 객체의 프로퍼티나 메소드에 접근하려고 한다면, JS는 일단 객체 그 자체를 찾아보고 없다면 객체의 [[Prototype]]을 찾는다. 이것도 없다면 연결된 모든 객체의 [[Prototype]]을 체크한다. 연결된 객체가 없을 때 까지 계속 찾는다. 아무것도 안나오면 null이 반환된다.

모든 객체들은 Object( 부모 Object, 선언한거 말고 내장된거 )의 프로퍼티와 메소드를 상속받는다. 

x.toString();  // [object Object]

위의 코드에서 우리는 빈 객체인 x를 만들었고 아무것도 없는데 .toString() 메소드를 불러냈다. 이게 부모 객체인 Object로부터 자동으로 상속받은 메소드라 불러낼 수 있는거다.

let y = [];   OR   let y = new Array();

y.__proto__;  // [constructor: ƒ, concat: ƒ, pop: ƒ, push: ƒ, …]

Array도 __proto__로 뭐가 들어있는지 알 수 있다. Array도 빈 객체와 비슷하게 부모 Array에게서 다양한 메소드를 자동 상속받는다.

계층은 이렇게 생겼다.

y.__proto__ === Array.prototype;            // true
y.__proto__.__proto__ === Object.prototype; // true

// 위와 같다
Array.prototype.isPrototypeOf(y);      // true
Object.prototype.isPrototypeOf(Array); // true

 

대충 요약하자면 모든 JS객체는 숨겨진 내부의 [[Prototype]] 프로퍼티가 내장되어 있고 이를 통해서 객체들은 상속 비슷한 능력을 사용할 수 있다. 암튼 이렇게 prototype을 연결연결하여 객체를 만들어 사용하는 것을 Prototype Chain이라 부른다.

 

https://javascript.info/constructor-new

 

Constructor, operator "new"

 

javascript.info

보통 { @#$ } 문법이 하나의 객체를 만든다. 그리고 다수의 비슷한 객체를 만들 때 constructor함수나 new 연산자(operator)를 사용한다.

 

- Constructor Function

consturcor함수는 첫 글자를 대문자로만들고 new 연산자를 사용하여 실행하게 된다. ( 생성자 함수, 즉 JAVA로 치면 부모역할을 할 함수를 만들 때 관습이다. )

function User(name) {
  // this = {};   --> Step.1
  
  // this에 프로퍼티 할당  --> Step.2
  this.name = name;
  this.isAdmin = false;
  
  // return this;  --> Step.3
}

let user = new User("Jack");

alert(user.name); // Jack
alert(user.isAdmin); // false

User함수가 new를 통해 실행 되는 단계를 보면

Step.1 /  this에 할당된 새로운 빈 객체가 만들어진다.

Step.2 /  User함수가 실행되고 this에 프로퍼티를 할당한다.

Step.3 /  this의 값이 리턴된다.

위의 방식으로 우리는 비슷한 객체를 쉽게 여러개로 만들 수 있다. Reusable하다.

 

생성자 함수에 return이 있을 수도 있는데 이 때는 return에 primitive 타입의 값들은 리턴이 안된다.

물론 생성자 함수에 메서드를 만들어 사용할 수 있다.

 

https://appendto.com/2016/10/what-is-the-instanceof-operator-in-javascript/

 

What Is the Instanceof Operator in JavaScript? | appendTo

The instanceof is an important operator the checks a pre-existing and defined value then returns true if the object is its specified object type, or false if it isn’t. A …

appendto.com

- Instanceof Operator

instanceof은 기존에 존재하거나 정의된 값을 체크하고 맞다면 true 아니라면 false를 반환하는 연산자이다.

var colorTrue = new String(“black”);
colorTrue instanceof String; // returns true
 
var colorFalse = “green”; // object is not specified 
colorFalse instanceof String; // returns false, it is not a string.
 

위의 예제처럼 colorTrue 변수는 생성자 함수로 String값인 black을 만들어서 true가 나왔다.

colorFalse의 green은 String이지만 String값을 정의한게 아니어서 false가 나온다.

http://blog.brew.com.hk/what-is-this-in-javascript/

 

What is 'this' in JavaScript?

Classes in JavaScript - Explained This article is an extended adaptation from Understanding Prototypes in JavaScript. This article is very in-depth, and may require…

blog.brew.com.hk

 

this !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

This Has Idiot Suck.

 

js에서 this란 '현재 실행하는 범위( context, scope, value,  whatever you want to call, cuz of that fxckin tricky )' 라고 아주 간단하게 설명할 수 있다. 

만일 this가 함수나 클래스 밖에 있다면 개념적으로 this는 global 실행 범위이다.  웹 브라우저에서 실행하면 window이다.

node.js에 있다면 export 객체 즉 비어있는 객체이다.

 

만약 함수안에 있다면 this의 값은 함수가 어떻게 호출되는지 거기에 달려있다.

const bar = function () { console.log(this) };
const poo = function () { 'use strict'; console.log(this); };
const foo = {};
foo.bar = bar;

bar(); // window
poo(); // undefined
foo.bar(); // bar

함수가( bar() )가 글로벌 스코프( window )에서 호출된다면 this는 window이다. 함수 블록은 영향을 못 미친다. 

엄격하게 검사한다면 ( 'use strict' ) this는 undefined로 로그 찍힌다. 뭐 이따구인지 모르겠다.

 

생성자함수( Function Constructor )에서 함수 안에 this는 생성된 객체( 모든 함수는 객체이다. in js )를 가리킨다.

// es5, es6로 작성된 아래 코드는 같은 결과다. 문법설탕됨

// ES5 ( ECAMAScript2009 )
function Car(make) {
  this.make = make;
}

// ES6 ( ECAMAScript2015 )
class Car {
  constructor(make) {
    this.make = make;
  }
}

const myCar = new Car("BMW");
myCar.make; // "BMW"

생성자 함수 안에 있는 this는 Car() 이 함수를 가리킨다. ( ?? )

 

근데 JS가 Prototype기반 언어라는걸 알고있는가 ?

JS에는 prototype link와 prototype object가 있는데 이걸 통칭해서 prototype이라 부른다.

( 나중에 prototype에 대한 내용은 #17 Prototype Inheritance and Prototype Chain [33 Concepts Every JavaScript Developer Should Know ]에서 포스팅 할거다 )

JS에 클래스 문법은 있지만 개념은 없다 ( ES6/7 기준 ). 그래서 다른 언어처럼 상속을 하고 싶을 때 JS는 함수와 new를 통해 상속을 흉내낸다.

- prototype와 this의 콜라보 예제

// 대충 공유 할 함수 하나 만든다.
// 이 생성자 함수 사람안에 this는 window가 아니라 사람을 가리킨다.
function 사람( someting ) {
	this.someting = someting;
}   

// 사람이 공유하게 될 눈, 코를 어딘가에 존재하는 사람함수의 prototype에 넣는다.
사람.prototype.눈 = 2;
사람.prototype.코 = 1;

// 사람이 공유하게 될 눈몇개임 함수를 만들어 prototype에 넣는다.
// 여기에 this는 눈몇개임 함수(객체)를 부르는 함수(객체)의 인스턴스를 가리킨다.
사람.prototype.눈몇개임 = function() { return this.someting + this.눈 };


// 선언한 변수 김씨, 박씨는 new를 사용하여 사람함수를 생성한다. 상속받는 것 처럼 생각하면된다.
var 김씨 = new 사람("김씨는 눈이 ");
var 박씨 = new 사람("박씨는 눈이 ");


// 결과
김씨.눈몇개임();  // '김씨는 눈이 2';

 

함수가 객체의 메소드라면 this 또한 객체를 가리킨다.

const foo = {};
foo.bar = function () { console.log(this); };
foo.bar(); // foo

빈 foo함수 객체가 있다. ( JS에서 모든 함수의 진짜 타입은 객체이다. )

foo함수(객체)에 bar메소드를 만들고 this를 리턴했다. bar에서 리턴한 this는 결국 foo를 가리킨다. ㅇㅋ? ㅇㅋ

 

Binding this.

.bind 아는가 ?

만약 this를 특정 객체에 묶고 싶다 ? this가 막 날라다닌데 고정시키고 싶다 ? 그럼 bind 메소드를 써야한다.

const foo = function () { console.log(this) };
const boundFoo = foo.bind("always me!"); // always me!를 boundFoo에 묶어버렸다.

foo(); // window
boundFoo(); // "always me!"

const a = {
  b: foo,
  boundB: boundFoo
}

a.b(); // a
a.boundB(); // "always me" <- a를 안 가리킨다.

bar = function () {};
bar.prototype.x = foo;
bar.prototype.boundX = boundFoo;

const myBar = new bar();

myBar.x(); // myBar
myBar.boundX(); // "always me" <- myBar를 안 가리킨다.

 

.bind는 ( Function.prototype.bind ) 객체에 묶인 this와 함께 new 함수를 리턴한다. 근데 원래 this가 가리켜야 하는 함수를 호출하지 못 한다. 

이 때문에 .call ( Function.prototype.call ) , .apply ( Function.prototype.apply ) 함수를 사용한다.  this를 고정시키지는 않지만 this를 특정 객체에 묶는다.

call, apply는 받는 첫 번째 인자를 함수 내부에서 사용할 this로 만들어준다. 차이점은 apply는 첫 번째 인자를 제외한 나머지를 배열로 받는다는 점이다. 이거 빼고는 차이점이 없다.

call, apply 예제확인

// a, b 객체 생성
const a = { here: 'a!!' };
const b = { here: 'b@@' };

// this.here에 따라 값이 바뀌도록 만듬
const say = function( whoyouare ) {
	console.log( ' my this belongs to ${this.here}, im ${ whoyouare }' );
}

// say함수의 this가 a를 가리킴
say.call( a, "playCall" ); // my this belongs to a!!, im playCall
say.apply( a, ["playApply"] ); // my this belongs to a!!, im playApply

// say함수의 this가 b를 가리킴
say.call( b, "playCall" ); // my this belongs to b@@, im playCall
say.apply( b, ["playApply"] ); // my this belongs to b@@, im playApply

// this가 가리킬게 없음
say("play"); // my this belongs to , im play

 

https://www.guru99.com/how-to-use-dom-and-events-in-javascript.html

 

JavaScript DOM Tutorial with Example

In this example program, we will reverse a string entered by a user. We will create a function to...

www.guru99.com

 

DOM ( Document Object Model ) 

이게 돔이다.

 

HTML이 돔인가 ? 아니다. 

웹 브라우저들은 페이지를 로드할 때 트리구조로 돔을 만든다. 브라우저에 의해 트리구조로 변경된게 바로 DOM이다.

JS는 웹 페이지에 있는 모든 엘리멘트들에 접근 할 수 있다. DOM을 사용하면서 말이다. 돔을 사용하면서 엘리멘트와 속성값을 만들고 바꾸고 제거한다. 또한 이벤트를 작동시키고 새로운 이벤트를 만들기도 한다.

<html>
<head>
	<title>DOM!!!</title>
</head>
<body>
  <h1 id="one">Welcome</h1>
  <p>This is the welcome message.</p>
  <h2>Technology</h2>
  <p>This is the technology section.</p>
  
  <script type="text/javascript"> 
  <!-- JS 코드 -->
		var text = document.getElementById("one").innerHTML;
		alert("The first heading is " + text);
  </script>
  
</body>
</html>

위의 코드를 보면 html로 만들어진 페이지에 여러 엘리멘트들이 있다. 

여기서 JS는 getElementById()와 innerHTML을 통해 특정 id의 엘리멘트에 접근하고 있다.

id 말고 tag로 접근 할 수 도 있다.

<html>
<head>
	<title>DOM!!!</title>
</head>
<body>
  <h1>Welcome</h1>
  <p>This is the welcome message.</p>
  <h2>Technology</h2>
  <p id="second">This is the technology section.</p>
  
  <script type="text/javascript">
	var paragraphs = document.getElementsByTagName("p"); 
    alert("Content in the second paragraph is " + paragraphs[1].innerHTML);
    document.getElementById("second").innerHTML = "The orginal message is changed.";
  </script>
  
</body>
</html>

getElementsByTagName() 을 통해 2번째 p 태그의 속성값을 바꿨다.

 

뭐 이런것들이 있다.

 

https://medium.com/techtrument/a-comprehensive-primer-on-binary-computation-and-bitwise-operators-in-javascript-81acf8341f04

 

A Comprehensive Primer on Binary Computation and Bitwise Operators in Javascript

Bitwise operators, though they take a few minutes to learn, are a fun way to make your code more space and time-efficient. They can also…

medium.com

 

비트의 연산자( Bitwise operators )는 32비트 시퀀스(4byte)로 이루어진 피연산자( oerands )를 다루는 연산자이다. 근데 리턴하면 표준 JS숫자 값으로 리턴한다.

이게 뭔 말이냐면 정수값을 같은 숫자의 2진법 처럼 조작한다는 얘기이다. 조작은 2진법 처럼 표현은 일반 10진법 숫자처럼.

우리가 ( +, -, *, / )를 사용할 때 실제로는 ( >>, <<, &, |, ^, -, ~ )를 사용한다.

 

2진법 설명은 넘어간다. 우린 손가락 10개니까

 

10진법 9를 2진법으로 바꾸면 1001이된다. 근데 여기다 ~(NOT)을 붙이면 0110 즉 6( 10진법에서 )이된다. 우리 생각과는 좀 다르다.

근데 비트 연산자는 32비트로 이루어진 피연산자를 연산한다고 했다. 9는 000000000000000000000000000001001이고 ~9는 1111111111111111111111110110이다.

현대 연산에서는( in modern computing ) 비트의 가장 왼쪽에 있는 숫자는 ~(NOT)을 판단하는 숫자이다. 

2진법  :   1001 = 1000 + 000 + 00 + 1
                       
10진법 :    -7  =  -8  +  0  +  0 + 1

 

자바스크립트에서 이 비트를 다루는 연산자를 알아보자

& Operator (AND)
example: 1100 & 1001 = 1000

| Operator (OR)
example: 1100 | 1001 = 1101

^ Operator (XOR)
example: 1100 ^ 1001 = 0101

 

& 연산자는 각 비트의 자리값을 비교한다. 자리의 값들이 같으면 1을 반환하고 다르면 0을 반환한다.

| 연산자는 같은 자리에 0이 있으면 0을 반환하고 나머지 경우에는 1을 반환한다.

^ 연산자는 &와 반대이다. 자리값이 같으면 0을 반환하고 다르면 1을 반환한다.

 

다른 것도 있다.

<< Operator (Left shift)
example: 1101 << 2 = 110100

>> (Sign-Propagating Right shift)
example1: 110100 >> 2 = 111101
example2: 0101 >> 2 = 0001

>>> (Zero-Fill Right shift)
example1: 110100 >>> 2 = 001101
example2: 0101 >> 2 = 0001

~ (NOT)
example: ~1001 = 0110

- (NEGATIVE)
example: -1001 = 0111

 

 

사실 비트단위 연산을 할 일이 없어서 잘 안쓰는데 이런 것들을 알아두면 좋단다. 예를 들어 ture, false가 들어있는 배열을 다룰 경우 배열 말고 위의 연산자를 쓰면서 비트로 하는게 편하다고 한다. 잘 모르겠다.

+ Recent posts