[javascript] 괄호없이 함수 호출

오늘은 괄호없이 함수를 호출 할 수 있다고 들었습니다. 내가 생각할 수있는 유일한 방법은 apply또는 과 같은 기능을 사용하는 것 call입니다.

f.apply(this);
f.call(this);

그러나 이들에 괄호를 필요로 apply하고 call원점에서 우리를 떠나. 또한 함수를 setTimeout다음 과 같은 일종의 이벤트 핸들러에 전달하는 아이디어를 고려했습니다 .

setTimeout(f, 500);

그러나 질문은 ” setTimeout괄호없이 어떻게 호출 합니까?”가됩니다.

이 수수께끼의 해결책은 무엇입니까? 괄호를 사용하지 않고 Javascript에서 함수를 어떻게 호출 할 수 있습니까?



답변

괄호없이 함수를 호출하는 방법에는 여러 가지가 있습니다.

이 함수를 정의했다고 가정 해 봅시다.

function greet() {
    console.log('hello');
}

그런 다음 greet괄호없이 호출하는 몇 가지 방법을 따르십시오 .

1. 생성자로서

함께 new사용하면 함수를 괄호없이 호출 할 수 있습니다 :

new greet; // parentheses are optional in this construct.

에서 온 MDN newoprator :

통사론

new constructor[([arguments])]

2. 그대로 toString또는 valueOf구현

toString그리고 valueOf특별한 방법은 다음과 같습니다 변환이 필요한 경우가 암시 적으로 호출되는 :

var obj = {
    toString: function() {
         return 'hello';
    }
}

'' + obj; // concatenation forces cast to string and call to toString.

이 패턴을 사용하여 greet괄호없이 호출 할 수 있습니다.

'' + { toString: greet };

또는과 valueOf:

+{ valueOf: greet };

valueOf그리고 toString실제로에서 호출 @@ toPrimitive (ES6부터) 방법, 그래서 당신은 또한 구현할 수 있습니다 방법을 :

+{ [Symbol.toPrimitive]: greet }
"" + { [Symbol.toPrimitive]: greet }

2.b valueOf함수 프로토 타입에서 재정의

프로토 타입 에서 valueOf메소드 를 대체하기 위해 이전 아이디어를 취할 수 있습니다 .Function

Function.prototype.valueOf = function() {
    this.call(this);
    // Optional improvement: avoid `NaN` issues when used in expressions.
    return 0;
};

일단 그렇게하면 다음과 같이 쓸 수 있습니다.

+greet;

그리고 줄 아래에 괄호가 있지만 실제 트리거 호출에는 괄호가 없습니다. “실제로 호출하지 않고 JavaScript에서 메소드 호출” 블로그에서 이에 대해 자세히 알아보십시오.

3. 발전기로

iterator 를 반환하는 생성기 함수 (로 *)를 정의 할 수 있습니다. 스프레드 구문 이나 구문을 사용하여 호출 할 수 있습니다 .for...of

먼저 원래 greet함수 의 생성기 변형이 필요 합니다.

function* greet_gen() {
    console.log('hello');
}

그리고 @@ iterator 메소드를 정의하여 괄호없이 호출합니다 .

[...{ [Symbol.iterator]: greet_gen }];

일반적으로 생성기는 yield어딘가에 키워드를 갖지만 함수를 호출 할 필요는 없습니다.

마지막 문장은 함수를 호출하지만, 그것은 파괴로 도 가능합니다 :

[,] = { [Symbol.iterator]: greet_gen };

또는 for ... of구문이지만 자체 괄호가 있습니다.

for ({} of { [Symbol.iterator]: greet_gen });

원래 기능을 사용하여 위의 작업을 수행 할 수도greet 있지만 실행 greet (FF 및 Chrome에서 테스트 된) 프로세스에서 예외가 발생합니다 . try...catch블록으로 예외를 관리 할 수 있습니다.

4. 게터로서

@ jehna1은 이것에 대한 완전한 답을 가지고 있으므로 그에게 신용을주십시오. 다음은 사용되지 않는 메소드를 피하면서 전역 범위에서 괄호 없이 함수를 호출하는 방법입니다. 대신 사용 합니다.__defineGetter__Object.defineProperty

이를 위해 원래 greet함수 의 변형을 작성해야합니다 .

Object.defineProperty(window, 'greet_get', { get: greet });

그리고:

greet_get;

교체 window글로벌 객체가 무엇이든 함께.

다음 greet과 같이 전역 객체에 흔적을 남기지 않고 원래 함수를 호출 할 수 있습니다 .

Object.defineProperty({}, 'greet', { get: greet }).greet;

그러나 우리는 여기에 괄호가 있다고 주장 할 수 있습니다 (실제 호출에 관여하지는 않지만).

