网络编程 
首页 > 网络编程 > 浏览文章

老生常谈JavaScript 函数表达式

(编辑:jimmy 日期: 2024/11/19 浏览:3 次 )

JavaScript中创建函数主要有两种方法:函数声明和函数表达式。这两种方式都有不同的适用场景。这篇笔记主要关注的是函数表达式的几大特点以及它的使用场景,下面一一描述。

主要特点

"htmlcode">

var sub = function(a1,a2){ 
  return a1-a2; 
} 

这个例子中函数表达式没有名称,属于匿名函数表达式。再看下面的例子:

var sub = function f(a1,a2){ 
  return a1-a2; 
} 
console.log(f(5,3));  //错误调用方式 
console.log(sub(5,3));  //正确调用方式 

在这个例子中,函数表达式的名称为f,这个名称f实际上变成了函数内部的一个局部变量,并且指代函数对象本身,在函数递归的时候有很大用处,后面会详细讲到。

"htmlcode">

console.log(add(1,2));  //"3" 
console.log(sub(5,3));  //"unexpected identifier",报错 
function add(a1,a2){ 
  return a1+a2; 
} 
var sub = function(a1,a2){ 
  return a1-a2; 
} 

第一条语句完全可以正常执行。对代码求值时,JavaScript引擎在第一遍就会声明函数并通过一个名为函数声明提升的过程将它们放到源代码树的顶部。也就是说在执行环境的创建阶段(函数被调用但还没有开始执行)就会对函数声明进行"hosting"操作。所以,即使声明函数的代码在调用它的代码后面,JavaScript引擎也会把函数声明提升到顶部。但是如果把函数声明更改为函数表达式,就会在执行期间报错。原因在于在执行到函数所在的语句之前,变量sub中并不会包含对函数的引用。也就是说在代码执行阶段,变量sub才会被赋值。除了以上不同,在其它方面函数声明和函数表达式的语法是等价的。

"htmlcode">

var sub = function f(a1,a2){ 
  console.log(typeof f); //"function" 
  return a1-a2; 
} 
console.log(typeof f);  //"Uncaught ReferenceError: f is not defined(…)" 

通过上面的例子可以看到,函数名称f只能在函数对象内部使用,函数表达式的函数名称并不存在于变量对象中。

使用场景

函数表达式的使用场景很多。下面主要描述的是函数递归以及代码模块化方面的应用。

"htmlcode">

function factorial(num){ 
  if(num <= 1){ 
     return 1; 
  }else{ 
     return num * factorial(num - 1); 
  } 
} 

这是一个经典的阶乘函数,但是这个例子存在的一个问题是函数名称factorial与函数体紧密耦合在一起,执行下面的语句就会报错:

var anotherFactorial = factorial; 
factorial = null; 
console.log(anotherFactorial(5));  //"Uncaught TypeError: factorial is not a function" 

报错的原因在于在函数体内部会调用factorial函数,而变量factorial对函数的引用已经被解除所以报错。这种情况的解决方法一般可以使用arguments.callee来解决,arguments.callee始终指向当前的函数,例如:

function factorial(num){ 
  if(num <= 1){ 
     return 1; 
  }else{ 
     return num * arguments.callee(num - 1); 
  } 
} 

 这样在此执行anotherFactorial函数就可以得到正确结果了。但是在严格模式"strict"下,arguments.callee是不能通过脚本访问的,这是就可以使用函数表达式来解决这个问题了,例如:

var factorial = (function f(num){ 
  if(num <= 1){ 
     return 1; 
  }else{ 
     return num * f(num - 1); 
  } 
}); 
console.log(factorial(5));  //"120" 

 "htmlcode">

var person = (function(){ 
  var _name = ""; 
  return{ 
    getName:function(){ 
       return _name; 
    }, 
    setName:function(newname){ 
       _name = newname; 
    } 
  }; 
}()); 
person.setName("John"); 
person.getName();  //"John"

 这个例子中创建了一个匿名函数表达式,这个函数表达式中包含了模块自身的私有变量和函数;这个函数表达式的执行结果返回一个对象,对象中包含了模块暴露给使用者的公共接口。代码模块化的具体形式还有很多,例如在一些常用的JavaScript库中通常都会使用类似下面例子的立即执行函数:

(function(){ 
  var _name = ""; 
  var root = this; 
  var person = { 
    getName: function(){ 
      return _name; 
    }, 
    setName: function(newname){ 
      _name = newname; 
    } 
  }; 
  root.person = person; 
}.call(this)); 
person.setName("John"); 
person.getName();  //"John"

 这种方式直接将包含模块公共接口的对象作为全局对象的一个属性,这样在其它地方直接可以使用全局对象的这个属性来使用这个模块了。

以上这篇老生常谈JavaScript 函数表达式就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。

上一篇:浅谈JavaScript 数据属性和访问器属性
下一篇:Ubuntu系统下Angularjs开发环境安装
荣耀猎人回归!七大亮点看懂不只是轻薄本,更是游戏本的MagicBook Pro 16.
人们对于笔记本电脑有一个固有印象:要么轻薄但性能一般,要么性能强劲但笨重臃肿。然而,今年荣耀新推出的MagicBook Pro 16刷新了人们的认知——发布会上,荣耀宣布猎人游戏本正式回归,称其继承了荣耀 HUNTER 基因,并自信地为其打出“轻薄本,更是游戏本”的口号。
众所周知,寻求轻薄本的用户普遍更看重便携性、外观造型、静谧性和打字办公等用机体验,而寻求游戏本的用户则普遍更看重硬件配置、性能释放等硬核指标。把两个看似难以相干的产品融合到一起,我们不禁对它产生了强烈的好奇:作为代表荣耀猎人游戏本的跨界新物种,它究竟做了哪些平衡以兼顾不同人群的各类需求呢?