[javascript] JavaScript : 함수 복제

JavaScript에서 (속성 유무에 관계없이) 함수를 복제하는 가장 빠른 방법은 무엇입니까?

떠오르는 두 가지 옵션은 eval(func.toString())function() { return func.apply(..) }입니다. 하지만 eval의 성능이 걱정되고, 랩핑은 스택을 악화시키고 많이 적용하거나 이미 랩핑에 적용하면 성능이 저하 될 수 있습니다.

new Function(args, body) 멋져 보이지만 JS에서 JS 파서없이 어떻게 기존 함수를 args와 body로 정확하게 분할 할 수 있습니까?

미리 감사드립니다.

업데이트 :
내가 의미하는 것은 할 수 있다는 것입니다

var funcB = funcA.clone(); // where clone() is my extension
funcB.newField = {...};    // without affecting funcA



답변

이 시도:

var x = function() {
    return 1;
};

var t = function(a,b,c) {
    return a+b+c;
};


Function.prototype.clone = function() {
    var that = this;
    var temp = function temporary() { return that.apply(this, arguments); };
    for(var key in this) {
        if (this.hasOwnProperty(key)) {
            temp[key] = this[key];
        }
    }
    return temp;
};

alert(x === x.clone());
alert(x() === x.clone()());

alert(t === t.clone());
alert(t(1,1,1) === t.clone()(1,1,1));
alert(t.clone()(1,1,1));


답변

다음은 업데이트 된 답변입니다.

var newFunc = oldFunc.bind({}); //clones the function with '{}' acting as it's new 'this' parameter

그러나 .bindJavaScript의 최신 (> = iE9) 기능 ( MDN호환성 해결 방법 포함 )

노트

  1. 그것은 복제하지 않는 함수 객체 추가 연결 속성을 , 포함 프로토 타입 속성입니다. @jchook에 대한 크레딧

  2. 변수 의 새로운 함수 는 새로운 함수 apply () 호출에서도 bind ()에 주어진 인수와 함께 붙어 있습니다. @Kevin에 대한 크레딧

function oldFunc() {
  console.log(this.msg);
}
var newFunc = oldFunc.bind({ msg: "You shall not pass!" }); // this object is binded
newFunc.apply({ msg: "hello world" }); //logs "You shall not pass!" instead
  1. 바인딩 된 함수 객체 인 instanceof는 newFunc / oldFunc를 동일하게 취급합니다. @Christopher에 대한 크레딧
(new newFunc()) instanceof oldFunc; //gives true
(new oldFunc()) instanceof newFunc; //gives true as well
newFunc == oldFunc; //gives false however


답변

Jared의 답변에 대한 약간 더 나은 버전이 있습니다. 이것은 더 많이 복제할수록 깊이 중첩 된 함수로 끝나지 않습니다. 항상 원본이라고 부릅니다.

Function.prototype.clone = function() {
    var cloneObj = this;
    if(this.__isClone) {
      cloneObj = this.__clonedFrom;
    }

    var temp = function() { return cloneObj.apply(this, arguments); };
    for(var key in this) {
        temp[key] = this[key];
    }

    temp.__isClone = true;
    temp.__clonedFrom = cloneObj;

    return temp;
};

또한 pico.creator가 제공 한 업데이트 된 답변에 대한 응답으로 bind()Javascript 1.8.5에 추가 된 함수가 Jared의 답변과 동일한 문제를 가지고 있다는 점에 주목할 가치가 있습니다. 중첩을 계속하여 사용할 때마다 기능이 느려지고 느려집니다.


답변

호기심이 많지만 여전히 위 질문의 성능 주제에 대한 답을 찾을 수 없었기 때문에, 제시된 (및 점수가 매겨진) 모든 솔루션의 성능과 신뢰성을 테스트하기 위해 nodejs에 대한 이 요점 을 작성했습니다 .

클론 기능 생성과 클론 실행의 벽 시간을 비교했습니다. 주장 오류와 함께 결과는 요점의 주석에 포함됩니다.

플러스 내 2 센트 (저자의 제안에 따라) :

clone0 cent (빠르지 만 더 못 생김) :

Function.prototype.clone = function() {
  var newfun;
  eval('newfun=' + this.toString());
  for (var key in this)
    newfun[key] = this[key];
  return newfun;
};

clone4 cent (느리지 만 그들과 조상에게만 알려진 목적으로 eval ()을 싫어하는 사람들을 위해) :

Function.prototype.clone = function() {
  var newfun = new Function('return ' + this.toString())();
  for (var key in this)
    newfun[key] = this[key];
  return newfun;
};

성능에 관해서는 eval / new Function이 래퍼 솔루션보다 느리면 (실제로 함수 본문 크기에 따라 다름) 불필요한 퍼지없이 베어 함수 클론 (속성이있는 진정한 얕은 클론을 의미하지만 공유되지 않은 상태)을 제공합니다. 숨겨진 속성, 래퍼 함수 및 스택 문제가 있습니다.

또한 고려해야 할 중요한 요소가 항상 하나 있습니다. 코드가 적을수록 실수 할 가능성이 적습니다.

eval / new 함수 사용의 단점은 복제본과 원래 함수가 다른 범위에서 작동한다는 것입니다. 범위 변수를 사용하는 함수에서는 잘 작동하지 않습니다. 바인딩 유사 래핑을 사용하는 솔루션은 범위에 독립적입니다.


답변

이 메서드를 작동시키는 것은 매우 흥미 로웠으므로 Function 호출을 사용하여 함수의 복제본을 만듭니다.

MDN Function Reference에 설명 된 클로저에 대한 몇 가지 제한 사항

function cloneFunc( func ) {
  var reFn = /^function\s*([^\s(]*)\s*\(([^)]*)\)[^{]*\{([^]*)\}$/gi
    , s = func.toString().replace(/^\s|\s$/g, '')
    , m = reFn.exec(s);
  if (!m || !m.length) return;
  var conf = {
      name : m[1] || '',
      args : m[2].replace(/\s+/g,'').split(','),
      body : m[3] || ''
  }
  var clone = Function.prototype.constructor.apply(this, [].concat(conf.args, conf.body));
  return clone;
}

즐겨.


답변

짧고 간단합니다.

Function.prototype.clone = function() {
  return new Function('return ' + this.toString())();
};


답변

const oldFunction = params => {
  // do something
};

const clonedFunction = (...args) => oldFunction(...args);