使用递归获取后代元素
1.自己调用自己
2.要有结束的条件
2.要有结束的条件
//斐波那契 数列 //1 1 2 3 5 8 13 21 //1 1 //2 1 //3 第1项 + 第2项 //4 第2项 + 第3项 //5 第3项 + 第4项 //n (n-2)+(n-1) function fibonacci(n){ if(n <= 2){ return 1; } return fibonacci(n-1) + fibonacci(n-2); } console.log(fibonacci(10));
什么是作用域
变量起作用的范围
### 什么是块级作用域
JS中没有块级作用域,使用代码块限定的作用域就是块级作用域
### JS中的作用域叫做 词法作用域
### 词法作用域
在代码写好的时候,就能确定变量的作用域,这种作用域就是词法作用域
在代码写好的时候,就能确定变量的作用域,这种作用域就是词法作用域
动态作用域(和词法作用域相对).(是词法作用域就不可能是动态作用域)
在js当中,只有函数能创造作用域
var num = 123; function f1(){ console.log(num); //如果是动态作用域打印的就是456 如果是词法作用域 打印123 } function f2(){ var num = 456; f1(); } f2();
### 变量提升
JS代码的运行分两个阶段
* 预解析阶段
* 变量名和函数提升
将var声明的变量名和function开头的函数进行提升
提升到当前作用域的最上方
* 执行阶段
* 预解析阶段
* 变量名和函数提升
将var声明的变量名和function开头的函数进行提升
提升到当前作用域的最上方
* 执行阶段
注意:
1.变量和函数同名的时候
只提升函数,忽略变量名
2.函数同名的时候
都提升,但是后面的函数会覆盖前面的函数
3.函数表达式,只会提升变量名,不会提后面的函数
1.变量和函数同名的时候
只提升函数,忽略变量名
2.函数同名的时候
都提升,但是后面的函数会覆盖前面的函数
3.函数表达式,只会提升变量名,不会提后面的函数
func(); var func = function(){ alert("你猜我会不会被调用"); } //提升后的代码 var func; func(); func = function(){ alert("你猜我会不会被调用"); };
4.变量提升只会将变量和函数提升到当前作用域的最上方
```js
funciton foo(){
var num =123;
}
```
5.变量提升是分块 <script> 的
```js
funciton foo(){
var num =123;
}
```
5.变量提升是分块 <script> 的
<script> foo() function foo(){ console.log("第一个script标签内的函数") }; </script> <script> foo() function foo(){ console.log("第2个script标签内的函数") } </script>
6.条件式函数声明 能否被提升,取决于浏览器, 不推荐使用!!!
```
foo();//会报错,因为未被提升
if(true){
function foo(){
```
foo();//会报错,因为未被提升
if(true){
function foo(){
}
}
}
```
## 作用域链
只要是函数都有作用域,函数内部的作用域可以访问函数外部的作用域
当多个函数嵌套的时候,就会形成一个链式的结构,这个就是作用域链
## 绘制作用域链图的步骤
1.先绘制0级作用域链
2.在全局作用域中查找,变量和函数的声明,找到之后,将所有的变量和函数用小方格放在0级作用域链上
3.再从0级作用域链上的函数引出1级作用域链
4.再去每一个1级作用域链中查找变量和函数的声明,找到之后.....
5.以此重复,就画好了整个作用域链
1.先绘制0级作用域链
2.在全局作用域中查找,变量和函数的声明,找到之后,将所有的变量和函数用小方格放在0级作用域链上
3.再从0级作用域链上的函数引出1级作用域链
4.再去每一个1级作用域链中查找变量和函数的声明,找到之后.....
5.以此重复,就画好了整个作用域链
//f1--->全局 function f1(){ //f2--->f1--->全局 function f2(){ //f3---->f2--->f1--->全局 function f3(){ } //f4--->f2--->f1---->全局 function f4(){ } } //f5--->f1---->全局 function f5(){ } }
注意:
1 设置值的时候,也是访问变量
2. 获取值的时候,是访问变量
3.并不是在函数内部写了变量,这个变量就属于这个函数的作用域,而是必须使用var来声明变量,这个变量才会属于这个作用域
## 变量的搜索规则
1.首先在访问变量的作用域中查找该变量,如果找到直接使用
2.如果没有找到,去上一级作用域中继续查找,如果如果找到直接使用
3.如果没有找到,继续去上一级作用域中继续查找,直到全局作用域
4.如果找到了就用,如果没有直到就报错
1.首先在访问变量的作用域中查找该变量,如果找到直接使用
2.如果没有找到,去上一级作用域中继续查找,如果如果找到直接使用
3.如果没有找到,继续去上一级作用域中继续查找,直到全局作用域
4.如果找到了就用,如果没有直到就报错
## 闭包
### 闭包是什么
一个封闭的对外不公开的包裹结构或空间
### js中的闭包是函数
### 闭包是什么
一个封闭的对外不公开的包裹结构或空间
### js中的闭包是函数
### 闭包要解决的问题
1、在函数外部访问不到函数内部的数据
2、要解决的问题就是需要在外部间接的访问函数内部的数据
1、在函数外部访问不到函数内部的数据
2、要解决的问题就是需要在外部间接的访问函数内部的数据
###闭包的原理就是作用域访问原则
上级作用域无法直接访问下级作用域中的变量
上级作用域无法直接访问下级作用域中的变量
### 闭包的基本结构
闭包基本模式
//在外部函数(foo)内创建函数(inner),在这个内部函数(inner)中,可以操作foo中的数据
//将外部函数的返回值设置为内部函数
//在外部调用外部函数(foo),就可以接受到返回值(内部函数)
//使用这个内部函数,就可以在外部对外部函数里的变量进行修改
//将外部函数的返回值设置为内部函数
//在外部调用外部函数(foo),就可以接受到返回值(内部函数)
//使用这个内部函数,就可以在外部对外部函数里的变量进行修改
function outer(){ var data = "数据"; return function(){ return data; } } function outer(){ var data = "数据"; var data1 = "数据1"; return { getData:function(){ return data; }, getData1:function(){ return data1; } } } function outer(){ var data = "数据"; return { getData:function(){ return data; }, setData:function(value){ data = value; return data; } } }
## 闭包的作用
最基本的作用:可以通过闭包返回的函数或者方法,来修改函数内部的数据
如果把数据放在全局作用域内,那么所有人都可以随意修改,这个数据就不再可靠。
闭包可以创建一个私有空间,在这个空间内部的数据,外部无法直接访问
外部空间想要访问函数内部的数据,只能通过闭包提供的指定的方法,在这个方法内部可以设置一些校验规则,让数据变得更加的安全。
如:
function foo(){ var name = "潘明"; var badLevel = -1000000000000000000000000000000000; return { getName: function () { return name; }, setName: function (value) { name = value; return name; }, getBadLevel:function(){ return badLevel; }, setBadLevel:function (value) { //在函数外部想要修改数据 //只能通过函数内部的方法 //我们可以在函数内部定义的这个方法里 //设置安全措施,校验之类的操作 //可以保证系统的安全性和稳定性 if(value > 0 ){ throw "你敢说我坏!!!"; } badLevel = value; return badLevel; } } } // var obj = foo(); // obj.setName("王二"); obj.setBadLevel(obj.getBadLevel() * -1 * Math.pow(10,10000000)); console.log(obj.getBadLevel());
使用闭包获取多个变量
function foo() { var name = "张国荣"; var age = 18; return { getName:function () { return name; }, getAge:function () { return age; } } } var obj = foo(); console.log(obj.getName()); console.log(obj.getAge());
## 闭包练习
setTimeout的执行时机
所有的主任务的代码执行完毕之后,去检查所有的setTimeout回调函数,如果到时间了就执行
setTimeout的执行时机
所有的主任务的代码执行完毕之后,去检查所有的setTimeout回调函数,如果到时间了就执行
用闭包来解决回调函数在调用的时候访问的是全局的变量
在闭包中创建一个变量,来单独存储当前的回调函数需要的数据,
在调用的时候就会去使用这个单独的数据,而不是去访问全局变量
在闭包中创建一个变量,来单独存储当前的回调函数需要的数据,
在调用的时候就会去使用这个单独的数据,而不是去访问全局变量
注册点击事件的时候
点击事件在触发的时候访问的是全局的变量
在闭包中创建一个变量,来单独存储当前的事件处理函数需要的数据,
在调用的时候就会去使用这个单独的数据,而不是去访问全局变量
点击事件在触发的时候访问的是全局的变量
在闭包中创建一个变量,来单独存储当前的事件处理函数需要的数据,
在调用的时候就会去使用这个单独的数据,而不是去访问全局变量
## 闭包缓存
缓存(cache):将常用的数据进行存储,以提升性能
硬件缓存
浏览器缓存
CDN(Content Delivery Network)
内存型数据库(非关系型)MongoDB Redis
浏览器缓存
CDN(Content Delivery Network)
内存型数据库(非关系型)MongoDB Redis
网站静态页面缓存机制
将网页静态化,存储在服务器端
如何用闭包实现缓存
1、写一个闭包在闭包中创建一个对象,用来做缓存的存储对象
2、在闭包中创建一个对象,用来做缓存的存储对象
3、在闭包中创建一个数组,用来存储换中的键
4、返回一个函数,这个函数需要两个参数,一个是key 一个是value
5、在返回的函数中,判断传入的value是否为undefined
6、如果为Undefined 则表示是获取值,就直接返回在第一步创建的缓存对象中指定的键对应的值
7、如果不为Undefined 则表示是设置值
8、在缓存对象中设置指定的key的值为value
9、把key加入存储key的数组
10、判断key数组是不是超出了缓存大小限制
11、如果超出限制,删除数组第一个元素(使用shift),获取到删除的key
12、使用删除的key删除缓存对象中存储的值(delete)
1、写一个闭包在闭包中创建一个对象,用来做缓存的存储对象
2、在闭包中创建一个对象,用来做缓存的存储对象
3、在闭包中创建一个数组,用来存储换中的键
4、返回一个函数,这个函数需要两个参数,一个是key 一个是value
5、在返回的函数中,判断传入的value是否为undefined
6、如果为Undefined 则表示是获取值,就直接返回在第一步创建的缓存对象中指定的键对应的值
7、如果不为Undefined 则表示是设置值
8、在缓存对象中设置指定的key的值为value
9、把key加入存储key的数组
10、判断key数组是不是超出了缓存大小限制
11、如果超出限制,删除数组第一个元素(使用shift),获取到删除的key
12、使用删除的key删除缓存对象中存储的值(delete)
## 使用缓存解决斐波那契数列的性能问题
就是将已经计算过的数字缓存进一个数组中,下次再来访问的时候,直接在数组中进行查找,如果找到直接使用,如果没有找到,计算后将数字存入数组,然后返回该数字
就是将已经计算过的数字缓存进一个数组中,下次再来访问的时候,直接在数组中进行查找,如果找到直接使用,如果没有找到,计算后将数字存入数组,然后返回该数字
解决方案
基本方案: var count =0 ;//计算计算次数 function createFib(){ var cache = []; function fib(n){ count ++; //1.从cache中获取数据 if(cache[n] !== undefined){ //如果缓存中有 直接返回 return cache[n]; } //如果缓存中没有 就计算 if(n <= 2){ //把计算结果存入数组 cache[n] = 1; return 1; } var temp = fib(n - 1) + fib(n - 2); //把计算结果存入数组 cache[n] = temp; return temp; } return fib; } //升级版: // 创建缓存容器 function createCache(){ var cache = {}; return function (key, value) { //如果传了值,就说明是设置值 if(value !== undefined){ cache[key] = value; return cache[key]; } //如果没有传值,只穿了键,那就是获取值 else{ return cache[key]; } } } var count =0 ; function createFib(){ var fibCache = createCache(); function fib(n){ count ++; //1.从cache中获取数据 if(fibCache(n) !== undefined){ //如果缓存中有 直接返回 return fibCache(n) ; } //如果缓存中没有 就计算 if(n <= 2){ //把计算结果存入数组 fibCache(n , 1) ; return 1; } var temp = fib(n - 1) + fib(n - 2); //把计算结果存入数组 fibCache(n, temp) ; return temp; } return fib; } var fib = createFib(); // console.log(fib(6)); fib(5); console.log(count); count = 0; fib(6); console.log(count); count = 0; fib(20); console.log(count); count = 0; fib(21); console.log(count); count = 0;
缓存:
jQuery 实现缓存 function createCache(){ //cache对象中以键值对的形式存储我们的缓存数据 var cache = {}; //index数组中该存储键,这个键是有顺序,可以方便我们做超出容量的处理 var index = []; return function (key, value) { //如果传了值,就说名是设置值 if(value !== undefined){ //将数据存入cache对象,做缓存 cache[key] = value; //将键存入index数组中,以和cache中的数据进行对应 index.push(key); //判断缓存中的数据数量是不是超出了限制 if(index.length >= 50){ //如果超出了限制 //删除掉最早存储缓存的数据 //最早存入缓存的数据的键是在index数组的第一位 //使用数组的shift方法可以获取并删除掉数组的第一个元素 var tempKey = index.shift(); //获取到最早加入缓存的这个数据的键,可以使用它将数据从缓存各种删除 delete cache[tempKey]; } } //如果没有传值,只穿了键,那就是获取值 // else{ // return cache[key]; // } return cache[key]; } }
## 沙箱模式
沙箱模式就是一个封闭的独立的环境,外界无法修改该环境内任何信息,沙箱内的东西单独属于一个世界
360沙箱模式
将软件和操作系统进行隔离,以达到安全的目的
将软件和操作系统进行隔离,以达到安全的目的
苹果手机的app使用的就是沙箱模式去运行
隔离app的空间,每个app独立运行
隔离app的空间,每个app独立运行
沙箱模式的基本模型
(function(){
//变量定义
//逻辑代码
//如果需要,向window对象添加成员,以暴露接口
})() //IIFE
(function(){
//变量定义
//逻辑代码
//如果需要,向window对象添加成员,以暴露接口
})() //IIFE
为什么要使用立即执行函数表达式(匿名函数)(IIFE)?
因为IIFE不会再外界暴露任何的全局变量,但是又可以形成一个封闭的空间,刚好可以实现沙箱模式
因为IIFE不会再外界暴露任何的全局变量,但是又可以形成一个封闭的空间,刚好可以实现沙箱模式
//jQuery当中的沙箱模式 (function(win){ var itcast = { getEle:function () { } } //如果需要在外界暴露一些属性或者方法,就可以将这些属性和方法 //加到window全局对象上去 //但是这window全局对象不可以直接引用,因为直接引用会破坏沙箱原则 //所以我们选择使用传参的形式将 window对象 传入沙箱内 //此时沙箱内使用window对象的时候,不会再去全局搜索window对象 //而使用的就是沙箱内部定义的形参 win.itCast = win.$ = itcast; })(window)
//沙箱模式一般应用在书写第三方框架
//或者为第三方框架书写插件
//或者书写功能独立的一些组件
//或者为第三方框架书写插件
//或者书写功能独立的一些组件
//沙箱模式的优势
//1.沙箱模式使用的是IIFE,不会再外界暴露任何的全局变量,也就不会造成全局变量污染
//2.沙箱中的所有数据,都是和外界完全隔离的,外界无法对其进行修改,也就保证了代码的安全性
//1.沙箱模式使用的是IIFE,不会再外界暴露任何的全局变量,也就不会造成全局变量污染
//2.沙箱中的所有数据,都是和外界完全隔离的,外界无法对其进行修改,也就保证了代码的安全性
//js中沙箱模式的实现原理就是
//函数可以构建作用域!上级作用域不能直接访问下级作用域中的数据