一、立即执行函数 --- IIFE
立即执行函数的集中表现形式:
立即执行函数的特点:
二、JS 全局污染
为什么会造成全局污染?
JS 没有块级作用域,在函数外定义的变量,均为全局变量;
全局变量过多会削弱程序的灵活性,增大了模块之间的耦合度,多人协作开发会导致变量冲突,造成环境污染。
- 耦合度:即模块之间的依赖关系:控制关系、调用关系、数据传递关系;
- 划分模块准则:高内聚低耦合
如何解决全局污染?
1. 命名空间
2. 立即执行函数(里面创建的变量,为局部变量)
(function(){})()(function(){}())!function(){}()var fn = function(){} ()
三、闭包
1. 闭包的概念
- 广义闭包:函数内部声明变量,函数外部无法访问到,模拟块级作用域,即生成闭包
- JS 中所有函数,都能形成一个闭包
狭义闭包:闭包模型(一个函数,作为另一个函数的返回值)
- 闭包实际上由两部分组成:
- 函数体:函数的代码
- 函数所处的环境:作用域链
综上:把函数体 及 函数所处的环境,成为闭包
2. 主线:函数 不管在哪个环境下调用,都要回到创建函数的环境下执行
3. 闭包作用:
解决全局污染
对函数内的变量,起保护作用;除了返回函数外,没有任何办法可以访问到函数内的变量
4. 经典闭包模型
可简单理解为,一个函数作为另一个函数的返回值;这样就有了内层函数,和外层函数;
外层函数中:定义变量(对变量起保护作用)
返回值返回的 内层函数:定义操作变量的方法
闭包模型如下:
<script> ???var fn = function() { ???????var num = 10; ???????????????return { ???????????f1: function() {} ???????????f2: function() {} ???????} ???}; ???????var temp = fn(); ???????// 调用 f1 、f2 方法 ???temp.f1(); ???temp.f2();</script>
4. 闭包弊端(内存泄露)---> 解决:内层函数 = null
闭包中外层函数定义的变量一直存在,不会被自动释放掉;
因为内层函数一直引用着 外层函数中定义的变量;并且内层函数每一次操作变量,都是在外层函数中变量基础上进行操作的
手动释放内存占用:
内层函数 = null
四、沙箱(闭包的一种体现形式)
1. 沙箱模式
函数自调用
给window注册属性 或 return 变量
经典沙箱模式如下:
<script>(function(window) { ???// 定义变量,及一些列js逻辑 ???var main = ‘‘; ???window.main = main; ?或 ?return main;})(window)</script>
- jQuery中运用沙箱模式,如下:
(function() { ???????window.jQuery = window.$ = jQuery; ???return jQuery;})();
2. 沙箱的作用:
变量隔离,保护变量(沙箱内定义的变量,沙箱外不能访问到)
避免全局污染
3. IIFE 立即执行函数
(function(){})()(function(){}())!function(){}()var fn = function(){} ()
五、函数 递归
1. 简单理解递归:函数自己 ---> 调用自己
2. 何时用递归:函数需要多次调用自己,自己嵌套自己
3. 如何书写 递归函数(3点)?
- 划归思想:找出规律,总结前者 、 后者之间的关系
- 临界条件:找出临界值,停止递归
- retrun 自己函数的调用
4. 递归相关算法实现:
- 递归实现兔子数列(菲波那切数列)
<script> ???function fn3(m) { ???????if (m === 1 || m === 2) { ???????????return 1; ???????} ???????return fn3(m-1) + fn3(m-2); ???} ???console.log(fn3(20));</script>
- 菲波那切数列 优化【缓存】
<script> ???var cache = {}; ???function fn4(m) { ???????if (cache[m]) { ???????????return cache[m]; ???????} ???????if (m === 1 || m === 2) { ???????????cache[m] = 1; ???????????return 1; ???????} else { ???????????return cache[m] = fn4(m-1) + fn4(m-2); ???????} ???} ???console.log(fn4(200));</script>
- 递归:阶乘
<script> ???function fn2(m) { ???????if (m <= 1) { ???????????return 1; ???????} ???????return fn2(m-1) * m; ???} ???console.log(fn2(3));</script>
- 递归:次方
<script> ????function fn1(m, n) { ???????if (n === 0) { ???????????return 1; ???????} ???????????????return fn1(m, n -1) * m; ???} ???????console.log(fn1(2,4));</script>
5. 严格模式下,递归 ---> 健壮代码
- 非严格模式下,简易递归
<script> ???var fn = function (m) { ???????if (m <= 1) { ???????????return 1; ???????} ???????return fn(m-1) * m; ???} ???var fn1 = fn; ???fn = null; ???console.log(fn1(3)); ?// 报错</script>
非严格模式下,健壮递归
arguments.callee
指向一个正在执行的函数的指针
<script> ???var fn = function (m) { ???????if (m <= 1) { ???????????return 1; ???????} ???????return arguments.callee(m-1) * m; ???} ???var fn1 = fn; ???fn = null; ???console.log(fn1(3)); ?// 6</script>
- 严格模式下,不允许用
arguments.callee
, 健壮代码如下:
<script> ???var fn1 = function fn(m) { ???????if (m <= 1) { ???????????return 1; ???????} ???????return fn(m-1) * m; ???} ???var fn2 = fn1; ???fn1 = null; ???console.log(fn2(3));</script>