js变量的作用域:
全局作用域(全局变量) : 在函数外面声明的变量
**生命周期(变量从声明到销毁): 页面从打开到关闭.
局部作用域(局部变量) : 在函数里面声明的变量
**生命周: 开始调用函数到函数执行完毕
1.闭包使用介绍
1.闭包介绍(closure)
1.1 闭包 : 是一个可以在函数外部访问函数内部变量的函数->闭包是函数
1.2 闭包作用: 可以在函数外部访问函数内部变量->延长局部变量的生命周期
1.3 闭包语法 :
--->a. 在外部函数outer的内部声明了一个闭包函数closure
--->b. 在闭包函数内部 返回想要访问的局部变量
--->c. 在外部函数中返回闭包函数
1.4 闭包本质:是一个沟通函数内部(局部作用域)与函数外部(全局作用域)的桥梁
//1.需求:在函数外部访问函数内部的变量
// function fn(){
// var zhangsan = {
// name:'张三',
// age:34
// };
// };
// fn();
// 函数里面的局部变量在函数之后完毕之后自动被系统回收
// console.log(zhangsan);
// 2. return返回值
// 弊端 :浪费内存资源。 每调用一次函数,就生成了一个新的对象
// function fn(){
// var zhangsan = {
// name:'张三',
// age:34
// };
// return zhangsan;
// };
// var a = fn();
// console.log(a);
// var b = fn();
// console.log(b);
// //a和b是同一个对象吗?
// //每调用一次函数,就生成了一个新的对象。两个对象虽然数据是一样,但是在堆中两个不同的地址
// console.log(a == b);
//3.使用闭包实现在函数内部访问函数外部的变量
//声明外部函数
function fn(){
var zhangsan = {
name:'张三',
age:34
};
//闭包函数
function closure(){
// console.log(canglaoshi);
return zhangsan;
};
return closure;
};
//调用fn, (1)声明了一个对象张三(0xaaaa) (2)声明了一个闭包函数closure(0xbbbb) (3)返回闭包函数
var bibao = fn();
//调用闭包函数:得到fn中的局部变量
var a = bibao();
console.log(a);
var b = bibao();
console.log(b);
console.log(a == b);//true
2.闭包函数的使用步骤和注意点
1.复习闭包的语法步骤(3个步骤)
--->a.在外部函数outer中声明一个函数closure
--->b.在闭包函数中返回想要访问的变量
--->c.返回闭包函数
2.了解闭包语法的注意点
--->a. 如果希望访问同一个变量,外部函数只能调用一次
--->b. 如果希望访问不同的变量,外部函数可以调用多次
每调用一次生成一个新的局部变量和新的闭包函数
function outer(){
var num = Math.floor(Math.random()*100);
console.log(num);
//1.声明闭包函数
function closure(){
console.log(num);
//2.在闭包函数中返回想要访问的变量
return num;
};
//3.返回闭包函数
return closure;
};
//第一个注意点: 如果希望闭包访问的是同一个变量,外部函数只能调用一次
//1.调用outer : 声明局部变量,声明闭包函数
//获取闭包函数
var bibao = outer();
//2.调用闭包函数
bibao();
bibao();
bibao();
//第二个注意点: 如果希望闭包访问的是不同的变量,外部函数需要调用多次
// var bibao1 = outer();
// bibao1();
outer()();
// var bibao2 = outer();
// bibao2();
outer()();
// var bibao3 = outer();
// bibao3();
outer()();
//2。示例 投票机
function vouter(){
var num = 10;
function closure(){
num++;
console.log(num);
return num;
};
return closure;
};
//2.1 一台投票机,投3票
var bb = vouter();
bb();//11
bb();//12
bb();//13
//2.2 三台投票机,各投1票
var bb1 = vouter();
bb1();//11
// var bb2 = vouter();
// bb2();//11
vouter()();
// var bb3 = vouter();
// bb3();//11
vouter()();
3.由一个练习题看闭包的本质
//1.
var name = 'My Window';
var obj = {
name:'My Object',
getFunction:function(){
return function(){
console.log(this.name);
};
}
};
//(1) var fn = obj.getFunction() (2)fn() // window.fn()
// (1)当调用obj.getFunction()的时候返回一个匿名函数 (返回到全局作用域)
//(2)调用匿名函数,由于全局的所以函数指向window
obj.getFunction()();//'My Window';
//2.
var name = 'My Window';
var obj = {
name:'My Object',
getFunction:function(){
var that = this;
return function(){
console.log(that.name);
};
}
};
//(1)调用obj.getFunction() : 这个函数中this是obj ,声明了一个局部变量that存储this
//(2)调用闭包函数,由于闭包函数延长了that的生命周期,所以会打印obj.name属性值
console.log(obj.getFunction()()); //My Object
4.闭包的经典使用场景
/*
1.沙箱模式:是js的一种设计模式。 一个独立的作用域完成独立的功能,通常是一个匿名函数自调用
2.沙箱模式好处
a. 封闭的空间,避免全局变量污染
b. 模块化开发(一个功能模块对应一个作用域)
*/
(function(w){
var person = {
name:'张三',
age:18
};
person.eat = function(){
console.log('今天吃米粉');
};
person.play = function(){
console.log('大吉大利,今晚吃鸡');
};
/*
1.外部如何访问沙箱中的变量?
2.使用参数
为什么不直接使用window?
a.沙箱里面不能直接访问外部变量,破坏封装性
b.以后实际开发代码会压缩,可能会把window压缩成w。直接使用无法获取的
*/
w.person = person;
})(window);
console.log(person);
person.eat();
递归
1.递归函数: 函数自己调用自己
2.递归函数的特点
a.能用递归实现的功能就一定可以使用循环调用函数来实现,只是语法简洁性与性能不同而已
b.一定要有结束条件,否则会导致死循环
注意:递归不可以乱用,因为在有些时候性能不好
1.递归的简使用
//递归
var i = 1;
function fn(){
console.log('吾日三省吾身');
i++;
if(i<=3){
fn();
};
};
fn();
// 循环
for(var i = 1;i<=3;i++){
fn();
}
function fn(){
console.log('吾日三省吾身');
};
2.递归的使用场景
//1.求1-n的累加和
//递归实现
function getSum(n) {
if (n == 1) {
return 1;
} else {
return getSum(n - 1) + n;
};
//递归分析
// if(n == 1){
// return 1;
// }else if(n == 2){
// return getSum(n-1) + n;
// }else if(n == 3){
// return getSum(2) + 3;
// }else if(n == 4){
// return getSum(3) + 4;
// }else if(n == 5){
// return getSum(4) + 5;
// }
// ***
// else if(n == n){
// return getSum(n-1) + n
// }
};
console.log(getSum(5));//1+ 2 + 3 + 4 + 5 = 15
//循环实现
// function getSum(n){
// //求和:箩筐思想三步法
// var sum = 0;
// for(var i = 1; i<=n;i++){
// sum+=i;
// };
// return sum;
// };
// console.log(getSum(100));
//2.求阶乘(*)
/*阶乘:
5! = 5 * 4 * 3 * 2 * 1
6! = 6 * 5 * 4 * 3 * 2 * 1
*/
//递归实现
function jieChen(n) {
return n == 1 ? 1 : jieChen(n - 1) * n;
// if(n == 1){
// return 1;
// }else{
// return jieChen(n-1) * n;
// };
//递归分析
// if(n == 1){
// return 1;
// }else if(n == 2){
// return jieChen(1) * 2;
// }else if(n == 3){
// return jieChen(2) * 3
// }else if(n == n){
// return jieChen(n-1) * n;
// }
};
console.log(jieChen(5));// 5 * 4 * 3 * 2 * 1 = 120
//阶乘奇葩面试题 arguments.callee 得到的是函数本身
var num = (function (n) { return n == 1 ? 1 : arguments.callee(n - 1) * n })(6);
console.log(num);
// //循环实现
// function jieChen(n){
// var sum = 1;
// for(var i = 1 ;i<=n;i++){
// sum *= i;
// };
// return sum;
// };
// console.log(jieChen(6));// 5 * 4 * 3 * 2 * 1 = 120
//3.斐波那契数列
//了解: 递归函数虽然语法简洁,但是性能不是一定高于循环
//检测递归的性能,递归不可以乱用,因为性能不好
console.time();
//递归实现
function fib(n){
if(n == 1 || n == 2){
return 1;
}else{
return fib(n-2) + fib(n-1);
};
// if(n == 1 || n == 2){
// return 1;
// }else if(n == 3){
// return fib(3-2) + fib(3-1);
// }else if(n == 4){
// return fib(4-1) + fib(4-2)
// }
};
console.log(fib(10));
console.timeEnd();
/* 需求:求斐波那契额数列第十列
1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368........
1.前面两个数字固定 1 , 1
2.第三个数字开始,每一个数字都是前面两个数字的和
*/
function fib(n) {
var arr = [1, 1];
for (var i = 2; i < n; i++) {
arr[i] = arr[i - 2] + arr[i - 1];
};
return arr[arr.length-1];
};
console.log(fib(100));
// arr[2] = arr[2-2] + arr[2-1];
// arr[3] = arr[3-2] + arr[3-1];
// arr[4] = arr[4-2] + arr[4-1];
3.使用递归遍历dom数
/* 递归应用场景:遍历DOM树
1.需求:获取father父元素的所有后代元素
2.DOM的api中有没有直接的方法可以获取呢? 没有
3.递归思路
a。遍历父元素father
b. 遍历每一个子元素,找到子元素的子元素。然后又继续遍历子元素的子元素的子元素,以此类推
形成递归调用
*/
var father = document.getElementById('father');
var list = [];//存储所有的后代元素
function houDai(ele){
for(var i = 0;i