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 相关文章推荐
jQuery学习3:操作元素属性和特性
Feb 07 Javascript
JQuery 常用方法和事件详细介绍
Apr 18 Javascript
JavaScript中为什么null==0为false而null大于=0为true(个人研究)
Sep 16 Javascript
node.js中的path.extname方法使用说明
Dec 09 Javascript
jQuery热气球动画半透明背景的后台登录界面代码分享
Aug 28 Javascript
浅析JavaScript中的对象类型Object
May 26 Javascript
JS实现滑动门效果的方法详解
Dec 19 Javascript
Vue.js使用$.ajax和vue-resource实现OAuth的注册、登录、注销和API调用
May 10 Javascript
用vscode开发vue应用的方法步骤
May 06 Javascript
bootstrap-table formatter 使用vue组件的方法
May 09 Javascript
vue中v-show和v-if的异同及v-show用法
Jun 06 Javascript
vue2.* element tabs tab-pane 动态加载组件操作
Jul 19 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开发模式(简写版)
2007/03/15 PHP
PHP 执行系统外部命令 system() exec() passthru()
2009/08/11 PHP
php中通过正则表达式下载内容中的远程图片的函数代码
2012/01/10 PHP
php解析xml提示Invalid byte 1 of 1-byte UTF-8 sequence错误的处理方法
2013/11/14 PHP
php页面缓存方法小结
2015/01/10 PHP
PHP+shell实现多线程的方法
2015/07/01 PHP
浅析php静态方法与非静态方法的用法区别
2016/05/17 PHP
ThinkPHP3.2框架使用addAll()批量插入数据的方法
2017/03/16 PHP
如何让您的中波更粗更长 - 中波框形天线制作
2021/03/10 无线电
从新浪弄下来的全屏广告代码 与使用说明
2007/03/15 Javascript
jQuery 常见操作实现方式和常用函数方法总结
2011/05/06 Javascript
js的.innerHTML = ""IE9下显示有错误的解决方法
2013/09/16 Javascript
setInterval计时器不准的问题解决方法
2014/05/08 Javascript
JavaScript数组和循环详解
2015/04/27 Javascript
基于node.js依赖express解析post请求四种数据格式
2017/02/13 Javascript
vue.js加载新的内容(实例代码)
2017/06/01 Javascript
在一般处理程序(ashx)中弹出js提示语
2017/08/16 Javascript
vue中如何实现后台管理系统的权限控制的方法步骤
2019/09/05 Javascript
javascript设计模式之迭代器模式
2020/01/30 Javascript
解决vue+webpack项目接口跨域出现的问题
2020/08/10 Javascript
vue+iview分页组件的封装
2020/11/17 Vue.js
[03:58]2014DOTA2国际邀请赛 龙宝赛后解密DK获胜之道
2014/07/14 DOTA
[55:48]VGJ.S vs TNC Supermajor 败者组 BO3 第二场 6.6
2018/06/07 DOTA
[02:53]2018年度DOTA2最佳战队-完美盛典
2018/12/17 DOTA
python脚本实现查找webshell的方法
2014/07/31 Python
PyQt5 如何让界面和逻辑分离的方法
2020/03/24 Python
基于Python爬取股票数据过程详解
2020/10/21 Python
python爬虫搭配起Bilibili唧唧的流程分析
2020/12/01 Python
python基于爬虫+django,打造个性化API接口
2021/01/21 Python
Myholidays美国:在线旅游网站
2019/08/16 全球购物
会计应届生的自荐信
2013/12/13 职场文书
个人委托书
2014/07/31 职场文书
2014小学二年级班主任工作总结
2014/12/05 职场文书
中学教师教学工作总结
2015/08/13 职场文书
简单实现一个手持弹幕功能+文字抖动特效
2021/03/31 HTML / CSS
pytorch训练神经网络爆内存的解决方案
2021/05/22 Python