2013-10-29 13 views

Odpowiedz

3

Nie, nie może wykryć, czy funkcja jest wywoływana z call/apply lub normalnie.

Nie są magicznymi istotami, tylko ustawiają argumenty i wartości. Jest subtle difference, jeśli chodzi o niezdefiniowane/niezadeklarowane wartości, ale to wszystko.

Wszystko .apply i .call zrobić w ES5 jest:

Powrót wynikiem wywołanie [[CALL]] metodę wewnętrzną func, zapewniając thisArg jako tej wartości i arglist jako lista argumentów.

Ustawia wewnętrzny atrybut caller poprawnie, więc coś naiwnego jak this nie zadziałałoby.

2

Chyba przedefiniować Function.prototype.call i Function.prototype.apply (i zdać kolejny argument do funkcji), nie sposób robić to - rzeczywistych mechanizmów wewnętrznych (funkcja wewnętrzny [[Call]]) nie zapewniają żadnej metody sygnalizacji, który nazywa się używając ().

Porównaj specyfikację dla general function call i dla Function.prototype.apply - każda wywołuje wewnętrzny kod funkcji w dokładnie taki sam sposób i nie istnieje żaden zestaw właściwości zewnętrznych, który jest w stanie podać, czy jest wywoływany z użyciem tego, czy nie.

Zobacz specyfikację dla funkcji wewnętrznej [[Call]]:

13.2.1 [[Call]]

Gdy wewnętrzne metody obiektu funkcja F jest wywoływana z tej wartości oraz listę argumentów [[Call]] następujące kroki są podejmowane:

  1. Niech funcCtx będzie wynikiem ustanowienia nowego kontekstu wykonania dla kodu funkcji przy użyciu wartości F [[Formal Parametry]] właściwość wewnętrzna, przekazane argumenty Lista argumentów, i ta wartość jak opisano w 10.4.3.
  2. Wynik powinien być wynikiem oceny FunctionBody, który jest wartością właściwości wewnętrznej F [[Code]]. Jeśli F nie ma właściwości wewnętrznej [[Code]] lub jej wartością jest pusta funkcja FunctionBody, wówczas wynik jest (normalny, niezdefiniowany, pusty).
  3. Wyjdź z kontekstu wykonywania funcCtx, przywracając poprzedni kontekst wykonania.
  4. Jeśli result.type to rzut, to rzuć result.value.
  5. Jeśli result.type zostanie zwrócony, a następnie return result.value.
  6. W przeciwnym razie result.type musi być normalne. Return undefined.

Nie ma przepisu, aby zmienić bieg zależności od tego, czy jest ona wywoływana przy użyciu call/apply czy nie - jedyną rzeczą, która zmienia się co robi są argumenty dla samej funkcji, a co this ma być w funkcji.

+0

'Chyba, że ​​przejąłeś funkcję Function.prototype.call' - Bardzo bym chciał, aby tego rodzaju rzeczy zostały zaimplementowane - uważam, że będzie trudniejsze, niż można by oczekiwać, ponieważ' .call' jest również używany wewnętrznie. –

+0

@BenjaminGruenbaum: Chodzi mi o to, że ponownie zdefiniuję funkcję jako opakowanie, powinienem to wyjaśnić. –

-1

Nie mogę wymyślić powodu, dla którego powinieneś sprawdzić, ale możesz sprawdzić, porównując this === window, ponieważ jest to domyślny zakres (zakładając JavaScript w przeglądarce), ale można to udać, po prostu wywołując foo jak foo.call(window), co w zasadzie wywołuje normę foo().

To prawdopodobnie nie zadziała przy użyciu window dla funkcji, które są właściwościami innych obiektów lub prototypów.

1

Mam nadzieję, że to rozwiąże problem:

function foo(){ 
    console.log('foo', this); 

    if (typeof this.length === "number") { 
     //function has been apply 
    } else { 
     //function has been call 
    } 
} 

foo(); 
foo.call({ bar: 1 }); 
foo.apply([{ bar: 1 }]); 
+0

Problem polega na tym, że przekazujesz tablicę dla 'apply' oraz obiekt dla' call'. Co się stanie, gdy przekażesz obiekt obu? –

1

Spróbuj czegoś takiego:

function foo(){ 

    console.log('foo', this); 
    console.log(arguments); 
} 
Function.prototype._apply = Function.prototype.apply; 
Function.prototype.apply = function(ths,args){ 
    args.unshift('apply'); 
    this._apply(ths,args); 
}; 

foo(); 
foo.call(this, { bar: 1 }); 
foo.apply(this, [{ bar: 1 }]); 
1

brudne, brudne Hack:

function foo() { 
    var isNatural = !/foo\.(call|apply)/.test("" + foo.caller) 
    console.log(isNatural ? "foo()" : "foo.{call,apply}()") 
} 

function caller1() { 
    foo() 
} 
function caller2() { 
    foo.call(null, { bar: 1 }) 
} 
function caller3() { 
    foo.apply(null, [{ bar: 1 }]) 
} 

caller1() 
caller2() 
caller3() 

to po prostu do myślenia. Nie używaj go podczas produkcji.

Powiązane problemy