《JavaScript面向對象精要》之二:函數

函數也是對象,使對象不一樣於其它對象的決定性特色是函數存在一個被稱爲 [[Call]] 的內部屬性。正則表達式

相似於[[Call]] 這種內部屬性沒法經過代碼訪問,他只是定義了代碼執行時的行爲。數組

ECMAScript爲JavaScript的對象定義了多種內部屬性,這些內部屬性都用雙重中括號來標註瀏覽器

[[Call]] 屬性是函數獨有的,代表該對象能夠被執行。因爲僅函數擁有該屬性,ECMAScript 定義 typeof 操做符對任何具備 [[Call]] 屬性的對象返回 "function"app

過去因某些瀏覽器曾在正則表達式中包含 [[Call]] 屬性,致使正則表達式被錯誤鑑別爲函數。函數

2.1 聲明仍是表達式

函數有兩種字面形式:函數聲明跟函數表達式。ui

二者的一個重要區別是:函數聲明會被提高至上下文(要麼是該函數被聲明時所在的函數範圍,要麼是全局範圍)的頂部。this

2.2 函數就是值

函數是JavaScript的一大重點。spa

基本上只要是可使用其餘引用值的地方,你就可使用函數。也就是說,你能夠像使用對象同樣使用函數(由於函數原本就是對象)。指針

2.3 參數

JavaScript函數另外一個獨特之處在於你能夠給函數傳遞任意數量的參數卻不形成錯誤。調試

這是由於函數參數保存在類數組對象 arguments中。

如同一個普通JavaScript數組,arguments能夠自由增加來包含任意個數的值,這些值可經過數字索引來引用。

雖然arguments相似數組,可是Array.isArray(arguments) 返回 false

最後須要注意兩點:

  • arguments對象自動存在於函數中,也就是說,函數的命名參數不過是爲了方便,並不真的限制了該函數可接受參數的個數。
  • 但JavaScript也沒忽視那些命名參數,函數的 length 屬性代表其指望的參數個數。

2.4 重載

大多數面嚮對象語言支持函數重載,它能讓一個函數具備多個簽名。函數簽名由函數的名字、參數的個數及其類型組成。

而 JavaScript 能夠接收任意數量的參數且參數類型徹底沒有限制。這說明 JavaScript 函數根本就沒有簽名,所以也不存在重載。

function sayMessage(message){
  console.log(message);
}
function sayMessage(){
  console.log("Default Message");
}

sayMessage("Hello!"); // 輸出"Default Message";
複製代碼

在 JavaScript 裏,當你試圖定義多個同名的函數時,只有最後的定義有效,以前的函數聲明被徹底刪除(函數也是對象,變量只是存指針)。

var sayMessage = new Function("message", "console.log(message)");
var sayMessage = new Function("console.log(\"Default Message\");");

sayMessage("Hello!"); 
複製代碼

固然,你能夠根據傳入參數的數量來模仿重載。

2.5 對象方法

對象的值是函數,則該屬性被稱爲方法。

2.5.1 this 對象

JavaScript 全部的函數做用域內都有一個 this 對象表明調用該函數的對象。

在全局做用域中,this 表明全局對象(瀏覽器裏的 window)。

當一個函數做爲對象的方法調用時,默認 this 的值等於該對象。

this在函數調用時才被設置。

function sayNameForAll(){
  console.log(this.name);
}

var person1 = {
  name: "Nicholas",
  sayName: sayNameForAll
}

var name = "Jack";
    
person1.sayName(); // 輸出 "Nicholas"
sayNameforAll(); // 輸出 "Jack"
複製代碼

2.5.2 改變 this

有3種函數方法運行你改變 this 值。

  1. fun.call(thisArg[, arg1[, arg2[, ...]]]);
  2. fun.apply(thisArg, [argsArray]);
  3. fun.bind(thisArg[, arg1[, arg2[, ...]]])

使用 callapply 方法,就不須要將函數加入每一個對象——你顯示地指定了 this 的值而不是讓 JavaScript 引擎自動指定。

callapply 的不一樣地方是,call 須要把全部參數一個個列出來,而 apply 的參數須要一個數組或者相似數組的對象(如 arguments 對象)。

bind 是 ECMAScript 5 新增的,它會建立一個新函數返回。其參數與 call 相似,並且其全部參數表明須要被永久設置在新函數中的命名參數(綁定了的參數(沒綁定的參數依然能夠傳入),就算調用時再傳入其它參數,也不會影響這些綁定的參數)。

function sayNameForAll(label){
  console.log(label + ":" + this.name);
}
var person = {
  name: "Nicholas"
}

var sayNameForPerson = sayNameForAll.bind(person);
sayNameForPerson("Person"); // 輸出"Person:Nicholas"

var sayName = sayNameForAll.bind(person, "Jc");

sayName("change"); // 輸出"Jc:Nicholas" 由於綁定的形參,會忽略調用時再傳入參數
複製代碼

2.6 總結

  • 函數也是對象,因此它能夠被訪問、複製和覆蓋。
  • 函數與其餘對象最大的區別在於它們有一個特殊的內部屬性 [[Call]],包含了該函數的執行指令。
  • 函數聲明會被提高至上下文的頂部。
  • 函數是對象,因此存在一個 Function 構造函數。但這會使你的代碼難以理解和調試,除非函數的真實形式要直到運行時才能肯定的時候纔會利用它。
相關文章
相關標籤/搜索