初探JavaScript的函数

什么是函数

函数是对象的一种,也是一段可以重复使用的代码块,开发人员为了完成某项功能,把相关代码块放到一起。

函数内部可以传参,也可以被当做参数传递

目前定义函数有五种方法

具名函数来定义

function f(x, y){
  return x + y
}
f.name //'f'

匿名函数来定义

var f
f = function(x, y){
  return x + y
}
f.name //'f'

具名函数定义了又赋值给了变量

var f1
f1 = function f(a, b){
  return a + b
}
f1.name //'f'

要注意:虽然f1.name='f',但是f只在函数内部可用,实际上函数的名字还是f1

window.Function来构造

var f2 = new Function('x', 'y', 'return x + y')
f2.name //'anonymous'

箭头函数

var f3 = (x, y) => {return x - y}
var sum = (x, y) => x + y //函数体内只有一行代码,可以省略大括号和return
var n2 = n => n*n //只有一个参数,可以省略小括号

常用的定义方法是1、2、5这三种方法。

函数的一些必备知识

函数的name属性

由上面的五种定义方法,我们可以知道函数具有name属性,而且不同的定义方法,name属性也很奇葩。

函数如何调用

为了理解后面的this,推荐使用call()方法,而不是使用常见的f()

以第一种定义方法为例

f.call(undefined, 1, 3)
4

call()方法的第一个参数就是this,后面的参数才是函数的执行参数。

下面用代码检验一下

function f1(m, n){
    console.log(this)
    console.log(m + n)
}
undefined
f1.call(undefined, 1, 3)
Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …} //不是应该打印undefined,为啥是window呢?
4 //这才是函数的执行内容

执行f1.call(undefined, 1, 3)后,this不是应该打印出undefined吗,为啥打印了Window呢(注意实际上是个小写的window,不是浏览器打印的大写的Window),可以用代码验证打印的就是小写的window

function f1(m, n){
    console.log(this === window)
    console.log(m + n)
}
undefined
f1.call(undefined, 1, 3)
true //说明是小写得到window
4
function f1(m, n){
    console.log(this === Window)
    console.log(m + n)
}
undefined
f1.call(undefined, 1, 3)
false //并不是大写的Window
4

我真是服啦,那window和Window有啥区别呢。真是蛋疼啊,竟然考虑这个问题……

答案就是 var object = new Object,那var window = new Window。而且Window毫无探讨的意义,倒是这个window是个全局属性,多少有点用。


有时候自己真是有点钻牛角尖,钻进去后,还不会举一反三。如果立刻想到obj的例子就不用浪费时间了。


这就是藏着的this

这是因为浏览器捣的鬼,他把undefined变成了window。接下来使用严格模式,让undefined现身

function f1(m, n){
    'use strict'
    console.log(this)
    console.log(m + n)
}
undefined
f1.call(undefined, 1, 3)
undefined //这个undefined就是call()方法的第一个参数undefined
4
  • 而且call()的第一个参数是啥,this就是啥
function f1(m, n){
    'use strict'
    console.log(this)
    console.log(m + n)
}
undefine
f1.call('我是啥this就是啥', 1, 3)
我是啥this就是啥 //打印的依然是call()的第一个参数
4

arguments

前面分析了call()的第一个参数,那后俩参数是啥呢。

对,你没猜错,那就是arguments。

当你写call(undefined, 1, 3)的时候。undefined可以被认为是this[1, 3]就是arguments

函数的call stack

上面我们接触了call()方法,现在我们学习一下当有多个函数调用的时候,JavaScript解析器是如何调用栈的。

MDN的解释如下

调用栈是解析器(如浏览器中的的javascript解析器)的一种机制,可以在脚本调用多个函数时,跟踪每个函数在完成执行时应该返回控制的点。(如什么函数正在执行,什么函数被这个函数调用,下一个调用的函数是谁)

  • 当脚本要调用一个函数时,解析器把该函数添加到栈中并且执行这个函数。
  • 任何被这个函数调用的函数会进一步添加到调用栈中,并且运行到它们被上个程序调用的位置。
  • 当函数运行结束后,解释器将它从堆栈中取出,并在主代码列表中继续执行代码。
  • 如果栈占用的空间比分配给它的空间还大,那么则会导致“堆栈溢出”错误。

以下是通过三个方面去理解call stack这个概念的。

普通调用

代码如下,直观的动图可以看上述的链接

function a(){
    console.log('a')
  return 'a'  
}

function b(){
    console.log('b')
    return 'b'
}

function c(){
    console.log('c')
    return 'c'
}

a.call()
b.call()
c.call()

