Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JavaScript 函数的三种定义方式 #47

Open
felix-cao opened this issue Sep 4, 2018 · 0 comments
Open

JavaScript 函数的三种定义方式 #47

felix-cao opened this issue Sep 4, 2018 · 0 comments

Comments

@felix-cao
Copy link
Owner

felix-cao commented Sep 4, 2018

JavaScript 语言里,主要有三种方式定义函数,分别是:

  • 函数声明,Function Declaration
  • 函数表达式,Function Expression
  • Function 构造器

下面依次来聊一聊

一、函数声明

函数声明是 JavaScript 最常见的函数定义法, 函数声明必须有如下5个要素:

  • 起始位置的 function 是函数声明关键词
  • function 关键词后面有至少一个空格,用于分割,然后是函数名标识符。
  • 函数名之后跟着圆括号 ()
  • 圆括号里是参数列表,多个参数之间使用逗号 , 分隔
  • 圆括号后是花括号 {},花括号里是函数体

下面就是一个通过函数声明的方式创建名为 person 的函数

function person(name) {
  console.log(name);
  return name;
}
person('up8');

1.1、特性一、声明提升

函数声明的一个重要特性就是提升(hoisting), 指的是整个函数体的提升, 也就是说可以在函数声明前调用函数

《JavaScript 作用域》 中 4.3 节,我们有一个案例提炼出来是这样的:

console.log('start:', a);
function a() {console.log(4)}

虽然函数 a 定义在后,但是它是函数声明,根据它的提升特性,它的整个函数体 JavaScript 引擎在编译时都会提升至 console.log('start:', a);的前面,所以它的输出结果是:输出结果是 start: ƒ a() {console.log(4)}

1.2、特性二、函数声明覆盖

如果同一个函数被多次声明,后面的声明就会覆盖前面的声明。

function add(x,y) {
  return x+y;
}
function add(x,y,z) {
  return n=x+y+z;
}
add(2, 3); // 结果是啥?

上面代码中,后一次的函数声明覆盖了前面一次。而且,由于函数名的提升,前一次声明在任何时候都是无效的,这一点要特别注意。同样再去看看 《JavaScript 作用域》 中 4.3 节 中的代码。

二、函数表达式(function expression)

var person = function(name) {
  console.log(name);
  return name;
}
person('up8');

JavaScript 语言里, 函数也是对象,对象可以赋值给一个变量, 函数表达式是将一个匿名函数(anonymous function)赋值给一个变量,function 关键词后面没有标识符

  • 先定义一个匿名函数
  • 利用 JavaScript= 等值操作符,将右边的匿名函数赋值给左边的变量

匿名函数(anonymous function), 也叫拉姆达函数:使用 function 关键字声明一个函数,但是未给函数名,所以叫匿名函数,匿名函数属于函数表达式。
匿名函数有很多作用,赋予一个变量则创建函数,赋予一个事件则成为事件处理程序,或创建闭包等等

匿名函数是函数式编程一个重要的特性,它让函数与函数之间变得很灵活。

// 下面的匿名函数会导致语法错误,虽然匿名函数术语函数表达式,但是未进行赋值操作
// 所以 JavaScript 引擎将开头的 function 关键字当做函数声明
function() {
    console.log('Hello world');
}

三、Function 构造器

这种方式在实际开发中,我们是不用的,但是这里有一个知识点和一个坑,很多面试题会碰到,所以顺带聊一聊

  • 一个知识点:在 JavaScript 中所有的函数都是 Function() 构造器的实例对象
  • 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()构造器很少用到,前两中定义方法使用比较普遍。

四、 return 语句及其坑

在函数体内,遇到 return,函数就执行完毕,并返回结果。如果没有 return ,函数执行完毕也会返回结果,只是返回的是 undefined

我们要注意的是 return 有一个坑,在 JavaScript 中,有一个自动在行末添加分号的机制,看下面的示例:

function person() {
  return {name: 'Felix Cao'};
}
person() // {name: 'Felix Cao'};

如果我们写成这样的话:

function person() {
  return  // 自动加了分好,相当于 return undefined
  {name: 'Felix Cao'}; // 因为上面已经返回,所以这句无法执行
}
person() // undefined

Reference

@felix-cao felix-cao changed the title JavaScript的三种函数定义方式 JavaScript 函数的三种定义方式 Dec 22, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant