美文网首页
第七章 函数表达式

第七章 函数表达式

作者: 烈风裘 | 来源:发表于2018-01-20 14:57 被阅读29次

第七章 函数表达式

问答

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引擎无法对动态作用域进行优化, 只能按照作用域链查找的传统模式进行执行.

相关文章

  • js - 函数表达式

    第七章 函数表达式 1. 创建函数两种方法 函数表达式特征 1,函数声明: 特征: 函数声明提升,意思执行代码前会...

  • 第七章 函数表达式

    第七章 函数表达式 问答 1. 函数声明的语法是? 2. 函数表达式(匿名函数)的语法是? 3. 以下递归语句是否...

  • javascript高级程序设计(第7章) -- 函数表达式

    第七章:函数表达式 本章内容: 函数表达式的特征 使用函数实现递归 使用闭包定义私有变量 定义函数的方式有两种,一...

  • 2020-01-09:第七章:函数表达式(闭包)

    第七章:函数表达式 函数表达式是js中一个非常强大但又让人困惑的特性,我们前面提到过,函数声明有两种方法:函数声明...

  • 第七章 Caché 函数大全 $CHAR 函数

    第七章 Caché 函数大全 $CHAR 函数 将表达式的整数值转换为相应的ASCII或Unicode字符。 大纲...

  • 《JavaScript高级程序设计》之笔记五

    第七章 函数表达式 1. 定义函数的两种方法 : 2. 递归 : 递归函数是在一个函数通过名字调用自身的情况下构成...

  • 07 | 读JavaScript 高程

    这是第七章函数表达式,这一章涉及函数预编译,闭包,作用域链内容。函数涉及内容繁多。 前情提要 02-1 | 读Ja...

  • 07-1 | 读JavaScript 高程

    这是第七章函数表达式,这一章涉及函数预编译,闭包,作用域链内容。函数涉及内容繁多。今天来看作用域链。 在 06-2...

  • 函数与作用域

    函数声明和函数表达式有什么区别 函数声明 函数表达式 区别 函数表达式结束后需要加;表示声明变量结束。 函数表达式...

  • 函数

    函数声明和函数表达式有什么区别 函数声明 函数表达式 函数声明和函数表达式的区别: 函数声明必须给定函数名称,函数...

网友评论

      本文标题:第七章 函数表达式

      本文链接:https://www.haomeiwen.com/subject/vkdtaxtx.html