天天看點

JavaScript Function中你可能不知道的知識點

目錄

函數的聲明

函數的屬性與特性  

函數作用域

函數參數的傳遞方式

arguments 對象

閉包

函數是一段可以反複調用的代碼塊。函數還能接受輸入的參數,不同的參數會傳回不同的值。

函數的聲明

  • function指令
function print(s) {
  console.log(s);
}
           
  •  函數表達式
var print = function(s) {
  console.log(s);
};
           
  •  Function 構造函數
var add = new Function(
  'x',
  'y',
  'return x + y'
);

// 等同于
function add(x, y) {
  return x + y;
}
           

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

Function

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

函數的屬性與特性  

  • name屬性

函數的

name

屬性傳回函數的名字。

function f1() {}
f1.name // "f1"

// 如果是通過變量指派定義的函數,那麼name屬性傳回變量名。
var f2 = function () {};
f2.name // "f2"
           
  •  leng屬性

函數的

length

屬性傳回函數預期傳入的參數個數,即函數定義之中的參數個數。

function f(a, b) {}
f.length // 2
           

不管調用時傳入多少個參數,leng屬性值始終等于2,

length

屬性就是定義時的參數個數,與調用無關。

  • 函數名提升

JavaScript 引擎将函數名視同變量名,是以采用

function

指令聲明函數時,整個函數會像變量聲明一樣,被提升到代碼頭部。是以,下面的代碼不會報錯。

f();

function f() {}
           

函數作用域

作用域分全局作用域 和 函數作用域,全局作用域,變量在整個程式中一直存在,所有地方都可以讀取;函數作用域,變量隻在函數内部存在。

  • 在函數内部定義的變量,外部無法讀取,稱為“局部變量”。
function f(){
  var v = 1;
}

v // ReferenceError: v is not defined
           
  • 函數内部定義的變量,會在該作用域内覆寫同名全局變量。
var v = 1;

function f(){
  var v = 2;
  console.log(v);
}

f() // 2
v // 1
           
  • 函數作用域内部也會産生“變量提升”現象。

    var

    指令聲明的變量,不管在什麼位置,變量聲明都會被提升到函數體的頭部
  •  函數本身也是一個值,也有自己的作用域。就是其聲明時所在的作用域,與其運作時所在的作用域無關(與調用無關)。
var a = 1;
var x = function () {
  console.log(a);
};

function f() {
  var a = 2;
  x();
}

f() // 1


// 函數執行時所在的作用域,是定義時的作用域,而不是調用時所在的作用域。
var x = function () {
  console.log(a);
};

function y(f) {
  var a = 2;
  f();
}

y(x)
// ReferenceError: a is not defined


// 同樣的,函數體内部聲明的函數,作用域綁定函數體内部。
function foo() {
  var x = 1;
  function bar() {
    console.log(x);
  }
  return bar;
}

var x = 2;
var f = foo();
f() // 1
           

函數參數的傳遞方式

  • 函數參數如果是原始類型的值(數值、字元串、布爾值),傳遞方式是傳值傳遞

這意味着,在函數體内修改參數值,不會影響到函數外部。

var p = 2;

function f(p) {
  p = 3;
}
f(p);

p // 2
           
  •  函數參數是複合類型的值(數組、對象、其他函數),傳遞方式是傳址傳遞。

也就是說,傳入函數的原始值的位址,是以在函數内部修改參數,将會影響到原始值。

var obj = { p: 1 };

function f(o) {
  o.p = 2;
}
f(obj);

obj.p // 2
           

 如果函數内部修改的,不是參數對象的某個屬性,而是替換掉整個參數,這時不會影響到原始值。

var obj = [1, 2, 3];

function f(o) {
  o = [2, 3, 4];
}
f(obj);

obj // [1, 2, 3]
           

這是因為,形式參數(

o

)的值實際是參數

obj

的位址,重新對

o

指派導緻

o

指向另一個位址,儲存在原位址上的值當然不受影響。

就是說 o 其實作為參數代表數組 [1,2,3] 的位址,又被賦予了 [2,3,4] 的位址,是以不會影響到 obj。

arguments 對象

  • 由于 JavaScript 允許函數有不定數目的參數,是以需要一種機制,可以在函數體内部讀取所有參數。這就是

    arguments

    對象的由來。

arguments

對象包含了函數運作時的所有參數,

arguments[0]

就是第一個參數,

arguments[1]

就是第二個參數,以此類推。這個對象隻有在函數體内部,才可以使用。

  • 雖然

    arguments

    很像數組,但它是一個對象。數組專有的方法(比如

    slice

    forEach

    ),不能在

    arguments

    對象上直接使用。
  • arguments

    對象帶有一個

    callee

    屬性,傳回它所對應的原函數。
var f = function () {
  console.log(arguments.callee === f);
}

f() // true
           

閉包

了解閉包,首先必須了解變量作用域。前面提到,JavaScript 有兩種作用域:全局作用域和函數作用域。函數内部可以直接讀取全局變量。但是函數外部無法讀取函數内部聲明的變量。出于種種原因,需要得到函數内的局部變量。正常情況下,這是辦不到的,隻有通過變通方法才能實作。那就是在函數的内部,再定義一個函數。

function f1() {
  var n = 999;
  function f2() {
    console.log(n);
  }
  return f2;
}

var result = f1();
result(); // 999
           

上面代碼中,函數

f1

的傳回值就是函數

f2

,由于

f2

可以讀取

f1

的内部變量,是以就可以在外部獲得

f1

的内部變量了。

閉包就是函數

f2,

由于在 JavaScript 語言中,隻有函數内部的子函數才能讀取内部變量,是以可以把閉包簡單了解成“定義在一個函數内部的函數”。

閉包的最大用處用兩個:

  1. 可以讀取外層函數内部的變量
  2. 讓外層函數的變量始終保持在記憶體中

為什麼閉包能夠傳回外層函數的内部變量:

閉包用到了外層變量,導緻外層函數不能從記憶體釋放。隻要閉包沒有被垃圾回收機制清除,外層函數提供的運作環境也不會被清除,它的内部變量就始終儲存着目前值,供閉包讀取。

繼續閱讀