javascript - Tillåter restparametrar optimering?


Avsnittet Hantering av arguments i Bluebirds artikel om optimeringsdödare anger att:

Objektet arguments får inte skickas eller läcka någonstans.

Med andra ord, gör inte följande:

function leaky(){
    return arguments;
}

Men gör det här:

function not_leaky(){
    var i = arguments.length,
        args = [];
    while(i--) args[i] = arguments[i];
    return args;
}

Med införandet av Rest-parametrar, kommer det att fortsätta att orsaka optimeringsproblem att skicka med restparameter-arrayen? Från vad jag förstår är problemet att vi inte kan låta det faktiska arguments-objektet komma loss. Begränsade, definierade kopior av arguments är okej, men inte själva objektet. Om det är fallet, behandlas då restargumentet som en okej och optimerbar kopia av arguments när det används på följande sätt?

function maybe_optimizable(...args){
    return args;
}

Specifikt försöker jag skriva en debounce-funktion baserad på David Walshs debounce-funktion (som är baserad på Underscore.js) och jag tror att det finns ett problem med att tilldela arguments till en variabel inom den översta nivån av debounce-funktionen. För att åtgärda detta skrev jag följande:

function debounce(func, wait, immediate) {
    let timeout;
    return function (...args) {
        let context = this,
            now = immediate && !timeout;
        clearTimeout(timeout);
        timeout = setTimeout(() => {
            timeout = null;
            if (!immediate) {
                func.apply(context, args);
            }
        }, wait);
        if (now) {
            func.apply(context, args);
        }
    };
}

Svar #1

En av utmaningarna med arguments-objektet är att det behöver vara en levande samling av parametervärdena, även när de tilldelas på nytt. Se hur även en (parameter)variabel med ett primitivt värde ändras utan någon uttrycklig tilldelning till det, och det händer utanför funktionen:

function modify(arr) {
    arr[0] = 3;
}

(function (a) {
    console.log('arguments is an ' + arguments.constructor.name);
    console.log('a is ', a);
    modify(arguments);
    console.log('after calling modify, a is ', a);
})(0); 

Se den här bloggen för mer om sådant beteende.

Man kan föreställa sig att kodoptimering blir en mardröm när ett sådant objekt rör sig runt, utan att någonsin förlora sådana magiska förmågor.

Detta händer förstås inte med en vanlig array, och arrayen du får med spridningssyntaxen är faktiskt en vanlig array:

(function (...args) {
    console.log('args is an ' + args.constructor.name);
})(0); 

Du kan vila dig (ingen ordlek avsedd) säker på att de nämnda kodoptimeringsproblemen med arguments inte gäller för ...args.

Strikt läge fixar allt

Som Bergi nämnde. Den levande semantiken för arguments finns inte längre när funktionen, vars arguments-variabel används, är skriven i strikt JavaScript-läge:

function modify(arr) {
    arr[0] = 3;
}

(function (a) {
    "use strict"; // <-----
    console.log('In strict mode');
    console.log('arguments is still an ' + arguments.constructor.name);
    console.log('a is ', a);
    modify(arguments);
    console.log('after calling modify, a is still ', a);
})(0); 

Som anges i MDN:s artikel om strikt läge:

strikt läge-kod skapar inte alias för egenskaper hos arguments-objekt som skapats inom den. I normal kod inom en funktion vars första argument är arg, sätter man arg så sätts även arguments[0], och vice versa (om inte inga argument tillhandahölls eller arguments[0] har raderats). arguments-objekt för funktioner i strikt läge lagrar de ursprungliga argumenten när funktionen anropades. arguments[i] följer inte värdet på motsvarande namngivna argument, och ett namngivet argument följer inte värdet i motsvarande arguments[i].