Js基礎知識4-函式的三種建立、四種呼叫(及關於new function()的解釋)

笠航發表於2018-09-08

在js中,函式本身屬於物件的一種,因此可以定義、賦值,作為物件的屬性或者成為其他函式的引數。函式名只是函式這個物件類的引用。

函式定義

 1 // 函式的三種建立方法(定義方式)
 2 function one(){                                 // 函式宣告語句,不屬於任何物件,始終預設為全域性物件
 3 console.log("第一個函式")
 4 //預設有一個return this,返回函式中的內容
 5 }
 6 one();      //必須呼叫;可以在函式宣告前呼叫(預處理變異機制)                       
 7 
 8 var fn=function(){                               //函式定義表示式
 9 console.log("第二個函式")
10 }
11 fn(); //必須先宣告再呼叫                                 
12 
13 var fun=new Function(console.log("第三個函式"));    //Function建構函式 無需呼叫,會自調  
//實際一般應這樣寫:var newFun = new Function("x","return alert(x)");
//[注意]Function建構函式無法指定函式名稱,它建立的是一個匿名函式。

從技術上講,這是一個函式表示式。但不推薦使用,因為這種語法會導致解析兩次程式碼。第一次是解析常規javascript程式碼,第二次解析傳入建構函式中的字串,影響效能。

var sum = new Function(`num1`,`num2`,`return num1 + num2`);
//等價於
var sum = function(num1,num2){
    return num1+num2;
}

[注意]並不是所有的函式都可以成為建構函式 

1 var o = new Math.min();//Uncaught TypeError: Math.min is not a constructor

【重複宣告】變數的重複宣告是無用的,不會覆蓋之前同一作用域宣告的變數,但函式的重複宣告會覆蓋前面的宣告的同名函式或同名變數

//變數的重複宣告無用
var a = 1;
var a;
console.log(a);//1
1 //覆蓋同名變數
2 var a;
3 function a(){
4     console.log(1);
5 }
6 a();//1
1 //覆蓋同名函式
2 a();//2
3 function a(){
4     console.log(1);
5 }
6 function a(){
7     console.log(2);
8 }

【刪除】函式宣告語句建立的變數無法刪除,這一點和變數宣告一樣。

1 function foo(){
2     console.log(1);
3 }
4 delete foo;//false
5 console.log(foo());//1

———————————————————

函式呼叫

javascript一共有4種呼叫模式:函式呼叫模式、方法呼叫模式、構造器呼叫模式和間接呼叫模式

【1】函式呼叫模式

  當一個函式並非一個物件的屬性時,那麼它就是被當做一個函式來呼叫的。對於普通的函式呼叫來說,函式的返回值就是呼叫表示式的值。

1 function add(x,y){
2     return x+y;
3 }
4 var sum = add(3,4);
5 console.log(sum)//7

使用函式呼叫模式呼叫函式時,非嚴格模式下,this被繫結到全域性物件;在嚴格模式下,this是undefined

function add(x,y){
    console.log(this);//window
}    
add();//window
function add(x,y){
    `use strict`;    //嚴格模式
    console.log(this);//undefined
}    
add();//undefined

因此,’this’可以用來判斷當前是否是嚴格模式

var strict = (function(){return !this;}());

【重寫】因為函式呼叫模式的函式中的this繫結到全域性物件,所以會發生全域性屬性被重寫的現象

1 var a = 0;
2 function fn(){
3     this.a = 1;
4 }
5 fn();
6 console.log(this,this.a,a);//window 1 1

【2】方法呼叫模式

當一個函式被儲存為物件的一個屬性時,我們稱它為一個方法。當一個方法被呼叫時,this被繫結到該物件。如果呼叫表示式包含一個提取屬性的動作,那麼它就是被當做一個方法來呼叫。

var o = {
    m: function(){
        console.log(1);
    }
};
o.m();//1

方法可以使用this訪問自己所屬的物件,所以它能從物件中取值或對物件進行修改。this到物件的繫結發生在呼叫的時候。通過this可取得它們所屬物件的上下文的方法稱為公共方法。

var o = {
    a: 1,
    m: function(){
        return this;
    },
    n: function(){
        this.a = 2;
    }
};
console.log(o.m().a);//1
o.n();
console.log(o.m().a);//2

和變數不同,關鍵字this沒有作用域的限制,巢狀的函式不會從呼叫它的函式中繼承this。如果巢狀函式作為方法呼叫,其this的值指向呼叫它的物件。如果巢狀函式作為函式呼叫,其this值不是全域性物件(非嚴格模式下)就是undefined(嚴格模式下)

var o = {
    m: function(){
         function n(){
             return this;
         }
         return n();
    }
}
console.log(o.m());//window
var o = {
    m: function(){
         function n(){
             `use strict`;
             return this;
         }
         return n();
    }
}
console.log(o.m());//undefined

如果想訪問這個外部函式的this值,需要將this的值儲存在一個變數裡,這個變數和內部函式都同在一個作用域內。通常使用變數_this、that或self來儲存this

var o = {
    m: function(){
        var self = this;
        console.log(this === o);//true
         function n(){
             console.log(this === o);//false
             console.log(self === o);//true
             return self;
         }
         return n();
    }
}
console.log(o.m() === o);//true

