1. 程式人生 > >解決掉你心中 js function與Function的關係的疑問

解決掉你心中 js function與Function的關係的疑問

前言

在網上有很多關於js function 與 Function直接關係的文章。
但是我感覺過於抽象化了,那麼如何是具體化的解釋?
正文部分為個人理解部分,如有不對望指出。

正文

<script>
    if((function(){}).constructor === Function)
    {
        console.log(true);
    }
</script>

我寫了一段這個程式碼,答案為true。
這就是function 與 Function的關係。
看下Function的定義:

Function 建構函式建立一個新的 Function 物件。直接呼叫此建構函式可用動態建立函式,但會遭遇來自 eval 的安全問題和相對較小的效能問題。然而,與 eval 不同的是,Function 建構函式只在全域性作用域中執行。

然後還給出了這樣一個結論:

每個 JavaScript 函式實際上都是一個 Function 物件。

重新理解一下,Function的定義。

第一句話很好理解,就是可以建立一個Function物件。第二句好也好理解,就是會有安全漏洞和效能問題,詳細部分可看eval的安全問題,至於為什麼低效,官方解釋是:因為使用後者建立的函式是跟其他程式碼一起解析的。
第三句話,什麼叫"Function 建構函式只在全域性作用域中執行"?

var func = new Function("alert(x+y);");
var test = function () {
    var x=1,y=2;
    func();
    eval("x+y");
}

就是func()執行會報錯。而eval("x+y")是成功的。
因為func()是在全域性作用域中執行。
如果是這樣:

var x=1,y=2;
var func = new Function("alert(x+y);");
var test = function () {
    var x=1,y=2;
    func();
    eval("x+y");
}

test();

那麼會彈出3;
當然這裡說eval是在區域性變數中執行也比較絕對,因為eval的引用變數是在全域性中執行的。

var global=eval;
global("x+y");

這樣就在全域性中執行,好吧,這不是該篇的重點。
那麼這種Function 物件和申明函式有什麼區別呢?前面已經說明了,Function物件是在全域性中執行,而宣告函式是在區域性中執行。
我找了一個例子:

var x = 10;

function createFunction1() {
    var x = 20;
    return new Function('return x;'); // 這裡的 x 指向最上面全域性作用域內的 x
}

function createFunction2() {
    var x = 20;
    function f() {
        return x; // 這裡的 x 指向上方本地作用域內的 x
    }
    return f;
}

var f1 = createFunction1();
console.log(f1());          // 10
var f2 = createFunction2();
console.log(f2());          // 20

看到這裡,以為結束了?下面是本文的核心思想部分。
我們如何才能夠,用function的方式建立出Function物件的效果?或者說我們如何模擬new Function() 到底發生了什麼。

var x=1,y=2;
var func = new Function("alert(x+y);");
var func1= function ()
{
    alert(x+y);
}
console.log(func);
console.log(func1);

打印出來func,是一個匿名的函式。
匿名函式自帶是閉包效果,在這裡我似乎找到了答案,現在我們就來模擬出new Function();

function main() {
    var x = 5;
    var y = 6;
     FunctionObj = function () {
        alert(x + y);
    };

    function func() {
        var x = 1;
        var y = 2;
        var Newfunc = FunctionObj;
        Newfunc();
    }
    func();
}
main();
FunctionObj();

彈出的結果是11,另一個彈出的還是11;
在這裡我假設啟動的是main主函式,x與y是全域性變數。
然後在func中的:

var func = FunctionObj;
func();

假設為new Function(),也就是說去假設構建一個Function 物件。
這樣實現的其實就是在全域性變數中建立了FunctionObj變數,然後賦值給了func,然後再執行func的時候自然就在main變數中了,因為匿名函式本身就閉包。
對應為:

var x=5;
var y=6;
function func()
{
    var x=1;
    var y=2;
    var NewFunc= new Function("alert(x+y)");
    NewFunc();
}
func();

繼續變化一下,模擬Function 做了什麼:

var x=5;
var y=6;
function func()
{
    var x=1;
    var y=2;
    var NewFunc= Functionsimulation();
    NewFunc();
}
function Functionsimulation()
{
     var global=eval;
     return eval("(function(){return function(){alert(x+y)}})()");
}
func();

彈出的結果依然是:11;

總結

回過頭來,再看一下這段話:

每個 JavaScript 函式實際上都是一個 Function 物件。

new Function 只是將匿名包,繫結到了一個全域性變量了,這樣它的this永遠固定,執行時,永遠是在全域性中執行。
至於宣告函式,因為是動態的執行,所以我們訪問的以區域性優先,也可以理解為繫結的為區域性變數。
以上是個人理解,如有不對望指出