如上的代码,先有三个函数声明,然后是三个调用。浏览器先执行a.call(),然后执行b.call(),c.call(),下面结合图具体详细分析。

[图片上传失败...(image-57cf0d-1515141332134)]

  • 第一步:浏览器入口是a.call(),a函数入栈,执行a函数内部代码
  • 第二步:console.log('a')执行完毕,就出栈,接着a函数结束,出栈死亡
  • 第三步:b.call()入栈,执行b函数内部代码
  • 第四步: console.log('b')执行完毕就出栈,接着b函数结束,出栈死亡
  • 第五步:c.call()入栈,执行c函数内部代码
  • 第六步:console.log('c')执行完毕就出栈,接着c函数结束,出栈死亡。
  • 整个代码结束,浏览器恢复平静。

嵌套调用

function a(){
    console.log('a1')
    b.call()
    console.log('a2')
  return 'a'  
}
function b(){
    console.log('b1')
    c.call()
    console.log('b2')
    return 'b'
}
function c(){
    console.log('c')
    return 'c'
}
a.call()
console.log('end')
嵌套调用
  • 第一步:浏览器的入口还是a.call(),a.call()入栈,执行a函数内部的代码
  • 第二步: a函数的第一行语句console.log('a1'),入栈,打印出a1,这句话就出栈死亡。此时a函数继续执行下面的代码。
  • 第三步: a函数的第二行语句b.call()入栈。执行b函数内部的代码。
    • 第四步:进入b函数内部,b函数的第一行语句console.log('b1')入栈,打印出b1,就出栈死亡。
    • 第五步:b函数的第二行c.call()入栈,又进入c函数内部
      • 第六步:进入c函数的内部,第一行语句console.log('c')入栈,打印出c,就出栈死亡。
      • 第七步:c函数执行完毕,出栈死亡。
    • 第八步:回到b函数内部,执行第三行代码console.log('b2')入栈,打印出b2,出栈死亡。
    • 第九步: b函数执行完毕,出栈死亡。
  • 第十步: 回到a函数内部,执行第三行代码console.log('a2'),入栈,打印出a2,就出栈死亡。
  • 第十一步:a函数执行完毕,出栈死亡。
  • 第十二步:console.log('end')入栈,打印出end,出栈死亡。
  • 整个代码运行完,浏览器归于平静。

递归调用

递归调用就是上面的嵌套调用的复杂变化,细心点,分析就能明白具体的代码顺序。

函数作用域

除了全局变量,其他变量只能在自己的函数内部被访问到,其他区域无法访问。通过几个面试题来学习一下。

  • 第一道面试题
var a = 1
function f1(){
    alert(a) // 是多少
    var a = 2
}
f1.call()

问:alert出什么东西?

这种题切忌上去就做,容易打错成了 a是2 一定要先把变量提升。变成如下这样的

var a = 1
function f1(){
    var a 
    alert(a) 
    a = 2
}
f1.call()

这样一提升就知道啦,答案:a是undefined

  • 第二道面试题
var a = 1
function f1(){
    var a = 2
    f2.call()
}
function f2(){
    console.log(a) // 是多少
}
f1.call()

问:a是多少

这个题用就近原则好做。

树形结构

用树形结构来分析,当上面的代码被浏览器渲染之后

  • 全局变量里面有:var a = 1,f1、f2函数
  • f1函数作用域里面又重新声明了一个var a = 2
  • f2函数作用域里面是console.log(a)

所以打印的那个a就是全局的a,答案是a=1

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,761评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,953评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,998评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,248评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,130评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,145评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,550评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,236评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,510评论 1 291
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,601评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,376评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,247评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,613评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,911评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,191评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,532评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,739评论 2 335

推荐阅读更多精彩内容

  • 函数和对象 1、函数 1.1 函数概述 函数对于任何一门语言来说都是核心的概念。通过函数可以封装任意多条语句,而且...
    道无虚阅读 4,434评论 0 5
  • Lua 5.1 参考手册 by Roberto Ierusalimschy, Luiz Henrique de F...
    苏黎九歌阅读 13,674评论 0 38
  •   引用类型的值(对象)是引用类型的一个实例。   在 ECMAscript 中,引用类型是一种数据结构,用于将数...
    霜天晓阅读 1,030评论 0 1
  • 工厂模式类似于现实生活中的工厂可以产生大量相似的商品,去做同样的事情,实现同样的效果;这时候需要使用工厂模式。简单...
    舟渔行舟阅读 7,703评论 2 17
  • 创建物体的三种方法,这三种方法实际上都是属于GameObject类的方法 通过代码给游戏物体添加组件如:添加一个刚...
    夜行水寒阅读 1,656评论 0 0