第七章 函数表达式

第七章 函数表达式

问答

1. 函数声明的语法是?

function test() {
  // ...
}

2. 函数表达式(匿名函数)的语法是?

var test = function() {
  // ...
};

3. 以下递归语句是否报错, 报错原因是? 如何改进?

function factorial (num) {
  if (num <= 1) {
    return 1
  } else {
    return num * factorial(num - 1)
  }
}

var anotherFactorial = factorial
factorial = null
anotherFactorial(4)

错误: Uncaught TypeError: factorial is not a function

原因: 函数factorial内部的factorial指向null, 因此报错.

方法1: arguments.callee, 它指向当前正在执行的函数指针. 但是严格模式不允许使用.

function factorial (num) {
  if (num <= 1) {
    return 1
  } else {
    return num * arguments.callee(num - 1)
  }

方法2: 函数表达式

var factorial = function f (num) {
  if (num <= 1) {
    return 1
  } else {
    return num * f(num - 1)
  }
}

4. 闭包的概念?

有权访问另一个函数作用域中的变量的函数(或其他). 只要不被垃圾回收机制回收即可, 不一定需要返回函数, 方式有:

  • 在一个函数中返回另一个函数, 返回的函数中包含上层函数的活动对象和全局对象(层层向外, 不止两层)
  • 在函数中将内部状态保存在外部变量中.
var test = (function () {
  var test = 123
  function fn () {
    return test
  }
  TEST = {
    test: test,
    fn: fn
  }
})()
console.log(TEST.fn()) // 123 , 注意: window.TEST === TEST
console.log(TEST) // {test:123, fn:f}

5. 在闭包中, 函数被调用的时候都发生了什么?

function createComparisonFunction (propertyName) {
  return function (object1, object2) {
    var value1 = object1[propertyName]
    var value2 = object2[propertyName]
    if (value1 < value2) {
      return -1
    } else if (value1 > value2) {
      return 1
    } else {
      return 0
    }
  }
}

// 创建函数
var compareNames = createComparisonFunction('name')
// 调用函数
var result = compareNames({ name: 'Nicholas' }, { name: 'Greg' })
// 解除对匿名函数的引用,释放内存
compareNames = null
创建createComparisonFunction函数
  1. 创建createComparisonFunction函数时, 会创建一个预先包含全局变量对象的作用域链, 这个作用域链被保存在当前函数内部的[[Scopes]]属性中.
执行createComparisonFunction函数
  1. 执行createComparisonFunction函数时, 会创建一个执行环境(execution context), 通过复制函数的[[Scopes]]属性中的对象构建起执行环境的作用域链.

  2. 使用arguments和其他 形参/变量/函数声明 初始化函数的 活动对象(activation object), 即另一种局部变量对象, 并被推入执行环境作用域链的顶端. 这里需要补充变量提升及优先级的说明.

  3. 此时, createComparisonFunction函数的执行环境中的作用域链包含多个变量对象:

    • 0: 本地活动对象
    • 1: 引用的外层函数变量对象
    • n-1: ...
    • n: 全局变量对象.
  4. 显然, 作用域链的本质是一个指向变量对象的指针列表, 它只包含引用但不实际包含变量对象.

  5. 当在函数中访问一个变量时, 就会从作用域链中搜索具有相应名字的变量(变量标识符匹配过程). 这里可以补充优化机制.

  6. createComparisonFunction函数执行完毕时, 其执行环境的作用域链会被销毁.

创建闭包
  1. 当匿名函数从createComparisonFunction函数中返回后, 它的作用域链被初始化为包含createComparisonFunction函数的活动对象和全局变量对象.
执行闭包
  1. 因此, 即使createComparisonFunction函数执行完毕, 但是它的活动对象仍然会留在内存中, 直到匿名函数主动销毁, createComparisonFunction()的活动对象才会被销毁. 这里需要补充JS垃圾回收及标记清除机制.

注意:

  1. 闭包保存的是整个变量对象, 而不是某个特殊的变量.
  2. 只要没有形成闭包, 当函数执行完毕, 就可以立即销毁其作用域链和活动对象(变量对象).
  3. 如果形成闭包, 作用域链也会被销毁, 但是活动对象及变量对象不会销毁.
  4. 作用域链决定哪些数据能被函数访问

6. 下面函数返回的是什么, 为什么?

var name = "The Window";
var object = {
  name: "My Object",
  getNameFunc: function() {
    return function() {
    // this只有执行时在确定指向谁, 当前this指定window
    // 活动对象中只有window
      return this.name;    
     };
  }
};
console.log(object.getNameFunc()()); // The Window

匿名函数的执行环境具有全局性, 因此其this对象通常指向window(这里可以使用call/apply主动改变this).

this的概念: 在函数运行时,基于调用位置的条件自动生成的内部对象,可以理解为动态绑定对象到this上。

每个函数在被调用时都会自动取得两个特殊的变量: thisarguments. 内部函数在搜索这两个变量时, 只会搜索到其活动变量为止, 因此最后一个函数执行时, 外层function的活动对象只有window, this为window, 即, this === window.

因此, 函数执行时才能确定this, 且this指向调用函数最近的一个对象.

7. IIFE能模仿块级作用域的原因?

JavaScript是函数级作用域, 在立即执行函数内定义的变量是局部变量, 一般情况下, 使用完毕后会自动销毁.

8. 关于作用域链优化部分的理解?

  • 缓存深层作用域链上的变量

变量使用前都会在作用域链中查找, 因此将常用的跨作用域变量缓存为局部变量能减少变量查找时间

  • 避免使用with

with能动态改变作用域链, 将指定的对象压入作用域链顶端, 造成原来局部变量对象转为第二位, 影响查找效率.

  • catch内部使用简化代码

当try代码中发生错误时, 执行过程会自动跳转到catch中, 然后把异常对象推入一个变量对象并置于作用域的首位, 建议catch中不要放复杂的语句

  • 尽量不使用eval()

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

推荐阅读更多精彩内容

  • 1.定义函数的方式: ①函数声明(其重要特征是函数声明提升,可以把函数声明放在调用它的语句后面): functio...
    张果果阅读 211评论 0 0
  • 定义函数的方式有两种: 函数声明重要特征就是函数声明提升,意思是在执行代码之前会先读取函数声明。这就意味着可以把函...
    dudu_du阅读 221评论 0 0
  • 还记得前年看了《黄飞鸿之英雄有梦》后,久久地迷恋于五月天的那首《将军令》,简直好听的无法自拔。每听一遍都能想到ja...
    召小南阅读 883评论 0 6
  • 我坐在回家的车上,经过了八天的军训洗礼与几天的混吃等死,我经历我梦寐以求的大学,却没有找到我最初的感觉,就像是荒废...
    旧城咯阅读 146评论 0 0
  • 伤了翅膀的鸟儿 音符写梦 2014-7-18 9:53 湛蓝的天空中 偶尔会有几朵白云飘过 三三两两的鸟儿们 在叽...
    如伊老师阅读 220评论 0 1