1. 程式人生 > >Javascript學習---函式物件

Javascript學習---函式物件

我們已經知道Javascript裡的值都有對應的型別,函式始終特殊的值,它的型別是物件


name屬性

函式物件包含一些可用的物件,例如name屬性

function sayHi() {
  alert("Hi");
}

alert(sayHi.name); // sayHi

顯然,name屬性返回函式的名字


有趣的是,函式名字的指配是靈活的,例如:

let sayHi = function() {
  alert("Hi");
}

alert(sayHi.name); // sayHi (works!)

當函式名是預設值的時候也起作用,例如:

function f(sayHi = function() {}) {
  alert(sayHi.name); // sayHi (works!)
}

f();

同樣,物件的方法也有name屬性,例如:

let user = {

  sayHi() {
    // ...
  },

  sayBye: function() {
    // ...
  }

}

alert(user.sayHi.name); // sayHi
alert(user.sayBye.name); // sayBye

length屬性

函式的length屬性返回函式引數的個數,例如:

function f1(a) {}
function f2(a, b) {}
function many(a, b, ...more) {}

alert(f1.length); // 1
alert(f2.length); // 2
alert(many.length); // 2
在這裡我們可以看出,rest引數並不會被計算進去


自定義屬性

除了函式的內建屬性,我們可以新增自定義屬性到函式中,例如新增counter屬性來放回函式被呼叫次數:

function sayHi() {
  alert("Hi");

  // let's count how many times we run
  sayHi.counter++;
}
sayHi.counter = 0; // initial value

sayHi(); // Hi
sayHi(); // Hi

alert( `Called ${sayHi.counter} times` ); // Called 2 times
這裡需要注意的是,sayHi.counter=0來指定counter的值並不意味著counter是該函式的本地變數


函式的自定屬性特性有時候可以用來當做閉包使用,例如:

function makeCounter() {
  // instead of:
  // let count = 0

  function counter() {
    return counter.count++;
  };

  counter.count = 0;

  return counter;
}

let counter = makeCounter();
alert( counter() ); // 0
alert( counter() ); // 1

相比於閉包,如果count存在於外部變數中,那麼外部程式碼將無法訪問count,只有巢狀函式才可以修改它,例如:

function makeCounter() {

  function counter() {
    return counter.count++;
  };

  counter.count = 0;

  return counter;
}

let counter = makeCounter();

counter.count = 10;
alert( counter() ); // 10

命名函式表示式

先看下面兩個例子:

let sayHi = function(who) {
  alert(`Hello, ${who}`);
};

現在我們為函式表示式加多一個名字,如下:

let sayHi = function func(who) {
  alert(`Hello, ${who}`);
};

對於新增的函式名字,我們不能夠直接在外部呼叫,否則會報錯,例如:

let sayHi = function func(who) {
  alert(`Hello, ${who}`);
};

sayHi("John"); // Hello, John

func("Jonh");  //error

這是因為命名函式表示式的名字(func)有以下兩個特性:

(1)它只能在函式內部被引用或呼叫;

(2)在函式外部函式名是不可見的,如上邊的例子;


那麼使用命名函式表示式有什麼好處呢?常見的作用是用於內部的自身呼叫,例如下面的例子:

let sayHi = function(who) {
  if (who) {
    alert(`Hello, ${who}`);
  } else {
    sayHi("Guest");
  }
};

這裡sayHi()函式在內部呼叫了自身,顯然這沒什麼問題,但是考慮一下下面這種情況:

let sayHi = function(who) {
  if (who) {
    alert(`Hello, ${who}`);
  } else {
    sayHi("Guest"); // Error: sayHi is not a function
  }
};

let welcome = sayHi;
sayHi = null;

welcome(); // Error, the nested sayHi call doesn't work any more!

很明顯,sayHi的值如果發生變化的話,其內部的自身呼叫也要改變。為了解決這種情況,命名函式表示式的作用就很明顯,如下:

let sayHi = function func(who) {
  if (who) {
    alert(`Hello, ${who}`);
  } else {
    func("Guest"); // Now all fine
  }
};

let welcome = sayHi;
sayHi = null;

welcome(); // Hello, Guest (nested call works)

這裡要注意一點是,函式“內部名”為命名函式表示式所有,普通的函式宣告並不具備這樣的語法特性。