天天看點

這些 function 的細節你都知道嗎?

大家好,我是 ​​Lvzl​​, 一個三年工作經驗的前端小菜雞,在​​掘金​​平台分享一些 平時學習的感悟 & 實際項目場景 的文章。

本文主要内容:詳細聊聊 ​

​JavaScript​

​ 函數。

函數概述

函數的聲明語句

​function​

​​ 指令聲明的代碼區塊就是一個函數。​

​function​

​ 指令後面是函數名,函數名後面是一對圓括号,裡面是傳入函數的參數,函數體放在大括号裡面。

以下面的例子來說:hello 是函數名、有一個參數、函數體是 ​

​console.log(a)​

function hello(a) {
  console.log(a)
}
// 調用:
hello('hello world')
複制代碼      

函數表達式

采用變量指派的寫法:将一個匿名函數指派給變量。這時,這個匿名函數又稱函數表達式。

const hello = function (a) {
  console.log(a)
}
// 将一個具名函數指派給變量。
const hello = function fn(a) {
  console.log(a)
  console.log(fn) // fn();
  console.log(fn === hello) // true;
}
console.log(fn) // ReferenceError: fn is not defined;
複制代碼      

具名函數​

​fn​

​​和​

​hello​

​​是同一個函數,但是作用範圍不一緻,​

​fn​

​​隻能在函數體内使用,相當于函數的一個局部變量,​

​hello​

​可在函數内部,外部調用。

Function 構造函數

可以傳遞任意數量的參數給​

​Function​

​構造函數,隻有最後一個參數會被當做函數體,如果隻有一個參數,該參數就是函數體。

const add = new Function('x', 'y', 'return x + y')
//等同于
function add(x, y) {
  return x + y
}
複制代碼      

Function構造函數可以不使用new指令,傳回結果完全一樣。

這些 function 的細節你都知道嗎?

函數的傳回值 return

​return​

​ 隻能出現在函數體内。

  1. 一個函數中可以有多個​

    ​return​

    ​語句。
  2. ​return​

    ​​後沒有任何傳回值,(傳回值為 ​

    ​undefined​

    ​​)代表直接提出函數執行,​

    ​return​

    ​​之後除了在 ​

    ​finally​

    ​中的代碼,都不會再執行。如下圖:
這些 function 的細節你都知道嗎?
  1. ​return​

    ​可以傳回任何資料類型的資料。
  2. 如果函數調用時在前面加上了​

    ​new​

    ​​字首,且傳回值不是一個對象或者沒有傳回值,則傳回​

    ​this​

    ​(該新對象),如下圖:
這些 function 的細節你都知道嗎?

函數調用

函數調用模式

使用函數調用模式調用函數時,非嚴格模式下,​

​this​

​​被綁定到全局對象;在嚴格模式​

​(use strict;)​

​​下,​

​this​

​​是​

​undefined​

​。

非嚴格模式: 

這些 function 的細節你都知道嗎?

嚴格模式:

這些 function 的細節你都知道嗎?

方法調用模式

當一個函數被儲存在對象的一個屬性時,我們稱它為一個方法。當一個方法被調用時,​

​this​

​被綁定到該對象。如果 調用表達式包含一個提取屬性的動作,那麼它就是被當做一個方法來調用。

var p = {
  a: 1,
  fn: function () {
    this.a = 2
  }
}
console.log(p.a) // 1
p.fn()
console.log(p.a) // 2
複制代碼      

構造器調用模式

如果函數或者方法調用之前帶有關鍵字 ​

​new​

​,它就構成構造函數調用。

  • 如果構造函數調用在圓括号内包含一組實參清單,先計算這些實參表達式,然後傳入函數内。
  • 如果構造函數沒有形參,​

    ​JavaScript​

    ​ 構造函數調用的文法是允許省略實參清單和圓括号的。凡是沒有形參的構造函數調 用都可以省略圓括号。
const o = new Object() 
// 等價于 
const o = new Object
複制代碼      

間接調用模式

​JavaScript​

​​ 中函數也是對象,函數對象也可以包含方法。​

​call​

​​ 和 ​

​apply​

​​方法可以用來間接地調用函數。 這兩個方法都允許顯式指定調用所需的​

​this​

​值,也就是說,任何函數可以作為任何對象的方法來調用,哪怕這個函數不是那個對象的方法。兩個方法都可以指定調用的實參。

  • ​call​

    ​方法使用它自有的實參清單作為函數的實參。
  • ​apply​

    ​法則要求以數組的形式傳入參數。
const obj = {}
function sum(x, y) {
  return x + y
}
console.log(sum.call(obj, 1, 2)) //3
console.log(sum.apply(obj, [1, 2])) //3
複制代碼      

函數參數

​JavaScript​

​​是弱類型語言,函數定義時未指定函數形參的類型,函數調用也未對傳入的實參值做任何類型檢查。實際上​

​JavaScript​

​函數調用甚至不檢查傳入形參的個數。

參數個數

1.當實參(函數被調用時摻入的實際參數值)比函數聲明指定的形參(函數定義時的參數清單)個數要少,剩下的形參都将設定為 undefined 值。
function add(x, y) {
  return x + y // x:1, y: undefined
}
add(1)
複制代碼      
2.當實參比形參個數要多時,剩下的實參沒有辦法直接獲得,需要使用 ​

