【JavaScript高级程序设计】读书摘录4-第七章 匿名函数
2015-04-10 23:52
507 查看
第七章、匿名函数
本章中比较重要的几个概念:闭包、匿名函数,作用域链,this对象等。在js中碰到的很多坑都来自本章。另外,在很多类库中如jQuery中都会大量使用闭包和匿名函数,它们都是非常有用的特性。1、函数定义和函数表达式的区别:对于函数定义,在代码执行以前会被加载到内存中,而对于函数表达式:只有在代码执行到那一行的时候才有定义:
function func(){ } var func = function(){ }
2、可以用arguments.callee实现递归,比直接使用函数名更加保险。即使你的函数更改为另外的名字,使用arguments.callee可以保证递归正确执行。
3、闭包和匿名函数的区别:
闭包是有权访问另一个函数作用域中的变量的函数。创建闭包的最常见方式,就是在一个函数内部创建另外一个函数。理解如何创建作用域链,以及作用域链的细节,对于理解闭包非常重要。作用域链本质是一个指向变量对象的指针列表,它只引用而不实际包含对象。
作用域链:当函数第一次被调用时,会创建一个执行环境以及相应的作用域链,并把作用域链复制给一个特殊的内部属性[[Scope]],然后,利用this,arguments和其他命名参数的值来初始化函数的活动对象。
后台的每个一执行环境都有一个表示变量的对象-变量对象。全局环境的变量对象始终存在。而对于函数的局部环境的变量对象,只在函数执行的过程中存在。
在另一个函数中定义的函数会将包含函数(外部函数)的活动对象添加到它的作用域链中。
function createCompare( proper ){ returnfunction( obj1, obj2) { varval1 = obj1[proper]; varval2 = obj2[proper]; returnval1 - val2; } } var obj = [ {id: 10, age : 20}, {id: 12, age : 16}, {id: 15, age : 22}, ]; var compare = createCompare('age'); obj.sort(compare); console.log(obj);
在匿名函数从createCompare函数返回之后,它的作用域链被初始化为包含createCompare函数的活动对象和全局变量对象。这样匿名函数就可以访问在createCompare函数中定义的所有变量。更为重要的是,createCompare函数在执行完毕之后,它的活动对象也不会销毁,因为匿名函数的作用域链依然在引用这个活动对象。换句话说:在createCompare函数返回后,其执行环境的作用域链会被销毁,但是它的活动对象依然会保留在内存中,直到匿名函数被销毁后,createCompare的活动对象才会被销毁。
compare = null;
由于闭包会携带包含它的函数的作用域,因此会比其他的函数占用更多的内存。
闭包与变量:
由于闭包的特性,导致闭包只能取得包含函数中任意变量的最后一个值。闭包中保存的是整个变量对象,而不是某个特殊的变量:
function create(){ var result = []; for( var i = 0; i < 10; i++ ){ result[i] = function(){ return i; }; } return result; }
由于闭包引用的外部函数的变量i,因此每个函数都保存着同一个变量对象,所以在每个函数内部i的值都是10.可以通过创建另一个匿名函数强制让闭包的行为符合预期:
function create(){ var result = []; for( var i = 0; i < 10; i++ ){ result[i] = function(num){ return function(){ return num; } }(i); } return result; }
关于this对象:
this对象是在运行时给予函数的执行环境绑定的,在全局函数中,this等于window,而当函数作为某个对象的方法调用时,this等于那个对象。不过匿名函数的执行具有全局性,因此其this对象通常指向window.在通过call()或者apply()改变函数执行环境的情况下,this指向其他对象。
var name = "the window"; var obj = { name : "the obj", getFunc: function(){ return function(){ return this.name; } } } console.log( obj.getFunc()() );
可以把外部作用域的this变量包含在一个闭包能够访问的变量里,就可以让闭包访问该对象了:
var name = "the window"; var obj = { name : "the obj", getFunc: function(){ var that = this; return function(){ return that.name; } } } console.log( obj.getFunc()() );
this和arguments也存在同样的问题。如果想要访问作用域中的arguments对象,必须将对该对象引用保存到另一个闭包能够访问的变量中。
闭包与内存泄露:
由于IE对于JScript对象和COM对象使用不同的垃圾收集机制(IE对Javascript采用标记清除的垃圾收集机制,而DOM和BOM对象并非原生的Javascript对象,是通过引用计数机制来进行垃圾收集的),因此闭包在IE中会导致一些特殊的问题。如果闭包的作用域链中包含了一个HTML元素,那么就意味着该元素无法被销毁:
function setHandler(){ var elem = document.getElementById("someElement"); elem.onclick= function(){ console.log(elem.id ); } }
以上代码创建了一个elem元素的时间处理程序的闭包,而这个闭包又创建了一个循环引用。由于匿名函数保存了一个对assignHandler的活动对象引用,因此在assignHandler执行完毕退出时,并不会销毁assignHandler的活动对象。因此只要有匿名函数存在,elem的引用数至少也是1. 解决方法:
functionsetHandler(){ var elem =document.getElementById("someElement"); var id = elem.id; elem.onclick = function(){ console.log( id ); } elem = null; }
块级作用域:javascript中没有块级作用域的概念,要模拟块级作用域,可以用下面的语法:
(function(){ })();
也就是定义一个匿名函数并立即调用它。
这个技术最常在全局作用域中被用于函数外部,从而限制向全局作用域中添加过多的变量和函数。这种方法可以减少闭包占用的内存问题,因为没有指向匿名函数的引用。
私有变量:
function MyObject(){ var privateVar = 10; function privateFunc(){ console.log(privateVar ); } this.publicFunc= function(){ privateVar++; privateFunc(); } }
相关文章推荐
- 【javascript高级程序设计】读书摘录3 第六章、面向对象
- Javascript高级程序设计第二版第七章匿名函数--笔记
- Javascript高级程序设计第二版第七章匿名函数--笔记
- 【javascript高级程序设计】读书摘录1 JavaScript简介
- Javascript高级程序设计第二版第七章匿名函数--笔记
- 【javascript高级程序设计】读书摘录2 第五章、引用类型
- javascript高级程序设计(第3版) 读书笔记1 基本概念
- Javascript高级程序设计——15-1.匿名函数和闭包
- javascript高级程序设计笔记-第七章(函数表达式)
- Javascript高级程序设计 第七章 --- 函数表达式
- 第七章 正则表达式 摘自《JavaScript 高级程序设计》
- JavaScript高级程序设计--读书心得
- JavaScript高级程序设计(第3版)第七章读书笔记
- Javascript高级程序设计第七章知识点总结
- JavaScript高级程序设计:第七章
- JavaScript高级程序设计 读书笔记 第二章
- 阅读摘录《javascript 高级程序设计》01
- javascript高级程序设计 读书笔记1
- Javascript高级程序设计第二版第十一章--DOM2,DOM3--笔记
- JavaScript高级程序设计学习笔记3: Math对象比较常用的几个方法