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
그러나 .bind
JavaScript의 최신 (> = iE9) 기능 ( MDN 의 호환성 해결 방법 포함 )
노트
-
그것은 복제하지 않는 함수 객체 추가 연결 속성을 , 포함 프로토 타입 속성입니다. @jchook에 대한 크레딧
-
이 변수 의 새로운 함수 는 새로운 함수 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
- 바인딩 된 함수 객체 인 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);