摘要:函数表达式和闭包函数声明的一个重要特征是函数声明提升如递归递归函数是在一个函数通过名字调用自身的情况下构成的。注意中已经是块级作用域了,所以这些东西感觉实际用途没有那么大,但是对理解闭包对作用域链中的属性的引用,这一点还是有作用的。
函数表达式和闭包 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 值。
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
这种方法创建私有变量和特权方法有构造函数所共有的缺点——函数没有复用。
(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 }()
// 函数表达式和闭包部分结束
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/86521.html
摘要:说明此摘要笔记系列是我最近看高级程序设计第版随手所记。摘要笔记本身没有系统性,没有全面性可言,写在这里供有一定基础的前端开发者参考交流。对每一项运行给定函数,返回该函数会返回的项组成的数组。是的反操作是的反操作第一部分结束。 说明: 此摘要笔记系列是我最近看《JavaScript高级程序设计(第3版)》随手所记。 里面分条列举了一些我认为重要的、需要记下的、对我有帮助的点,是按照我看...
摘要:如果重设构造函数的原型对象,那么,会切断新的原型对象和任何之前已经存在的构造函数实例之间的联系,它们引用的仍然是最初的原型。说明返回的对象与构造函数或者与构造函数的原型属性没有关系。 说明: 此摘要笔记系列是我最近看《JavaScript高级程序设计(第3版)》随手所记。里面分条列举了一些我认为重要的、需要记下的、对我有帮助的点,是按照我看的顺序来的。摘要笔记本身没有系统性,没有全面性...
摘要:说明此摘要笔记系列是我最近看高级程序设计第版随手所记。其中,描述符对象的属性必须是设置其中一个或多个值,可以修改对应的特性值。如支持的浏览器,可以取得指定属性的描述符。 说明: 此摘要笔记系列是我最近看《JavaScript高级程序设计(第3版)》随手所记。里面分条列举了一些我认为重要的、需要记下的、对我有帮助的点,是按照我看的顺序来的。摘要笔记本身没有系统性,没有全面性可言,写在这里...
摘要:关于对象定义了全局对象。支持的浏览器有除了接受要序列化的对象外,还可以接受另外两个参数。如果是数值,则表示每个级别缩进的空格数,最大,超过的值自动转换成。字符串长度超过,结果中将只出现前个字符。会在结果字符串中插入换行符提高可读性。 关于JSON 1. JSON 对象 es5 定义了全局对象 JSON。支持的浏览器有 IE8+ 、Firefox 3.5+ 、Safari 4+、Chro...
摘要:思路是,使用原型链对原型属性和方法进行继承,借用构造函数实现对实例属性的继承。注意使用寄生式继承来为对象添加函数,会由于不能做到函数复用而降低效率,这一点与构造函数模式类似。无论什么情况下都会调用两次超类型的构造函数。 说明: 此摘要笔记系列是我最近看《JavaScript高级程序设计(第3版)》随手所记。里面分条列举了一些我认为重要的、需要记下的、对我有帮助的点,是按照我看的顺序来的...
阅读 3686·2021-08-10 09:42
阅读 586·2019-08-30 15:55
阅读 882·2019-08-30 15:54
阅读 3105·2019-08-30 13:45
阅读 552·2019-08-29 16:23
阅读 1988·2019-08-29 16:23
阅读 979·2019-08-29 15:18
阅读 2258·2019-08-29 12:57