【3】建構函式呼叫模式

  如果函式或者方法呼叫之前帶有關鍵字new,它就構成建構函式呼叫

function fn(){
    this.a = 1;
};
var obj = new fn();
console.log(obj.a);//1

如果建構函式呼叫在圓括號內包含一組實參列表,先計算這些實參表示式,然後傳入函式內

function fn(x){
    this.a = x;
};
var obj = new fn(2);
console.log(obj.a);//2

如果建構函式沒有形參,javascript建構函式呼叫的語法是允許省略實參列表和圓括號的。凡是沒有形參的建構函式呼叫都可以省略圓括號

var o = new Object();
//等價於
var o = new Object;

[注意]儘管建構函式看起來像一個方法呼叫,它依然會使用這個新物件作為呼叫上下文。也就是說,在表示式new o.m()中,呼叫上下文並不是o

var o = {
    m: function(){
        return this;
    }
}
var obj = new o.m();
console.log(obj,obj === o);//{} false
console.log(obj.constructor === o.m);//true

建構函式通常不使用return關鍵字,它們通常初始化新物件,當建構函式的函式體執行完畢時,它會顯式返回。在這種情況下,建構函式呼叫表示式的計算結果就是這個新物件的值

function fn(){
    this.a = 2;
}
var test = new fn();
console.log(test);//{a:2}

如果建構函式使用return語句但沒有指定返回值,或者返回一個原始值,那麼這時將忽略返回值,同時使用這個新物件作為呼叫結果

function fn(){
    this.a = 2;
    return;
}
var test = new fn();
console.log(test);//{a:2}

如果建構函式顯式地使用return語句返回一個物件,那麼呼叫表示式的值就是這個物件

var obj = {a:1};
function fn(){
    this.a = 2;
    return obj;
}
var test = new fn();
console.log(test);//{a:1}

【4】間接呼叫模式

  javascript中函式也是物件,函式物件也可以包含方法。call()和apply()方法可以用來間接地呼叫函式。

  這兩個方法都允許顯式指定呼叫所需的this值,也就是說,任何函式可以作為任何物件的方法來呼叫,哪怕這個函式不是那個物件的方法。兩個方法都可以指定呼叫的實參。call()方法使用它自有的實參列表作為函式的實參,apply()方法則要求以陣列的形式傳入引數。

var obj = {};
function sum(x,y){
    return x+y;
}
console.log(sum.call(obj,1,2));//3
console.log(sum.apply(obj,[1,2]));//3

(函式返回值、函式引數、函式屬性、函式方法)另開篇章

js的執行機制問題:(宣告提升) 
1、在js中js引擎會優先解析var變數和function定義!在預解析完成後從上到下逐步進行! 
2、解析var變數時,會把值儲存在“執行環境”中,而不會去賦值,值是儲存作用!例如: 
alert(a); var a = 2; 這時會輸出undifiend,意思是沒有被初始化沒有被賦值! 
這並不是沒有被定義,錯誤了的意思! 
3、在解析function時會把函式整體定義,這也就解釋了為什麼在function定義函式時為什麼可以先呼叫後宣告瞭!其實表面上看是先呼叫了,其實在內部機制中第一步實行的是把以function方式定義的函式先宣告瞭(預處理)

//*****************上篇出自:https://blog.csdn.net/luanpeng825485697/article/details/77010261

//*****************下篇出自:https://www.cnblogs.com/hss-blog/articles/9358251.html

//js中只有new Function沒有new function,或者我理解你說的new function是指例項化一個物件
//new Function的作用是從字串中建立一個匿名方法,如下:
var newFun = new Function("alert(1)");
nweFun();        //彈出1
 
//如果你說的new function是例項化一個物件,那麼程式碼如下:
function cls(){
    this.helloWord = function(){
        alert("hello Word!");
    }
}
var clsObj = new cls();
clsObj.helloWord();        //彈出hello Word!
//這裡的cls這個你應該要把他看成物件導向裡面的類,而不是js裡面的方法。
//當然本質上他就是一個方法,而且你也可以cls()這樣直接呼叫。
//更多js物件導向的東西我就不細說了,你可以自行百度。
 
//直接定義個function,然後呼叫,程式碼如下:
function fun(){
    alert("hello Word!");
}
fun();            //彈出hello Word!
//這裡的方法你應該看成物件導向裡面的靜態方法,而不是物件導向裡面的類。
//當然他也確實是一個類,你也可以new fun()來呼叫。
//但是new fun()得到的物件沒有任何方法。

//其實關於new function 應該是這樣的( 更準確的說應該是new function()
var clsObj = new function()
{
this.helloWord = function(){
alert("hello Word!");
}
}
clsObj.helloWord(); //彈出hello Word!
//是例項化一個物件(匿名函式),這樣寫的好處是可以防止沒有new呼叫函式

 

看到一個new Function用法

function callAnotherFunc(fnFunction, vArgument) {
  fnFunction(vArgument);
}
var doAdd = new Function("iNum", "alert(iNum + 10)");
callAnotherFunc(doAdd, 10);    //輸出 "20"

相關文章