js變量的作用域:
全局作用域(全局變量) : 在函數(shù)外面聲明的變量
**生命周期(變量從聲明到銷毀): 頁面從打開到關(guān)閉.
局部作用域(局部變量) : 在函數(shù)里面聲明的變量
**生命周: 開始調(diào)用函數(shù)到函數(shù)執(zhí)行完畢
1.閉包使用介紹
1.閉包介紹(closure)
1.1 閉包 : 是一個(gè)可以在函數(shù)外部訪問函數(shù)內(nèi)部變量的函數(shù)
* 閉包是函數(shù)
1.2 閉包作用: 可以在函數(shù)外部訪問函數(shù)內(nèi)部變量
* 延長(zhǎng)局部變量的生命周期
1.3 閉包語法 :
a. 在外部函數(shù)outer的內(nèi)部聲明了一個(gè)閉包函數(shù)closure
b. 在閉包函數(shù)內(nèi)部 返回想要訪問的局部變量
c. 在外部函數(shù)中返回閉包函數(shù)
1.4 閉包本質(zhì):
是一個(gè)溝通函數(shù)內(nèi)部(局部作用域)與函數(shù)外部(全局作用域)的橋梁
//1.需求:在函數(shù)外部訪問函數(shù)內(nèi)部的變量
// function fn(){
// var zhangsan = {
// name:'張三',
// age:34
// };
// };
// fn();
// 函數(shù)里面的局部變量在函數(shù)之后完畢之后自動(dòng)被系統(tǒng)回收
// console.log(zhangsan);
// 2. return返回值
// 弊端 :浪費(fèi)內(nèi)存資源。 每調(diào)用一次函數(shù),就生成了一個(gè)新的對(duì)象
// function fn(){
// var zhangsan = {
// name:'張三',
// age:34
// };
// return zhangsan;
// };
// var a = fn();
// console.log(a);
// var b = fn();
// console.log(b);
// //a和b是同一個(gè)對(duì)象嗎?
// //每調(diào)用一次函數(shù)支鸡,就生成了一個(gè)新的對(duì)象受葛。兩個(gè)對(duì)象雖然數(shù)據(jù)是一樣巫橄,但是在堆中兩個(gè)不同的地址
// console.log(a == b);
//3.使用閉包實(shí)現(xiàn)在函數(shù)內(nèi)部訪問函數(shù)外部的變量
//聲明外部函數(shù)
function fn(){
var zhangsan = {
name:'張三',
age:34
};
//閉包函數(shù)
function closure(){
// console.log(canglaoshi);
return zhangsan;
};
return closure;
};
//調(diào)用fn群嗤, (1)聲明了一個(gè)對(duì)象張三(0xaaaa) (2)聲明了一個(gè)閉包函數(shù)closure(0xbbbb) (3)返回閉包函數(shù)
var bibao = fn();
//調(diào)用閉包函數(shù):得到fn中的局部變量
var a = bibao();
console.log(a);
var b = bibao();
console.log(b);
console.log(a == b);//true
2.閉包函數(shù)的使用步驟和注意點(diǎn)
1.復(fù)習(xí)閉包的語法步驟(3個(gè)步驟)
a.在外部函數(shù)outer中聲明一個(gè)函數(shù)closure
b.在閉包函數(shù)中返回想要訪問的變量
c.返回閉包函數(shù)
2.了解閉包語法的注意點(diǎn)
a. 如果希望訪問同一個(gè)變量扑庞,外部函數(shù)只能調(diào)用一次
b. 如果希望訪問不同的變量淡溯,外部函數(shù)可以調(diào)用多次
* 每調(diào)用一次生成一個(gè)新的局部變量和新的閉包函數(shù)
function outer(){
var num = Math.floor(Math.random()*100);
console.log(num);
//1.聲明閉包函數(shù)
function closure(){
console.log(num);
//2.在閉包函數(shù)中返回想要訪問的變量
return num;
};
//3.返回閉包函數(shù)
return closure;
};
//第一個(gè)注意點(diǎn): 如果希望閉包訪問的是同一個(gè)變量读整,外部函數(shù)只能調(diào)用一次
//1.調(diào)用outer : 聲明局部變量,聲明閉包函數(shù)
//獲取閉包函數(shù)
var bibao = outer();
//2.調(diào)用閉包函數(shù)
bibao();
bibao();
bibao();
//第二個(gè)注意點(diǎn): 如果希望閉包訪問的是不同的變量咱娶,外部函數(shù)需要調(diào)用多次
// var bibao1 = outer();
// bibao1();
outer()();
// var bibao2 = outer();
// bibao2();
outer()();
// var bibao3 = outer();
// bibao3();
outer()();
//2米间。示例 投票機(jī)
function vouter(){
var num = 10;
function closure(){
num++;
console.log(num);
return num;
};
return closure;
};
//2.1 一臺(tái)投票機(jī)强品,投3票
var bb = vouter();
bb();//11
bb();//12
bb();//13
//2.2 三臺(tái)投票機(jī),各投1票
var bb1 = vouter();
bb1();//11
// var bb2 = vouter();
// bb2();//11
vouter()();
// var bb3 = vouter();
// bb3();//11
vouter()();
3.由一個(gè)練習(xí)題看閉包的本質(zhì)
//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)當(dāng)調(diào)用obj.getFunction()的時(shí)候返回一個(gè)匿名函數(shù) (返回到全局作用域)
//(2)調(diào)用匿名函數(shù)屈糊,由于全局的所以函數(shù)指向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)調(diào)用obj.getFunction() : 這個(gè)函數(shù)中this是obj ,聲明了一個(gè)局部變量that存儲(chǔ)this
//(2)調(diào)用閉包函數(shù)的榛,由于閉包函數(shù)延長(zhǎng)了that的生命周期,所以會(huì)打印obj.name屬性值
console.log(obj.getFunction()()); //My Object
4.閉包的經(jīng)典使用場(chǎng)景
/*
1.沙箱模式:是js的一種設(shè)計(jì)模式逻锐。 一個(gè)獨(dú)立的作用域完成獨(dú)立的功能夫晌,通常是一個(gè)匿名函數(shù)自調(diào)用
2.沙箱模式好處
a. 封閉的空間,避免全局變量污染
b. 模塊化開發(fā)(一個(gè)功能模塊對(duì)應(yīng)一個(gè)作用域)
*/
(function(w){
var person = {
name:'張三',
age:18
};
person.eat = function(){
console.log('今天吃米粉');
};
person.play = function(){
console.log('大吉大利昧诱,今晚吃雞');
};
/*
1.外部如何訪問沙箱中的變量晓淀?
2.使用參數(shù)
為什么不直接使用window?
a.沙箱里面不能直接訪問外部變量,破壞封裝性
b.以后實(shí)際開發(fā)代碼會(huì)壓縮,可能會(huì)把window壓縮成w盏档。直接使用無法獲取的
*/
w.person = person;
})(window);
console.log(person);
person.eat();
遞歸
1.遞歸函數(shù): 函數(shù)自己調(diào)用自己
2.遞歸函數(shù)的特點(diǎn)
a.能用遞歸實(shí)現(xiàn)的功能就一定可以使用循環(huán)調(diào)用函數(shù)來實(shí)現(xiàn),只是語法簡(jiǎn)潔性與性能不同而已
b.一定要有結(jié)束條件,否則會(huì)導(dǎo)致死循環(huán)
注意:遞歸不可以亂用,因?yàn)樵谟行r(shí)候性能不好
1.遞歸的簡(jiǎn)使用
//遞歸
var i = 1;
function fn(){
console.log('吾日三省吾身');
i++;
if(i<=3){
fn();
};
};
fn();
// 循環(huán)
for(var i = 1;i<=3;i++){
fn();
}
function fn(){
console.log('吾日三省吾身');
};
2.遞歸的使用場(chǎng)景
//1.求1-n的累加和
//遞歸實(shí)現(xià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
//循環(huán)實(shí)現(xiàn)
// 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
*/
//遞歸實(shí)現(xiàn)
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 得到的是函數(shù)本身
var num = (function (n) { return n == 1 ? 1 : arguments.callee(n - 1) * n })(6);
console.log(num);
// //循環(huán)實(shí)現(xiàn)
// 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.斐波那契數(shù)列
//了解: 遞歸函數(shù)雖然語法簡(jiǎn)潔凶掰,但是性能不是一定高于循環(huán)
//檢測(cè)遞歸的性能,遞歸不可以亂用,因?yàn)樾阅懿缓?
console.time();
//遞歸實(shí)現(xiàn)
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();
/* 需求:求斐波那契額數(shù)列第十列
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.前面兩個(gè)數(shù)字固定 1 , 1
2.第三個(gè)數(shù)字開始,每一個(gè)數(shù)字都是前面兩個(gè)數(shù)字的和
*/
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數(shù)
/* 遞歸應(yīng)用場(chǎng)景:遍歷DOM樹
1.需求:獲取father父元素的所有后代元素
2.DOM的api中有沒有直接的方法可以獲取呢赘理? 沒有
3.遞歸思路
a宦言。遍歷父元素father
b. 遍歷每一個(gè)子元素,找到子元素的子元素商模。然后又繼續(xù)遍歷子元素的子元素的子元素奠旺,以此類推
形成遞歸調(diào)用
*/
var father = document.getElementById('father');
var list = [];//存儲(chǔ)所有的后代元素
function houDai(ele){
for(var i = 0;i<ele.children.length;i++){
list.push(ele.children[i]);
//遞歸求子元素的子元素
houDai(ele.children[i]);
};
};
//求父元素的所有后代元素
// houDai(father);
// console.log(list);
//求整顆dom樹
houDai(document);
console.log(list);