5. 태그 기능

ES6부터 다음 구문 으로 템플릿 리터럴 을 전달하는 함수를 호출 할 수 있습니다 .

greet``;

“태그 된 템플릿 리터럴”을 참조하십시오 .

6. 프록시 처리기

ES6부터 프록시를 정의 할 수 있습니다 .

var proxy = new Proxy({}, { get: greet } );

그런 다음 속성 값을 읽으면 다음이 호출됩니다 greet.

proxy._; // even if property not defined, it still triggers greet

이것에는 많은 변형이 있습니다. 하나 더 예 :

var proxy = new Proxy({}, { has: greet } );

1 in proxy; // triggers greet

7. 인스턴스 검사기

instanceof연산자는 실행 @@hasInstance정의 할 때 두 번째 오퍼랜드에 대한 방법 :

1 instanceof { [Symbol.hasInstance]: greet } // triggers greet


답변

가장 쉬운 방법은 new연산자를 사용하는 것 입니다.

function f() {
  alert('hello');
}

new f;

그것은 정통적이고 부자연 스럽지만 작동하며 완벽하게 합법적입니다.

new매개 변수를 사용하지 않을 경우 운영자는 괄호를 필요로하지 않습니다.


답변

게터와 세터를 사용할 수 있습니다.

var h = {
  get ello () {
    alert("World");
  }
}

다음을 사용하여이 스크립트를 실행하십시오.

h.ello  // Fires up alert "world"

편집하다:

우리는 심지어 논쟁을 할 수 있습니다!

var h = {
  set ello (what) {
    alert("Hello " + what);
  }
}

h.ello = "world" // Fires up alert "Hello world"

편집 2 :

괄호없이 실행할 수있는 전역 함수를 정의 할 수도 있습니다.

window.__defineGetter__("hello", function() { alert("world"); });
hello;  // Fires up alert "world"

그리고 논쟁과 함께 :

window.__defineSetter__("hello", function(what) { alert("Hello " + what); });
hello = "world";  // Fires up alert "Hello world"

부인 성명:

@MonkeyZeus가 말했듯이 : 의도가 아무리 좋더라도 프로덕션 에서이 코드를 절대 사용하지 마십시오.


답변

특정 상황에 대한 는 다음과 같습니다 .

window.onload = funcRef;

비록 그 진술이 실제로 발동되는 것은 아니지만 미래의 발발로 이어질 것입니다 입니다.

그러나, 나는 회색 지역이 다음과 같은 수수께끼에 대해 괜찮을 것이라고 생각합니다.


답변

측면 사고 방식을 받아들이면 브라우저에는 실제 괄호 문자없이 함수 호출을 포함하여 임의의 JavaScript를 실행하기 위해 남용 할 수있는 몇 가지 API가 있습니다.

1. location 그리고javascript: 프로토콜 :

그러한 기술 중 하나는 javascript:location 할당시 프로토콜 입니다.

작업 예 :

location='javascript:alert\x281\x29'

코드가 일단 평가되면 기술적 \x28 으로 \x29는 여전히 괄호 이지만 실제 () 문자는 나타나지 않습니다. 괄호는 할당시 평가되는 JavaScript 문자열로 이스케이프됩니다.


2. onerroreval:

마찬가지로 브라우저에 따라 전역 onerror을로 설정하고 eval유효한 JavaScript로 묶을 무언가를 던져서 남용 할 수 있습니다 . 브라우저는이 동작에 일관성이 없기 때문에 까다 롭지 만 Chrome의 예입니다.

Chrome의 실제 예 (Firefox가 아닌 다른 테스트되지 않은) :

window.onerror=eval;Uncaught=0;throw';alert\x281\x29';

이것은 거의 유효한 JavaScript 인에 대한 첫 번째 인수로 throw'test'전달 되기 때문에 Chrome에서 작동 합니다. 우리가 대신 하면 통과합니다'Uncaught test'onerrorthrow';test''Uncaught ;test' 합니다. 이제 유효한 JavaScript가 있습니다! 그냥 정의 Uncaught하고 테스트를 페이로드로 바꾸십시오.


결론적으로:

이러한 코드는 정말 끔찍한 , 그리고 사용해서는 안됩니다 이야기의 도덕적 않도록 방지 XSS에 괄호를 필터링에 의존하지 않는 것입니다,하지만 때로는 XSS 공격에 사용됩니다. 이러한 코드를 방지하기 위해 CSP 를 사용하는 것도 좋은 생각입니다.


답변

ES6에는 Tagged Template Literals가 있습니다.

예를 들면 다음과 같습니다.

function foo(val) {
    console.log(val);
}

foo`Tagged Template Literals`;


답변