Open felix-cao opened 6 years ago
在 JavaScript 语言里,主要有三种方式定义函数,分别是:
JavaScript
Function Declaration
Function Expression
Function
下面依次来聊一聊
函数声明是 JavaScript 最常见的函数定义法, 函数声明必须有如下5个要素:
function
()
,
{}
下面就是一个通过函数声明的方式创建名为 person 的函数
person
function person(name) { console.log(name); return name; } person('up8');
函数声明的一个重要特性就是提升(hoisting), 指的是整个函数体的提升, 也就是说可以在函数声明前调用函数。
hoisting
在 《JavaScript 作用域》 中 4.3 节,我们有一个案例提炼出来是这样的:
console.log('start:', a); function a() {console.log(4)}
虽然函数 a 定义在后,但是它是函数声明,根据它的提升特性,它的整个函数体 JavaScript 引擎在编译时都会提升至 console.log('start:', a);的前面,所以它的输出结果是:输出结果是 start: ƒ a() {console.log(4)}
a
console.log('start:', a);
start: ƒ a() {console.log(4)}
如果同一个函数被多次声明,后面的声明就会覆盖前面的声明。
function add(x,y) { return x+y; } function add(x,y,z) { return n=x+y+z; } add(2, 3); // 结果是啥?
上面代码中,后一次的函数声明覆盖了前面一次。而且,由于函数名的提升,前一次声明在任何时候都是无效的,这一点要特别注意。同样再去看看 《JavaScript 作用域》 中 4.3 节 中的代码。
var person = function(name) { console.log(name); return name; } person('up8');
在 JavaScript 语言里, 函数也是对象,对象可以赋值给一个变量, 函数表达式是将一个匿名函数(anonymous function)赋值给一个变量,function 关键词后面没有标识符
=
匿名函数(anonymous function), 也叫拉姆达函数:使用 function 关键字声明一个函数,但是未给函数名,所以叫匿名函数,匿名函数属于函数表达式。 匿名函数有很多作用,赋予一个变量则创建函数,赋予一个事件则成为事件处理程序,或创建闭包等等
匿名函数是函数式编程一个重要的特性,它让函数与函数之间变得很灵活。
// 下面的匿名函数会导致语法错误,虽然匿名函数术语函数表达式,但是未进行赋值操作 // 所以 JavaScript 引擎将开头的 function 关键字当做函数声明 function() { console.log('Hello world'); }
这种方式在实际开发中,我们是不用的,但是这里有一个知识点和一个坑,很多面试题会碰到,所以顺带聊一聊
Function()
var person = new Function('name', 'console.log(name); return name;');
Function() 构造器可以传入任意数量的字符串实参,最后一个实参所表示的文本是函数体,可以包含任意数量的 JavaScript 语句。如果构造的函数不包含任何参数,则只需传入一个函数体即可。
与前两者方式不同的是,Function() 构造器允许 JavaScript 在运行时动态地创建并翻译函数。每次调用 Function() 构造器都会解析函数体,并创建新的函数对象。因而,在循环或多次调用的函数中执行这个构造函数,执行效率会受影响。相比之下,循环中的嵌套函数和函数定义表达式则不会每次执行时都重新编译。
var name = 'up8'; // 顶层函数 function person() { var name = 'up8.wang' // 局部变量 return new Function('console.log(name)'); // 无法捕获局部作用域 } person()(); // 控制台输出up8,说明构造函数的编译在顶层函数执行
我们可以将 Function() 构造器认为是在全局作用域中执行的 eval()。在实际编程中,Function()构造器很少用到,前两中定义方法使用比较普遍。
eval()
在函数体内,遇到 return,函数就执行完毕,并返回结果。如果没有 return ,函数执行完毕也会返回结果,只是返回的是 undefined
return
undefined
我们要注意的是 return 有一个坑,在 JavaScript 中,有一个自动在行末添加分号的机制,看下面的示例:
function person() { return {name: 'Felix Cao'}; } person() // {name: 'Felix Cao'};
如果我们写成这样的话:
function person() { return // 自动加了分好,相当于 return undefined {name: 'Felix Cao'}; // 因为上面已经返回,所以这句无法执行 } person() // undefined
在
JavaScript
语言里,主要有三种方式定义函数,分别是:Function Declaration
Function Expression
Function
构造器下面依次来聊一聊
一、函数声明
函数声明是
JavaScript
最常见的函数定义法, 函数声明必须有如下5个要素:function
是函数声明关键词function
关键词后面有至少一个空格,用于分割,然后是函数名标识符。()
,
分隔{}
,花括号里是函数体下面就是一个通过函数声明的方式创建名为
person
的函数1.1、特性一、声明提升
函数声明的一个重要特性就是提升(
hoisting
), 指的是整个函数体的提升, 也就是说可以在函数声明前调用函数。在 《JavaScript 作用域》 中 4.3 节,我们有一个案例提炼出来是这样的:
虽然函数
a
定义在后,但是它是函数声明,根据它的提升特性,它的整个函数体JavaScript
引擎在编译时都会提升至console.log('start:', a);
的前面,所以它的输出结果是:输出结果是start: ƒ a() {console.log(4)}
1.2、特性二、函数声明覆盖
如果同一个函数被多次声明,后面的声明就会覆盖前面的声明。
上面代码中,后一次的函数声明覆盖了前面一次。而且,由于函数名的提升,前一次声明在任何时候都是无效的,这一点要特别注意。同样再去看看 《JavaScript 作用域》 中 4.3 节 中的代码。
二、函数表达式(function expression)
在
JavaScript
语言里, 函数也是对象,对象可以赋值给一个变量, 函数表达式是将一个匿名函数(anonymous function)赋值给一个变量,function 关键词后面没有标识符JavaScript
的=
等值操作符,将右边的匿名函数赋值给左边的变量匿名函数是函数式编程一个重要的特性,它让函数与函数之间变得很灵活。
三、Function 构造器
这种方式在实际开发中,我们是不用的,但是这里有一个知识点和一个坑,很多面试题会碰到,所以顺带聊一聊
JavaScript
中所有的函数都是Function()
构造器的实例对象Function()
构造器所创建的函数并不是使用词法作用域,函数体代码的编译总在顶层函数执行。Function()
构造器可以传入任意数量的字符串实参,最后一个实参所表示的文本是函数体,可以包含任意数量的JavaScript
语句。如果构造的函数不包含任何参数,则只需传入一个函数体即可。与前两者方式不同的是,
Function()
构造器允许JavaScript
在运行时动态地创建并翻译函数。每次调用Function()
构造器都会解析函数体,并创建新的函数对象。因而,在循环或多次调用的函数中执行这个构造函数,执行效率会受影响。相比之下,循环中的嵌套函数和函数定义表达式则不会每次执行时都重新编译。我们可以将
Function()
构造器认为是在全局作用域中执行的eval()
。在实际编程中,Function()
构造器很少用到,前两中定义方法使用比较普遍。四、 return 语句及其坑
在函数体内,遇到
return
,函数就执行完毕,并返回结果。如果没有return
,函数执行完毕也会返回结果,只是返回的是undefined
我们要注意的是
return
有一个坑,在JavaScript
中,有一个自动在行末添加分号的机制,看下面的示例:如果我们写成这样的话:
Reference