文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Javascript中怎么实现函数声明与递归调用

2024-04-02 19:55

关注

Javascript中怎么实现函数声明与递归调用,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。

1、函数声明

变量式声明先创建一个匿名函数,然后把它赋值给一个指定的变量:

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

通常我们不必关心等号右边表达式的作用域是全局还是某个闭包内,因为它只能通过等号左边的变量f来引用,应该关注的是变量f的作用域。如果f指向函数的引用被破坏(f = null),且函数没有被赋值给任何其它变量或对象属性,匿名函数会因为失去所有引用而被垃圾回收机制销毁。

也可以使用函数表达式创建函数:

function f() { // function body }

与变量式不同的是,这种声明方式会为函数的一个内置属性name赋值。同时把函数赋值给当前作用域的一个同名变量。(函数的name属性,configurable、enumerable和writable均为false)

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

Javascript变量有一个的特别之处,就是会把变量的声明提前,表达式式的函数声明,也会把整个函数的定义前置,因此你可以在函数定义之前使用它:

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

函数表达式的声明会被提升到作用域顶层,试试下面的代码,它们不是本文的重点:

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

Crockford建议永远使用***种方式声明函数,他认为第二种方式放宽了函数必须先声明后使用的要求从而会导致混乱。(Crockford是一个类似于罗素口中用来比喻维特根斯坦的"有良心的艺术家"那样的"有良心的程序员",这句话很拗口吧)

函数式声明

function f() {}

看起来是

var f = function f(){};

的简写。

var a = function b(){};

的表达式,创建一个函数并把内置的name属性赋值为"b",然后把这个函数赋值给变量a,你可以在外部使用a()来调用它,但却不能使用b(),因为函数已被赋值给a,所以不会再自动创建一个变量b,除非你使用var b = a声明一个变量b。当然这个函数的name是"b"而不是"a"。

使用Function构造函数也可用来创建函数:

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

这种方式其实是在全局作用域内生成一个匿名函数,并把它赋值给变量f。

2、递归调用

递归被用来简化许多问题,这需要在一个函数体中调用它自己:

// 一个简单的阶乘函数  var f = function (x) {      if (x === 1) {          return 1;      } else {          return x * f(x - 1);      }  };

Javascript中函数的巨大灵活性,导致在递归时使用函数名遇到困难,对于上面的变量式声明,f是一个变量,所以它的值很容易被替换:

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

函数是个值,它被赋给fn,我们期待使用fn(5)可以计算出一个数值,但是由于函数内部依然引用的是变量f,于是它不能正常工作了。

函数式的声明看起来好些,但很可惜:

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

看起来,一旦我们定义了一个递归函数,便须注意不要轻易改变变量的名字。

上面谈论的都是函数式调用,函数还有其它调用方式,比如当作对象方法调用。

我们常常这样声明对象:

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

声明一个匿名函数并把它赋值给对象的属性(fac)。

如果我们想要在这里写一个递归,就要引用属性本身:

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

当然,它也会遭遇和函数调用方式一样的问题:

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

方法被赋值给obj2的fac属性后,内部依然要引用obj1.fac,于是…失败了。

换一种方式会有所改进:

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关键字获取函数执行时的context中的属性,这样执行obj2.fac时,函数内部便会引用obj2的fac属性。

可是函数还可以被任意修改context来调用,那就是***的call和apply:

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

于是递归函数又不能正常工作了。

我们应该试着解决这种问题,还记得前面提到的一种函数声明的方式吗?

var a = function b(){};

这种声明方式叫做内联函数(inline function),虽然在函数外没有声明变量b,但是在函数内部,是可以使用b()来调用自己的,于是

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

就这样,我们有了一个可以在内部使用的名字,而不用担心递归函数被赋值给谁以及以何种方式被调用。

Javascript函数内部的arguments对象,有一个callee属性,指向的是函数本身。因此也可以使用arguments.callee在内部调用函数:

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

但arguments.callee是一个已经准备被弃用的属性,很可能会在未来的ECMAscript版本中消失,在ECMAscript 5中"use strict"时,不能使用arguments.callee。

看完上述内容,你们掌握Javascript中怎么实现函数声明与递归调用的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注编程网行业资讯频道,感谢各位的阅读!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     801人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     348人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     311人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     432人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     220人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