JavaScript的各种常见函数定义方法


Posted in Javascript onSeptember 16, 2014

本文详细讲述了JavaScript的各种常见函数定义方法,分享给大家供大家参考。具体分析如下:

首先看一下JavaScript最常见的四种函数定义:

用Function构造函数定义的函数,代码如下:

var multiply = new Function('x', 'y', 'return x * y;');

函数声明,这种方式也是最为常见的一种:

function multiply(x, y) {
  return x * y;
}

函数表达式,声明为匿名函数然后赋值给一变量,很常见的方式:

var multiply = function(x, y) {
  return x * y;
}

函数表达式,但是函数声明为命名函数再赋值给一变量,长得跟上一种方式真像:

var multiply = function multi(x, y) {
  return x * y;
}

首先比较一下函数名,以及将函数赋值给的那个函数变量直接的关系,真绕……直观一点儿,从刚刚的例4说吧,就是multiply这个函数变量与multi这个函数名的关系:

函数名是不能够被修改的,相反的,函数变量是可以重新被赋值的。函数变量可以被重新赋值应该很好理解,我们第4个例子刚刚定义的multiply这个变量,看它不顺眼,重新赋值为:

multiply = function(x, y) {
  return x + y;
}

立马摇身一变,从乘法变成加法了。但是multi这个函数变量想变就是不可能的了,函数定义已经在那儿了,只要还保留这它的引用,它就是不会变的,可能这里不大好理解,先这样想着,往下看,慢慢就应该能理解了。

函数名同时是无法在函数外部使用的,它只在函数体内部可见,一个很简单的例子:

var foo = function bar() {
  alert('hello');
}
foo(); // 提示“hello”字符串
bar(); // 执行报错,bar未定义

和明显,这里的bar确实是一个函数名,但是它确实不能在外部调用。这时候肯定会有童鞋问干嘛这个例子还是长得那么乖,和例4一个样,怎么不用例2的方式呢?问得好,且听我慢慢分解。

继续说例4,我们可以看见函数名(multi)和函数变量(multiply),本不相同,其实两者根本就没有任何关系,因此没有保持一致的必要。说到这儿,我想上面4个例子应该可以精简到3个,例2和例4本质应该是一致的。什么,不信?嘻嘻,我还得继续卖关子哈~继续读下去~~

我们发现例2和例4相比,只不过少了var的函数变量,而例3与例4相比,只不过少了那个函数名,这里从现象上看,例2和例4的本质是相同的,铁证如下:

function foo() {}
alert(foo); // 提示包含“foo”的函数名
var bar = foo;
alert(bar); // 提示依然只包含“foo”的函数名,和bar半毛钱关系都没有

的确是铁证吧?上面的类似例2的代码结合起来写是不是就成例4的方式了?正确,这就是我刚刚所说的两者本质应该相同,只是用例2方式定义函数的时候,JS引擎帮我们做了一些事情,比如声明了函数名为multiply的函数,同时还悄悄定义了一个也叫multiply的变量,然后赋值给这个变量,两个完全一样的名字,我们自以为在使用函数名multiply的时候,实际是在用multiply这个函数变量,晕了吧~说实话,我也晕了~~总之我们调用的时候,实在用函数变量调用,而函数名是无法在外部调用函数的,因此有了我上述的推断。

但是这里要提到的一个小小的差别,函数声明方式定义的函数,与构造函数声明的或者函数表达式声明的不同之处在于,函数声明方式的函数可以在函数定义之前就调用……不说了,还是看代码:

foo(); // 提示Foo
function foo() {
  alert('Foo');
}
bar(); // 哥们,和上面确实不一样,就不要逞能,这不报错了?提示bar未定义
var bar = function() {
  alert('Bar');
}

再说说构造函数声明的函数,这样声明的函数是不会继承当前声明位置的作用域,它默认只会拥有全局作用域,然而这个是其他几种函数声明方式也一样有的,如下:

function foo() {
  var hi = 'hello';
  //return function() {
  //  alert(hi);
  //};
  return Function('return hi;');
}
foo()(); // 执行效果大家自己跑一下看看

可以想见,用构造函数声明返回的这个函数执行必然报错,因为其作用域(即全局作用域)中没有hi这个变量。

还有一点,就是往往大家要说构造函数方式声明的函数效率要低,这是为什么呢?今天从文档是得知是因为另外3种方式申明的函数只会被解析一次,其实他们存在于闭包中,但是那也只与作用域链有关,函数体是只会被解析一次的。但是构造函数方式呢,每次执行函数的时候,其函数体都会被解析一次,我们可以想想这样声明的函数是一个对象,其中存放了参数以及函数体,每次执行的时候都要先解析一次,参数和函数体,才会执行,这样必然效率低下。具体实验不知道如何做?

最后说一个大家都不怎么注意的地方,什么时候看似函数声明方式的方式却不是函数生命方式(还是这么绕~简单点儿说,就是例2的方式什么时候在不经意间就成其他方式了):

当成为表达式的一部分,就如同例3和例4。
不再是脚本本身或者函数的“源元素”(source element)。什么是源元素呢?即在脚本中的非嵌套语句或者函数体(A "source element" is a non-nested statement in the script or a function body),例如:

var x = 0;        // source element 
if (x == 0) {      // source element 
  x = 10;        // not a source element, 因为嵌套在了if语句里
  function boo() {}   // not a source element, 因为嵌套在了if语句里
} 
function foo() {     // source element 
  var y = 20;      // source element 
  function bar() {}   // source element 
  while (y == 10) {   // source element 
   function blah() {} // not a source element, 因为嵌套在了while语句里
   y++;        // not a source element, 因为嵌套在了while语句里
  } 
}

