什么是表达式
表达式的特征,
- 参与计算
- 有值的返回
- 放在括号里面
- 非变量声明的语句
下面我们看几个例子
//参与计算
1 + 1;
1 || 0;
//有值得返回
a = 1 * 1;
b = 1 && 0;
//括号里面
(a)
(1)
//等号右边,非变量声明的语句
let a = 1;
let b = a;
注意:JS引擎对表达式的操作就是为了获取表达式的值,也就是执行RHS的操作
也可以理解,执行RHS的语句叫,表达式。也就是JS引擎只关注变量的值,而不关注变量容器本身在什么地方
什么函数声明
在理解函数表达式之前,我们先理解什么是函数声明
- 具名函数声明,形如
function foo(){}的语句 - 匿名函数声明,形如
function (){}语句
也就是说,函数声明分成两种,具名和匿名
什么是函数表达式
- 声明的同时,参与了计算
- 声明语句被放进了括号中
- 声明放在等号的右边
也就是说,符合表达式特征的函数声明,就是函数表达式
也就是说,JS引擎执行代码的时候,只关心函数声明语句的值的时候,该语句就是函数表达式
下面举个例子
//参与了计算
+ function foo(){};
0 || function(){};
//放进了括号
(function foo(){});
(function(){});
//放在了等号的右边
var bar = function foo(){};
var bar_2 = function(){};
上面的例子都是函数表达式,因为其符合表达式的特征。
好吧,知道了这些有什么用呢?
仅仅是学会一个名词吗? 并不是,函数声明和函数表达式有天壤之别
-
代码生效的时间不同。
- 函数声明,是在代码编译时期,由编译器完成的动作
- 函数表达式,在JS引擎执行到该语句的时候,该代码才会生效
-
代码生效之后,作用域也不一样
- 函数声明后,当前作用域会存放一个变量,用来指向生成的函数
- 函数表达式生效之后,当前作用域并不会存在一个变量,用来指向该函数。无论该函数表达式是具名的,还是匿名的。(前提是该函数没有被赋值给另外一个变量)
//参与了计算
+ function foo(){};
0 || function(){};
console.log( foo ); // ReferenceError: foo is not defined
//放进了括号
(function foo2(){});
(function(){});
(function(){})();
console.log( foo2 ); // ReferenceError: foo2 is not defined
//放在了等号的右边
var bar = function foo3(){};
var bar_2 = function(){};
console.log( foo3 ); // ReferenceError: foo3 is not defined
console.log( bar ); // [Function: bar]
你可能会对结果感到奇怪,为什么会这样?要解释这个现象,我们要回到表达式的本质,JS引擎对表达式处理的本质。
该本质,就是只关心表达式的值,只对表达式进行求值操作,只对表达式进行RHS的查询。在面对匿名函数是如此,具名函数也是如此。
JS引擎并不关心你这函数叫什么名字,它只关心你这个函数的值,更准确的,是要拿到存放在堆区中的函数对象。拿到该对象之后,就可以参与计算,或者调用该函数,或者将其赋值给另外一个变量等一系列的操作
如果你还不熟悉LHS和RHS的概念,推荐你看我的这篇文章: JS-一定要知道的两种变量查询
上面对于函数表达式本质的描述,已经很精彩了。但是有一个漏洞。
具名函数表达式内部是可以访问函数本身的。递归调用会用到此功能。
var a = function b() {
console.log('b.name: ', b.name);
console.log('arguments.name: ', arguments.callee.name);
}
a();
// b.name: b
// arguments.name: b
很神奇吧,这是为啥呢?这个大家可以好好想想。这个问题我放在IIFE(立即函数表达式)这一章节了。
总结:
- 什么是表达式
- 什么是函数声明
- 什么是函数表达式
- 通过RHS和LHS去理解表达式,函数表达式