你应该知道的 JavaScript function(函数) – JavaScript 完全手册(2018版)

10年服务1亿前端开发工程师

小编推荐:掘金是一个面向程序员的高质量技术社区,从 一线大厂经验分享到前端开发最佳实践,无论是入门还是进阶,来掘金你不会错过前端开发的任何一个技术干货。

注:本文为 《 JavaScript 完全手册(2018版) 》第17节,你可以查看该手册的完整目录。

现在我们将学习 JavaScript 中所有关于函数的知识,从概述到小细节帮助你更好的使用 JavaScript 函数。愚人码头注:本文没有一句废话,对于新手,可以作为该知识点的入门指南,对于有经验的开发人员可以作为一次很好的回顾。

JavaScript 中的所有内容都在函数中执行。

函数是一个自包含的代码块,可以定义一次,并运行任意次数。

函数可以选择接受参数,并返回一个值。

JavaScript中的函数是 对象,一种特殊的对象:function objects(函数对象)。

另外,函数在 JavaScript 中是一等公民,因为它们可以被赋值给一个值,它们可以作为参数传递并用作返回值。

让我们从“旧的”,ES6 / ES2015之前的语法开始。 这是一个函数声明:

function dosomething(foo) {
  // do something
}

在 ES6 / ES2015 流行的当下,简称为常规函数。

可以将函数分配给变量(这称为函数表达式):

const dosomething = function(foo) {
  // do something
}

命名函数表达式类似,但在堆栈调用跟踪中更好用,这在发生错误时很有用 – 它保存函数的名称:

const dosomething = function dosomething(foo) {
  // do something
}

ES6 / ES2015 引入了箭头函数,在使用内联函数时,它们特别适合用作参数或回调函数:

const dosomething = foo => {
  //do something
}

箭头函数与上面的其他函数定义有很大的不同,我们会在后面的章节中详细介绍。

参数

一个函数可以有一个或多个参数。

const dosomething = () => {
  //do something
}
const dosomethingElse = foo => {
  //do something
}
const dosomethingElseAgain = (foo, bar) => {
  //do something
}

从ES6 / ES2015开始,函数可以具有参数的默认值:

const dosomething = (foo = 1, bar = 'hey') => {
  //do something
}

这允许您在不填充所有参数的情况下调用函数:

dosomething(3)
dosomething()

ES2017引入了参数的尾随逗号,这个功能有助于减少因移动参数时丢失逗号而导致的错误(例如,移动中间的最后一个):

const dosomething = (foo = 1, bar = 'hey') => {
  //do something
}
dosomething(2, 'ho!')

您可以将所有参数包装在一个数组中,并在调用函数时使用展开运算符:

const dosomething = (foo = 1, bar = 'hey') => {
  //do something
}
const args = [2, 'ho!']
dosomething(...args)

使用许多参数,记住顺序可能很困难。这时使用对象解构这个对象允许保留参数名称:

const dosomething = ({ foo = 1, bar = 'hey' }) => {
  //do something
  console.log(foo) // 2
  console.log(bar) // 'ho!'
}
const args = { foo: 2, bar: 'ho!' }
dosomething(args)

注意这里用的是 { foo = 1, bar = 'hey' },用的是等号 ,不是 { foo : 1, bar : 'hey' }

返回值

每个函数都返回一个值,默认情况下是 undefined

任何函数在其代码行结束时或执行流找到 return 关键字时终止执行。

当 JavaScript 遇到 return 关键字时,它退出函数执行并将控制权交还给其调用者。

如果 return 后面跟一个值,则该值将作为函数的结果返回:

const dosomething = () => {
  return 'test'
}
const result = dosomething() // result === 'test'

您只能返回一个值。

要模拟返回多个值,可以返回对象字面量或数组,并在调用函数时使用解构赋值。

使用数组:

使用对象:

嵌套函数

可以在函数中定义其他函数:

const dosomething = () => {
  const dosomethingelse = () => {}
  dosomethingelse()
  return 'test'
}