源元素的概念大概有了理解,继续刚刚说的函数声明,请看:

// 函数声明
function foo() {} 
  
// 函数表达式
(function bar() {}) 
  
// 函数表达式
x = function hello() {} 
 
if (x) { 
  // 函数表达式
  function world() {} 
}
 
// function statement 
function a() { 
  // function statement 
  function b() {} 
  if (0) { 
   // 函数表达式
   function c() {} 
  } 
}

最后这里说一下我自己的理解,之所以要区分函数声明与非函数声明,因为在我看了,函数声明方式的函数定义,在JS解析引擎执行的时候会将其提前声明,也就是像我们刚刚上面说的那样,可以在函数定义之前使用,实际上是解析引擎在我们使用前已经将其解析了,但是非函数声明式,就像表达式函数声明,JS解析引擎只会把var声明的变量提前定义,此时变量值为undefined,而真正对这个变量的赋值是在代码实际所在位置,因此上述提到报错都是undefined,实际变量已经定义了,只是还没有赋值,JS解析引擎不知道它为函数。

相信本文所述对大家javascript WEB程序设计的有一定的借鉴价值。

Javascript 相关文章推荐
javascript sudoku 数独智力游戏生成代码
Mar 27 Javascript
JS字符串函数扩展代码
Sep 13 Javascript
高效率JavaScript编写技巧整理
Aug 23 Javascript
JavaScript eval() 函数介绍及应用示例
Jul 29 Javascript
关闭页面时window.location事件未执行的原因分析及解决方案
Sep 01 Javascript
基于jQuery实现的双11天猫拆红包抽奖效果
Dec 01 Javascript
javascript基础语法学习笔记
Jan 04 Javascript
Javascript(es2016) import和require用法和区别详解
Aug 11 Javascript
react router 4.0以上的路由应用详解
Sep 21 Javascript
VUE 配置vue-devtools调试工具及安装方法
Sep 30 Javascript
详解如何实现Element树形控件Tree在懒加载模式下的动态更新
Apr 25 Javascript
JS实现商品橱窗特效
Jan 09 Javascript
JavaScript中合并数组的N种方法
Sep 16 #Javascript
JavaScript的null和undefined区别示例介绍
Sep 15 #Javascript
JavaScript中变量声明有var和没var的区别示例介绍
Sep 15 #Javascript
JavaScript的arguments对象应用示例
Sep 15 #Javascript
avascript中的自执行匿名函数应用示例
Sep 15 #Javascript
JavaScript function 的 length 属性使用介绍
Sep 15 #Javascript
css与javascript跨浏览器兼容性总结
Sep 15 #Javascript
You might like
PHP中使用循环实现的金字塔图形
2014/11/08 PHP
phplot生成图片类用法详解
2015/01/06 PHP
浅析ThinkPHP缓存之快速缓存(F方法)和动态缓存(S方法)(日常整理)
2015/10/26 PHP
基于命令行执行带参数的php脚本并取得参数的方法
2016/01/25 PHP
Centos 6.5下PHP 5.3安装ffmpeg扩展的步骤详解
2017/03/02 PHP
PHP大文件分块上传功能实例详解
2019/07/22 PHP
PHP读取XML文件的方法实例总结【DOMDocument及simplexml方法】
2019/09/10 PHP
DOMAssitant最新版 DOMAssistant 2.5发布
2007/12/25 Javascript
js鼠标点击事件在各个浏览器中的写法及Event对象属性介绍
2013/01/24 Javascript
ExtJS实现文件下载的方法实例
2013/11/09 Javascript
基于jquery实现百度新闻导航菜单滑动动画
2016/03/15 Javascript
js实现可键盘控制的简单抽奖程序
2016/07/13 Javascript
JavaScript基础——使用Canvas绘图
2016/11/02 Javascript
JS实现拖拽的方法分析
2016/12/20 Javascript
JavaScript实现类似淘宝的购物车效果
2017/03/16 Javascript
socket.io与pm2(cluster)集群搭配的解决方案
2017/06/02 Javascript
详解javascript 正则表达式之分组与前瞻匹配
2018/05/30 Javascript
layui 监听表格复选框选中值的方法
2018/08/15 Javascript
细说Vue组件的服务器端渲染的过程
2019/05/30 Javascript
Layui 带多选框表格监听事件以及按钮自动点击写法实例
2019/09/02 Javascript
Python中MySQLdb和torndb模块对MySQL的断连问题处理
2015/11/09 Python
Python中的条件判断语句基础学习教程
2016/02/07 Python
python 将列表中的字符串连接成一个长路径的方法
2018/10/23 Python
在python中实现对list求和及求积
2018/11/14 Python
VSCode中自动为Python文件添加头部注释
2019/11/14 Python
Python爬虫爬取百度搜索内容代码实例
2020/06/05 Python
keras训练浅层卷积网络并保存和加载模型实例
2020/07/02 Python
HTML5 Canvas+JS控制电脑或手机上的摄像头实例
2014/05/03 HTML / CSS
美国领先的商务贺卡出版商:The Gallery Collection
2018/02/13 全球购物
Perfume’s Club意大利官网:欧洲美妆电商
2019/05/03 全球购物
Johnson Fitness澳大利亚:高级健身器材
2021/03/16 全球购物
大学生毕业自我鉴定范文
2014/02/03 职场文书
80后职场人的职业生涯规划
2014/03/08 职场文书
警察群众路线整改措施
2014/09/26 职场文书
单位租房协议范本
2014/12/03 职场文书
Python pyecharts案例超市4年数据可视化分析
2022/08/14 Python