脚本宝典收集整理的这篇文章主要介绍了

JavaScript高级程序设计-摘要笔记-5

脚本宝典小编觉得挺不错的,现在分享给大家,也给大家做个参考,希望能帮助你少写一行代码,多一份安全和惬意。

函数表达式和闭包

1. 函数声明的一个重要特征是函数声明提升

如:

sayHi()
function sayHi () {
  console.log('hi')
}

2. 递归

递归函数是在一个函数通过名字调用自身的情况下构成的。
如:

function factorial (num) {
  if (num <= 1) {
    return 1
  } else {
    return num * factorial(num - 1) // 这里存在强耦合,不太好
  }
}

比如下面的代码会导致它出错

var anotherFactorial = factorial
factorial = null
anotherFactorial(3) // 出错:factorial is not a function
arguments.callee 是一个指向正在执行函数的指针,可以做如下改善:
function factorial (num) {
  if (num <= 1) {
    return 1
  } else {
    return num * arguments.callee(num - 1)
  }
}

严格模式下使用 arguments.callee 会出错。可以做以下改善

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

我的理解:这里的 f 只在函数内部有效,所以不会受到外界影响,外部得不到这个变量。

(function test () {
  console.log('this is test')
})
test() // 报错。

用小括号括起来的函数声明在外部是得不到的,test()只有函数内部可以用

3. 闭包

闭包指有权访问另一个函数作用域中的变量的函数。
创建闭包的常用方式,就是在一个函数内部创建另一个函数。
如:

function createComparisonFunction (propertyName) {
  return function(obj1, obj2) {
    var value1 = obj1[propertyName]
    var value2 = obj2[propertyName]
    return value1 - value2
  }
}
var obj = [{age: 13}, {age: 29}, {age: 18}, {age: 37}, {age: 5}, {age: 14}]
var compare = createComparisonFunction('age')
var obj2 = obj.sort(function (a, b) {
  compare(a, b)
})

这个例子中,内部函数访问了外部函数的变量 propertyName,而且即使被返回了在其它地方调用,仍然可以访问。
是因为内部函数的作用域链中包含了 createComparisonFunction 的作用域链。

由于闭包会携带包含它的函数的作用域,因此会比其它函数占用更多的内存。

4. 闭包与变量

闭包作用域链的配置机制有一个副作用,闭包只能取得包含函数中任何变量的最后一个值,因为闭包保存的是整个变量对象,不是某个特殊的变量。
如:

function createFunction () {
  var result = []
  for (var i = 0; i < 10; i++) {
    result[i] = function () {
      return i
    }
  }
  return result
}
// 返回数组中每个函数的执行结果都是10

改善版:

function createFunction () {
  var result = []
  for (var i = 0; i < 10; i++) {
    result[i] = (function (num) {
      return function () {
        return num
      }
    }(i))
  }
  return result
}

这样就按预期来返回值了,这里通过立即执行函数把变量 i 的当前值作为参数传递到了内部闭包函数里,由于函数的基本类型参数是按值传递,
所以每个内部闭包函数保存的值都是当前 i 值。

5. 关于闭包的this对象

var name = 'window';
(function () {
  this.name = 'fn'
  console.log(this.name) // 'fn'
  var obj = {
    name: 'obj',
    getFunc: function () {
      return function () {
        console.log(this.name)
      }
    }
  }
  obj.getFunc()() // 'fn'
}())

闭包函数的this指向它被调用时的作用域对象。
每个函数被调用时都会自动取得两个特殊的变量:this 和 arguments。在调用这两个变量时,只会搜索到其活动对象为止。
this 的改变,如:

var name = 'the Window'
var obj = {
  name: 'obj',
  getName: function () {
    console.log(this.name)
  }
}
obj.getName(); // 'obj'
(obj.getName)(); // 'obj'
(obj.getName = obj.getName)(); // 'the Window'
var obj2 = {
  name: 'obj2'
}
(obj2.getName = obj.getName)(); // 'the Window'
// 这里我的理解是相当于一个立即执行函数,这个立即执行函数是在window环境下被执行的,所以返回window下的变量值。
obj2.getName = obj.getName
obj2.getName() // 'obj2'

6. 通过构造函数创建私有变量和特权方法

如:

function Person (name) {
  this.getName = function () {
    return name
  }
  this.setName = function (value) {
    name = value
  }
}
var ming = new Person('ming')
ming.name // undefined
ming.getName() // 'ming'
ming.setName('ming2')
ming.getName() // 'ming2'

说明:getName() setName() 在构造函数外部使用,只有这两个方法有权访问私有变量name,没有其它办法,因为它们作为闭包能够通过作用域链访问name
这种方法创建私有变量和特权方法有构造函数所共有的缺点——函数没有复用。

7. 静态私有变量

(function () {
  var name = ''
  Person = function (value) {
    name = value
  }
  Person.prototype.getName = function () {
    return name
  }
  Person.prototype.setName = function (value) {
    name = value
  }
}())
var ming = new Person('ming')
ming.getName() // 'ming'
ming.setName('ming2')
ming.getName() // 'ming2'
var li = new Person('li')
li.getName() // 'li'
ming.getName() // 'li' // 说明所有实例都是对作用域链中的 name 的引用

说明:这里立即执行函数里Person使用函数表达式来定义,而且没有使用 var , 是为了形成全局变量。

或者先在全局定义一下 Person也可以。
var Person = function () {}

注意:es6 中已经是块级作用域了,所以这些东西感觉实际用途没有那么大,但是对理解闭包对作用域链中的属性的引用,这一点还是有作用的。

8. 模块模式

指为单例创建私有变量和特权方法。所谓单例,指的是只有一个实例的对象。
如:

var application = function () {
  var components = []
  components.push(new BaseComponent()) // BaseComponent 指初始化组建
  return {
    getComponentCount: function () {
      return components.length
    },
    registerComponent: function (component) {
      if (typeof component === 'object') {
        components.push(component)
      }
    }
  }
}()

增强版,适合那种单例必须是某种类型的实例:

var application = function () {
  var components = []
  components.push(new BaseComponent())
  var app = new Base()
  app.getComponentCount = function () {
    return components.length
  }
  app.registerComponent = function (component) {
    if (typeof component === 'object') {
      components.push(component)
    }
  }
  return app
}()
// 函数表达式和闭包部分结束

总结

以上是脚本宝典为你收集整理的

JavaScript高级程序设计-摘要笔记-5

全部内容,希望文章能够帮你解决

JavaScript高级程序设计-摘要笔记-5

所遇到的程序开发问题,欢迎加入QQ群277859234一起讨论学习。如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典网站推荐给程序员好友。 本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。

80%的人都看过