一、立即执行函数 — 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>