​arguments​

​​ 對象來擷取。​

​arguments​

​​是個類數組,有數組的部分屬性,如​

​length​

​,可以通過索引去擷取對應的參數清單。
function add(x, y) {
  console.log(arguments)
  return x + y
}
add(1, 2, 3, 4, 5)

// Arguments(5) [1, 2, 3, 4, 5, callee: ƒ, Symbol(Symbol.iterator): ƒ]
複制代碼      
3.函數定義時也可以不給形參,到時直接通過​

​arguments[索引]​

​去擷取實參。

同名形參

在非嚴格模式下,函數中可以出現同名形參,且隻能通路最後出現的該名稱的形參。

function add(x, x, x) {
  return x
}
console.log(add(1, 2, 3)) // 3
// 嚴格模式編譯報錯。
複制代碼      

函數重載

在​

​Java​

​語言中,函數的重載是這樣定義的:方法名相同,參數的個數或者類型必須不同。

​JavaScript​

​​函數不能像​

​Java​

​上那樣實作重載。隻能通過檢查傳入函數中參數的類型和數量并作出不同的反應,來模仿方法的重載。

function doAdd() {
  if (arguments.length == 1) {
    alert(arguments[0] + 10)
  } else if (arguments.length == 2) {
    alert(arguments[0] + arguments[1])
  }
}
複制代碼      

參數傳遞

值傳遞:對于基本資料類型的參數傳遞。比如​

​String​

​​,​

​Number​

​​,​

​Boolean​

​​等。在向參數傳遞基本類型的值時,被傳遞的值會被複制到一個局部變量(命名參數或 ​

​arguments​

​對象的一個元素)。
function addTen(num) {
  num += 10
  return num
}
var count = 20
var result = addTen(count)
console.log(count) //20,沒有變化
console.log(result) //30
複制代碼      
引用傳遞:參數為引用類型的資料時​

​(Object、Array、......)​

​,傳遞過去的是引用資料的記憶體位址。會把這個位址複制給一個局部變量,是以這個局部變量的變化會直接改變指向該記憶體位址的引用資料。
function setName(obj) {
  //obj 在函數内部是一個局部變量
  obj.name = 'test'
}
var person = new Object()
setName(person)
console.log(person.name) //'test'
複制代碼      

函數屬性和方法

函數是 ​

​JavaScript​

​​ 中特殊的對象,可以擁有屬性和方法,就像普通的對象擁有屬性和方法一樣。甚至可以用​

​Function​

​構造函數來建立新的函數對象。

屬性

  • length:​

    ​arguments​

    ​​對象的​

    ​length​

    ​​屬性表示實參個數,而參數的​

    ​length​

    ​屬性則表示形參個數。 
  • prototype:每一個函數都有一個​

    ​prototype​

    ​​屬性,這個屬性指向了一個對象的引用,這個對象叫做原型對象(​

    ​prototype object​

    ​)。每一個函數都包含不同的原型對象。将函數用作構造函數時,新建立的對象會從原型對象上繼承屬性。
function fn() {}
var obj = new fn()
fn.prototype.a = 1
console.log(obj.a) //1
複制代碼      
  • name:函數定義了一個非标準的​

    ​name​

    ​​屬性,通過這個屬性可以通路到給定函數指定的名字,這個屬性的值永遠等于跟在​

    ​function​

    ​​關鍵字後面的辨別符,匿名函數的 ​

    ​name​

    ​屬性為空。

方法

每一個函數都包含兩個非繼承而來的方法:​

​apply​

​​和​

​call​

​方法。這兩個方法的用途都是在特定的作用域中調用函數。

調用方式:

  • func.apply(作用域對象, [a,b,c]);
  • func.call(作用域對象, a,b,c);

以對象 o 的方法來調用函數 f(),可以使用 call()和 apply():

window.color = "red";
var o = {color: "blue"};
function sayColor(){
  console.log(this.color);
}
sayColor();      //red
sayColor.call(this);  //red
sayColor.call(window); //red
sayColor.call(o);   //blue
sayColor.call(o) 

// 等價于:
o.sayColor = sayColor;
o.sayColor();  //blue
delete o.sayColor;
複制代碼      

在非嚴格模式下,使用函數的​

​call​

​​或​

​apply​

​​方法時,​

​null​

​​或​

​undefined​

​​值會被轉換為全局對象。而在嚴格模式下,函數的​

​this​

​值始終是指定的值。

應用:

找出數組中最大元素。
const a = [10, 2, 4, 15, 9]
Math.max.apply(null, a) // 15
// es6
Math.max(...a)
複制代碼      
将類數組轉成真正的數組。
var add = function(x,y){
  console.log(Array.prototype.slice.apply(arguments));
  // es6
  Array.from(arguments)
};
add(1,2); // [1, 2]
複制代碼      
将一個數組的值 push 到另一個數組中。
var a = []
Array.prototype.push.apply(a, [1, 2, 3])
// es6
a.push(...[1,2,3])
console.log(a) //[1,2,3]
複制代碼      
function getConfig(colors, size, otherOptions) {
  console.log(colors, size, otherOptions)
}
var defaultConfig = getConfig.bind(null, '#c00', '1024*768')
defaultConfig('123') //'#c00 1024*768 123'
defaultConfig('456') //'#c00 1024*768 456'      

繼續閱讀