被嵌套函数的作用域是嵌套它的函数,不能从外部调用。

对象方法

当用作对象属性时,函数称为方法:

const car = {
  brand: 'Ford',
  model: 'Fiesta',
  start: function() {
    console.log(`Started`)
  }
}
car.start()

箭头函数中的“this”

当箭头函数与常规函数用作对象方法时,有一个重要的行为区别。考虑这个例子:

const car = {
  brand: 'Ford',
  model: 'Fiesta',
  start: function() {
    console.log(`Started ${this.brand} ${this.model}`)
  },
  stop: () => {
    console.log(`Stopped ${this.brand} ${this.model}`)
  }
}

stop() 方法无法正常工作。

这是因为在两个函数声明风格中处理 this 的方式是不同的。this 在箭头函数中指的是封闭的函数上下文,在这种例子中是 window 对象:

this,是指使用 function() 的宿主对象

这意味着箭头函数不适合用于对象方法和构造函数(箭头函数构造函数实际上会在调用时引发 TypeError )。

IIFE,立即调用函数表达式

IIFE 是一个在声明后立即执行的函数:

;(function dosomething() {
  console.log('executed')
})()

您可以将结果分配给变量:

const something = (function dosomething() {
  return 'something'
})()

它们非常方便,因为您无需在定义后单独调用该函数。

函数提升(Hoisting)

执行代码之前的JavaScript会根据某些规则对其进行重新排序。

特别需要记住的一点是函数会被移动到其作用域的顶部。所以下面的写法是合法的:

dosomething()
function dosomething() {
  console.log('did something')
}

在内部,JavaScript 在调用之前移动函数,以及在同一作用域内找到的所有其他函数:

function dosomething() {
  console.log('did something')
}
dosomething()

看下面的代码,如果您使用命名函数表达式,因为您正在使用 JavaScript 变量,所以会发生不同的事情。我们说的变量提升,其实是变量声明被提升,但不是值被提升,因此下面的代码中不是那个函数被提升。

dosomething()
const dosomething = function dosomething() {
  console.log('did something')
}

上面代码不会工作:

上面代码内部发生的事情是这样的:

const dosomething
dosomething()
dosomething = function dosomething() {
  console.log('did something')
}

let 声明也是如此。 var 声明也不起作用,但有不同的错误:

这是因为 var 声明被提升并初始化为 undefined 作为值,而 constlet 被提升但未初始化。


以下内容2018-11-30更新

愚人码头注:

关于 constlet 是否被 hoisting(提升)的问题争论了好几年了至今没有定论,主要原因是 hoisting(提升)和 TDZ (temporal dead zone,暂时性死区)都是非官方的解释,只是帮助大家理解 JavaScript 机制的说法。

本文最后说到的示例有两种解释:

第一种解释: constlet 声明的变量是不会提升,因为没有提升,所以报 not defined 错误,var提升了,初始值为 undefined ,但是被当做了函数运算,undefined不是函数,所以说变量不是函数。

第二种解释:JavaScript 中所有的声明 (var, let, const, function, function*, class) 都会被 hoisting(提升)。var / function / function*声明和 let / const / class 声明之间的区别是初始化。constlet 有一个TDZ,初始化被推迟,也就是变量保持未初始化。 所以访问它时会引发ReferenceError 异常。 只有在碰到 let / const / class语句时,才会初始化变量,这叫临时死区。和变量提升是两码事

参考文档:

反正就是很乱!写文档的说文档不正确,MDN的文档也是一会这个解释,一会那个解释。大家觉得哪个解释适合自己理解,就用哪个解释吧!


如果你觉得本文对你有帮助,那就请分享给更多的朋友
关注「前端干货精选」加星星,每天都能获取前端干货
赞(4) 打赏
未经允许不得转载:WEB前端开发 » 你应该知道的 JavaScript function(函数) – JavaScript 完全手册(2018版)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

前端开发相关广告投放 更专业 更精准

联系我们

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