溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Javascript中怎么實(shí)現(xiàn)函數(shù)聲明與遞歸調(diào)用

發(fā)布時(shí)間:2021-07-14 14:35:02 來源:億速云 閱讀:122 作者:Leah 欄目:web開發(fā)

Javascript中怎么實(shí)現(xiàn)函數(shù)聲明與遞歸調(diào)用,相信很多沒有經(jīng)驗(yàn)的人對此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個(gè)問題。

1、函數(shù)聲明

變量式聲明先創(chuàng)建一個(gè)匿名函數(shù),然后把它賦值給一個(gè)指定的變量:

var f = function () { // function body };

通常我們不必關(guān)心等號右邊表達(dá)式的作用域是全局還是某個(gè)閉包內(nèi),因?yàn)樗荒芡ㄟ^等號左邊的變量f來引用,應(yīng)該關(guān)注的是變量f的作用域。如果f指向函數(shù)的引用被破壞(f = null),且函數(shù)沒有被賦值給任何其它變量或?qū)ο髮傩?,匿名函?shù)會因?yàn)槭ニ幸枚焕厥諜C(jī)制銷毀。

也可以使用函數(shù)表達(dá)式創(chuàng)建函數(shù):

function f() { // function body }

與變量式不同的是,這種聲明方式會為函數(shù)的一個(gè)內(nèi)置屬性name賦值。同時(shí)把函數(shù)賦值給當(dāng)前作用域的一個(gè)同名變量。(函數(shù)的name屬性,configurable、enumerable和writable均為false)

function f() { // function body }  console.log(f.name); // "f"  console.log(f); // f()

Javascript變量有一個(gè)的特別之處,就是會把變量的聲明提前,表達(dá)式式的函數(shù)聲明,也會把整個(gè)函數(shù)的定義前置,因此你可以在函數(shù)定義之前使用它:

console.log(f.name); // "f"  console.log(f); // f()  function f() { // function body }

函數(shù)表達(dá)式的聲明會被提升到作用域頂層,試試下面的代碼,它們不是本文的重點(diǎn):

var a = 0;  console.log(a); // 0 or a()?  function a () {}

Crockford建議永遠(yuǎn)使用***種方式聲明函數(shù),他認(rèn)為第二種方式放寬了函數(shù)必須先聲明后使用的要求從而會導(dǎo)致混亂。(Crockford是一個(gè)類似于羅素口中用來比喻維特根斯坦的"有良心的藝術(shù)家"那樣的"有良心的程序員",這句話很拗口吧)

函數(shù)式聲明

function f() {}

看起來是

var f = function f(){};

的簡寫。

var a = function b(){};

的表達(dá)式,創(chuàng)建一個(gè)函數(shù)并把內(nèi)置的name屬性賦值為"b",然后把這個(gè)函數(shù)賦值給變量a,你可以在外部使用a()來調(diào)用它,但卻不能使用b(),因?yàn)楹瘮?shù)已被賦值給a,所以不會再自動創(chuàng)建一個(gè)變量b,除非你使用var b = a聲明一個(gè)變量b。當(dāng)然這個(gè)函數(shù)的name是"b"而不是"a"。

使用Function構(gòu)造函數(shù)也可用來創(chuàng)建函數(shù):

var f = new Function("a,b,c","return a+b+c;");

這種方式其實(shí)是在全局作用域內(nèi)生成一個(gè)匿名函數(shù),并把它賦值給變量f。

2、遞歸調(diào)用

遞歸被用來簡化許多問題,這需要在一個(gè)函數(shù)體中調(diào)用它自己:

// 一個(gè)簡單的階乘函數(shù)  var f = function (x) {      if (x === 1) {          return 1;      } else {          return x * f(x - 1);      }  };

Javascript中函數(shù)的巨大靈活性,導(dǎo)致在遞歸時(shí)使用函數(shù)名遇到困難,對于上面的變量式聲明,f是一個(gè)變量,所以它的值很容易被替換:

var fn = f;  f = function () {};

函數(shù)是個(gè)值,它被賦給fn,我們期待使用fn(5)可以計(jì)算出一個(gè)數(shù)值,但是由于函數(shù)內(nèi)部依然引用的是變量f,于是它不能正常工作了。

函數(shù)式的聲明看起來好些,但很可惜:

function f(x) {      if (x === 1) {          return 1;      } else {          return x * f(x - 1);      }  }  var fn = f;  f = function () {}; // may been warning by browser  fn(5); // NaN

看起來,一旦我們定義了一個(gè)遞歸函數(shù),便須注意不要輕易改變變量的名字。

上面談?wù)摰亩际呛瘮?shù)式調(diào)用,函數(shù)還有其它調(diào)用方式,比如當(dāng)作對象方法調(diào)用。

我們常常這樣聲明對象:

var obj1 = {      num : 5,      fac : function (x) {          // function body      }  };

聲明一個(gè)匿名函數(shù)并把它賦值給對象的屬性(fac)。

如果我們想要在這里寫一個(gè)遞歸,就要引用屬性本身:

var obj1 = {      num : 5,      fac : function (x) {          if (x === 1) {              return 1;          } else {              return x * obj1.fac(x - 1);          }      }  };

當(dāng)然,它也會遭遇和函數(shù)調(diào)用方式一樣的問題:

var obj2 = {fac: obj1.fac};  obj1 = {};  obj2.fac(5); // Sadness

方法被賦值給obj2的fac屬性后,內(nèi)部依然要引用obj1.fac,于是…失敗了。

換一種方式會有所改進(jìn):

var obj1 = {       num : 5,       fac : function (x) {          if (x === 1) {              return 1;          } else {              return x * this.fac(x - 1);          }      }  };  var obj2 = {fac: obj1.fac};  obj1 = {};  obj2.fac(5); // ok

通過this關(guān)鍵字獲取函數(shù)執(zhí)行時(shí)的context中的屬性,這樣執(zhí)行obj2.fac時(shí),函數(shù)內(nèi)部便會引用obj2的fac屬性。

可是函數(shù)還可以被任意修改context來調(diào)用,那就是***的call和apply:

obj3 = {};  obj1.fac.call(obj3, 5); // dead again

于是遞歸函數(shù)又不能正常工作了。

我們應(yīng)該試著解決這種問題,還記得前面提到的一種函數(shù)聲明的方式嗎?

var a = function b(){};

這種聲明方式叫做內(nèi)聯(lián)函數(shù)(inline function),雖然在函數(shù)外沒有聲明變量b,但是在函數(shù)內(nèi)部,是可以使用b()來調(diào)用自己的,于是

var fn = function f(x) {      // try if you write "var f = 0;" here      if (x === 1) {          return 1;      } else {          return x * f(x - 1);      }  };  var fn2 = fn;  fn = null;  fn2(5); // OK
// here show the difference between "var f = function f() {}" and "function f() {}"  var f = function f(x) {      if (x === 1) {          return 1;      } else {          return x * f(x - 1);      }  };  var fn2 = f;  f = null;  fn2(5); // OK
var obj1 = {      num : 5,      fac : function f(x) {          if (x === 1) {              return 1;          } else {              return x * f(x - 1);          }      }  };  var obj2 = {fac: obj1.fac};  obj1 = {};  obj2.fac(5); // ok   var obj3 = {};  obj1.fac.call(obj3, 5); // ok

就這樣,我們有了一個(gè)可以在內(nèi)部使用的名字,而不用擔(dān)心遞歸函數(shù)被賦值給誰以及以何種方式被調(diào)用。

Javascript函數(shù)內(nèi)部的arguments對象,有一個(gè)callee屬性,指向的是函數(shù)本身。因此也可以使用arguments.callee在內(nèi)部調(diào)用函數(shù):

function f(x) {      if (x === 1) {          return 1;      } else {          return x * arguments.callee(x - 1);      }  }

但arguments.callee是一個(gè)已經(jīng)準(zhǔn)備被棄用的屬性,很可能會在未來的ECMAscript版本中消失,在ECMAscript 5中"use strict"時(shí),不能使用arguments.callee。

看完上述內(nèi)容,你們掌握J(rèn)avascript中怎么實(shí)現(xiàn)函數(shù)聲明與遞歸調(diào)用的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI