언어/자바스크립트

[Javascript] call, apply 메서드, Array.from

youngble 2022. 6. 19. 13:20
Function.prototype.call(thisArg[, arg1[, arg2[, ...]]])

call / apply 메서드

call 메서드와, apply 메서드기능적으로 완전히 동일하다.

call 메서드apply 메서드는 메서드의 호출 주체인 함수를 즉시 실행하도록 하는 명령이다. 

이때 첫번째 인자를 this로 바인딩하고, 그 이후 인자들을 호출할 함수의 매개변수로 한다.

명시적 this 바인딩 지정

함수를 그냥 실행하면 this는 전역객체를 참조하지만 call, apply 메서드를 사용하면 임의의 객체를 this로 지정할 수 있다.

차이점은 call 경우 첫번째 인자 이후의 모든 인자들을 호출할 함수의 매개변수로 순서대로 지정하는 반면, apply는 두번째에 배열로 인자를 받아 호출할 함수의 매개변수로 지정한다. 

call 메서드의 형식

Function.prototype.call(thisArg[, arg1[, arg2[, ...]]])

apply 메서드의 형식

Function.prototype.apply(thisArg[, argsArray])

call / apply 메서드 예시 코드

var func = function (a, b, c) {
  console.log(this, a, b, c);
};

func.call({ x: 1 }, 4, 5, 6);
func.apply({ x: 1 }, [4, 5, 6]);

var obj = {
  a: 1,
  method: function (x, y) {
    console.log(this.a, x, y);
  },
};

obj.method(2, 3);
obj.method.call({ a: 4 }, 5, 6);
obj.method.apply({ a: 4 }, [5, 6]);

결과

{ x: 1 } 4 5 6
{ x: 1 } 4 5 6
1 2 3
4 5 6
4 5 6

call / apply 메서드의 활용

유사배열객체(array-like object)에 배열메서드 사용

var obj = {
	0: 'a',
    1: 'b',
    2: 'c',
    length: 3
};

Array.prototype.push.call(obj, 'd');
console.log(obj); // {0: 'a', 1: 'b', 2: 'c', 3: 'd', length: 4}

var arr = Array.prototype.slice.call(obj)
console.log(arr) // ['a', 'b', 'c', 'd']

위의 pushslice경우 배열 메서드에 해당하는데 원래 객체에는 사용할수없는 메서드이다. 하지만 키가 0또는 양의 정수인 프로퍼티가 존재하고 length 프로퍼티 값이 0 또는 양의 정수인 객체인 배열의 구조와 유사한 객체의 경우(유사배열객체) call 또는 apply메서드를 이용해 배열 메서드를 차용할수있다.

배열메서드인 push객체 obj에 적용해 프로퍼티 3에 'd'를 추가하였고 slice 메서드를 적용해 객체를 배열로 전환하였다.

slice경우 매개변수로 아무것도 넘기지 않으면 원본배열의 얕은 복사를 반환하는데 call메서드를 이용하여 원본인 유사배열객체의 얕은 복사본배열로 반환한다.

function list() {
  return Array.prototype.slice.call(arguments);
}

let list1 = list(1, 2, 3); // [1, 2, 3]

함수 내부에서 접근할 수 있는 arguments 객체도 유사배열객체 이므로 위 코드와 같이 배열로 전환하는데 활용할 수 있다. 

또한 querySelectorAll, getElementByClassName 등의 Node 선택자로 선택한 결과인 NodeList도 가능하다.

arguments, NodeList에 배열 메서드 적용

function a() {
        var argv = Array.prototype.slice.call(arguments);
        argv.forEach(function (arg) {
          console.log(arg);
        });
      }
      a(1, 2, 3);

      document.body.innerHTML = "<div>a</div><div>b</div><div>c</div>"; // 브라우저에서 실행하기
      var nodeList = document.querySelectorAll("div");
      console.log(nodeList);
      var nodeArr = Array.prototype.slice.call(nodeList);
      console.log(nodeArr);
      nodeArr.forEach(function (node) {
        console.log(node);
      });

 

문자열에도 call/apply 메서드를 이용하여 배열메서드를 적용할 수 있는데, 문자열의 경우 length 프로퍼티가 읽기 전용이라 원본 문자열을 변경할 수 있는 push, pop, shift, unshift, splice 등은 에러를 던지고, concat처럼 대상이 반드시 배열이어야하는 경우에러는 나지 않지만 제대로된 결과를 얻을 수 없다.

문자열에 배열 메서드 적용 예시

var str = 'abc def';
Array.prototype.push.call(str, ', pushed string');
// Error: Cannot assign to read only property 'length' of object [object String]

Array.prototype.concat.call(str, 'string'); // [String {"abc def"}, "string"]

 

 

ES6의 Array.from 메서드

ES6부터는 유사배열객체 또는 순회 가능한 모든 종류의 데이터 타입을 배열로 전환하는 Array.from 메서드가 도입되었다.

var obj = {
	0: 'a',
    1: 'b',
    2: 'c',
    length: 3
};
var arr = Array.from(obj);
console.log(arr); // ['a', 'b', 'c']

변화

Array.prototype.slice.call() => Array.from() , 코드길이 감소

 

생성자 내부에서 다른 생성자 호출

만약 생성자 내부다른 생성자와 공통된 내용이 있을 경우 call 또는 apply를 이용하여 다른 생성자를 호출하여 반복을 줄일 수 있다.

function Person(name, gender){
    this.name = name;
    this.gender = gender;
}
function Student(name, gender, school){
    Person.call(this, name, gender);
    this.school = school;
}
function Employee(name, gender, company){
    Person.apply(this, [name, gender]);
    this.company = company;
}
var by = new Student('보영', 'female', '단국대');
var jn = new Employee('재난', 'male', '구글');
console.log(by, jn) 
// Student { name: '보영', gender: 'female', school: '단국대' } 
// Employee { name: '재난', gender: 'male', company: '구글' }

 

여러 인수를 받는 메서드에 apply 적용

var numbers = [10, 20, 3, 16, 45];
var max = Math.max.apply(null, numbers);
var min = Math.min.apply(null, numbers);
console.log(max, min) // 45 3

Math.max / Math.min Syntax

Math.max(value0, value1, /* ... ,*/ valueN)

 

여기서 null 을 쓴 이유는, Math 가 내장객체이기 때문에 this 기반을 제공할 필요가 없어서이다

(Math.max 는 별도의 선언 없이 사용할 수 있음)

보통 null 을 주게되면 전역객체 window의 this를 참조

apply 사용 방법을 설명한 위 코드에서 첫 번째 인자는 null이다. 이 자리에 다른 객체 값을 넘겨주면, 함수 내에서는 인자로 넘겨준 객체를 this로 인식한다.

ES6 Spread Operator 이용

var numbers = [10, 20, 3, 16, 45];
var max = Math.max(...numbers);
var min = Math.min(...numbers);
console.log(max, min) // 45 3

이와 같이 spread operator를 사용하면 더욱 간단하게 쓸수있고 this바인딩에 대해서도 예측하기 어렵거나 해석하기 방해하는 단점없지만 ES5이하의 환경에서는 다른 대안이 없기때문에 실무에서 매우 광범위하게 활용 된다고 한다.

 

 

 

Array.prototype.push() - JavaScript | MDN

The push() method adds one or more elements to the end of an array and returns the new length of the array.

developer.mozilla.org