08-JavaScript数组

08-数组


1.数组的简介

1).数组(Array)

-数组也是一个对象,它和普通对象功能类似,也是用来存储一些值的,
不同的是普通对象是使用字符串作为属性名的,而数组是使用数字来作为索引操作元素
-数组的存储性能比普通对象要好,在开发中我们经常使用数组来存储一些数据
-数组也是一种复合数据类型,在数组可以存储多个不同类型的数据
-数组中存储的是有序的数据,数组中的每个数据都有一个唯一的索引,可以通过索引来操作获取数据
-数组中存储的数据叫做元素
-索引(index)是一组大于0的整数,从0开始的整数就是索引

a.创建数组

通过 Array() 来创建数组,也可以通过字面量 [ ] 来创建数组

//创建一个数组
var arr=new Array();

//使用字面量来创建数组
语法:
	[]
var arr=[];
b.向数组中添加元素
语法:
		数组[索引]=元素
c.读取数组中的元素
语法:
		数组[索引]
-如果读取了一个不存在的元素,不会报错而是返回undefined
d.获取数组的长度

可以使用length属性来获取数组的长度(元素的个数)

语法:
		数组.length   

对于连续的数组,使用length可以获取到数组的长度(元素的个数)
对于非连续的数组,使用length会获取到数组的最大的索引+1,尽量不要创建非连续的数组

①.length是可以修改的

修改length:
如果修改的length大于原长度,则多出部分会空出来
如果修改的length小于原长度,则多出的元素会被删除

②.向数组的最后一个位置添加元素
语法:
		数组[数组.length]=值;
const arr=new Array()
const arr2=[]	//数组字面量
arr[0]=10
arr[1]=22
//使用数组时,应该避免非连续数组,因为它性能不好 
//arr[100]=99
console.log(arr)
console.log(arr2)
console.log(typeof arr)
console.log(arr.length)
arr.length=5
console.log(arr.length)
console.log(arr)
arr[arr.length]=70;
console.log(arr)

2).数组的字面量

//使用字面量创建数组时,可以在创建时就指定数组中的元素
var arr3=[1,2,3,4,5,10];
console.log(arr[3]);
//使用构造函数创建数组时,也可以同时添加元素,将要添加的元素作文构造函数的参数传递
//元素之间使用,隔开
var arr4=new Array(10,20,30);
console.log(arr4);
//创建一个数组,数组中只有一个元素10
arr3=[10];
//创建一个长度为10的数组
arr4=new Array(10);
console.log(arr3);
console.log(arr4);
//数组中的元素可以是任意的数据类型
arr3=["hello",1,true,null,undefined];

//也可以是对象
var obj={name:"孙悟空"};
arr3[arr3.length]=obj;
console.log(arr3[5].name);
arr3=[{name:"孙悟空"},{name:"孙悟空"},{name:"孙悟空"}];
console.log(arr3[1]);

//也可以是函数
arr3=[function(){alert(1)},function(){alert(2)}];
console.log(arr3);
arr3[1]();

//数组也可以放数组,这种数组被称为二维数组
arr3=[[1,2,3],[3,4,5],[5,6,7]];
console.log(arr3[0]);

2.数组的遍历

1).遍历数组

-遍历数组简单理解,就是获取到数组中的每一个元素

//创建一个数组
var arr=["孙悟空","猪八戒","沙和尚"];
//所谓遍历数组就是将数组中所有的元素都取出来
/* for(var i=0;i<arr.length;i++){
             console.log(arr[i]);
         } */
//倒序
for(var i=arr.length-1;i>=0;i--){
 console.log(arr[i]);
}

2).for-of语句

for-of语句可以用来遍历可迭代对象

语法:
for(变量 of 可迭代的对象){
 语句...
}
执行流程:
for-of的循环体会执行多次,数组中有几个元素就会执行几次,
每次执行时都会将一个元素赋值给变量
const arr=["孙悟空","猪八戒","沙和尚","唐僧"];
for(let value of arr){
 console.log(value)
}

3.数组的方法

1).非破坏性的方法

①.Array. isArray( )

​ -用来检查一个对象是否是数组

console.log(Array.isArray({name:"孙悟空"}));		//false
console.log(Array.isArray([1,2,3]));			//true
②.at( )

​ -可以根据索引获取数组中的指定元素
​ -at可以接收负索引作为参数

var arr=["孙悟空","猪八戒","沙和尚"];
console.log(arr.at(-2));	//猪八戒
//等同于
console.log(arr[arr.length-2]);		//猪八戒
③.concat( )

​ -可以连接两个或多个数组,并将新的数组返回

var arr=["孙悟空","猪八戒","沙和尚"];
var arr2=["白骨精","玉兔精","蜘蛛精"];
var arr3=["二郎神","唐僧","太上老君"];
console.log((arr.concat(arr2)));
console.log((arr.concat(arr2,arr3)));
console.log((arr.concat(arr2,arr3,"牛魔王","铁扇公主")));

④. indexOf( )

​ -获取元素在数组中第一次出现的索引
​ -参数:
​ 1.要查询的元素
​ 2.查询的其实位置

var arr=["孙悟空","猪八戒","沙和尚","唐僧","沙和尚"];
let result=arr.indexOf("沙和尚")
console.log(result);		//2
⑤.lastIndexOf( )

​ -获取元素在数组中最后一次出现的位置
​ -返回值:
​ 找到了则返回元素的索引,
​ 没有找到返回-1

var arr=["孙悟空","猪八戒","沙和尚","唐僧","沙和尚"];
let result=arr.lastIndexOf("沙和尚")		//4
result=arr.lastIndexOf("白骨精")			//-1
console.log(result);
⑥.join()

​ -该方法可以将数组转换为一个字符串
​ -该方法不会对原数组产生影响,而是将转换后的字符串作为结果返回
​ -在join( )中可以指定一个字符串作为参数,这个字符串将会成为数组中元素的连接符,如果不指定连接符,则默认使用,作为连接符

var arr=["孙悟空","猪八戒","沙和尚","唐僧","沙和尚"];
console.log((arr.join("@-@")));

⑦. slice()

​ -可以用来从数组截取指定元素
​ -该方法不会改变元素数组,而是将截取到的元素封装到一个新数组中返回
​ -参数:
​ 1.截取开始的位置的索引,包含开始索引(包括该位置)
​ 2.截取结束的位置的索引,不包含结束索引(不包括该位置)
​ -第二个参数可以省略不写,此时会截取从开始索引往后的所有元素
​ -索引可以传递一个负值,如果传递一个负值,则从后往前计算
​ -1倒数第一个
​ -2倒数第二个
​ -如果将两个参数:全都省略,则可以对数组进行浅拷贝(浅复制)

var arr = ["孙悟空", "猪八戒", "沙和尚", "唐僧", "沙和尚"];
console.log(arr.slice(0,2));
console.log(arr.slice(1));
console.log(arr.slice(1,-1));


3).破坏性的方法

①.splice()

​ -可以删除、添加、插入、替换数组中的元素
​ -使用splice()会影响到原数组,会将指定元素从原数组中删除
​ 并将被删除的元素作为返回值返回
​ -参数:
​ 第一个,表示开始位置的索引
​ 第二个,表示删除的数量
​ 第三个及以后,要插入的元素
​ 可以传递一些新的元素,这些元素将会自动插入到开始位置索引前边

arr = ["孙悟空", "猪八戒", "沙和尚","唐僧"];
result=arr.splice(1,3)
console.log(arr);
console.log(result);
result=arr.splice(1,2,"牛魔王")
console.log(arr);
console.log(result);
result=arr.splice(1,1,"牛魔王","铁扇公主")
console.log(arr);
console.log(result);
result=arr.splice(4,0,"牛魔王","铁扇公主","红孩儿")
console.log(arr);
console.log(result);

②.push()

​ -该方法可以向数组的末尾添加一个或多个元素,并返回数组的新的长度
​ -可以将要添加的元素作为方法的参数传递,这样这些元素将会自动添加到数组的末尾
​ -该方法会将数组新的长度作为返回值返回

var arr = ["孙悟空", "猪八戒", "沙和尚"];
var result = arr.push("唐僧", "蜘蛛精", "白骨精", "玉兔精");
console.log(arr);
console.log(result);

③.pop()

​ -该方法可以删除数组的最后一个元素,并将被删除的元素作为返回值返回

var arr = ["孙悟空", "猪八戒", "沙和尚"];
var result=arr.pop();
console.log(arr);
console.log(result);

④.unshift()

​ -向数组开头添加一个或多个元素,并返回新的数组长度
​ -向前边插入元素以后,其他的元素索引会依次调整

var arr = ["孙悟空", "猪八戒", "沙和尚"];
arr.unshift("牛魔王","二郎神");
console.log(arr);

⑤.shift()

​ -可以删除数组的第一个元素,并将被删除的元素作为返回值返回

var arr = ["孙悟空", "猪八戒", "沙和尚"];
var result=arr.shift();
console.log(arr);
console.log(result);

⑥.reverse( )

​ -该方法用来反转数组(前边的去后边,后边的去前边)
​ -该方法会直接修改原数组

arr=["a","b","c","d","e"]
arr.reverse();
console.log(arr);

4).带有高阶函数的方法

①.sort( )

​ -sort用来对数组进行排序(会对改变原数组)
​ -sort默认会将数组升序排列
​ 注意: sort默认会按照Unicode编码进行排序,所以如果直接通过sort对数字进行排序,可能会得到一个不正确的结果

​ -参数:
​ -可以传递一个回调函数作为参数,通过回调函数来指定排序规则
​ ( a,b) => a - b升序排列
​ ( a,b) => b - a降序排列

arr = ["b", "d", "e", "a", "c"];
arr.sort();
console.log(arr);

即使对于纯数字的数组,使用sort( )排序时,也会按照Unicode编码来排序,
	所以对数字进排序时,可能会得到错误的结果。

我们可以自己来指定排序的规则
	我们可以在sort( )添加一个回调函数,来指定排序规则,
     回调函数中需要定义两个形参,
     浏览器将会分别使用数组中的元素作为实参去调用回调函数
     使用哪个元素调用不确定,但是肯定的是在数组中a一定在b前边
 	-浏览器会根据回调函数的返回值来决定元素的顺序,
         如果返回一个大于0的值,则元素会交换位置
         如果返回一个小于0的值,则元素位置不变
         如果返回一个0,则认为两个元素相等,也不交换位置
     -如果需要升序排列, 则返回a-b
     	如果需要降序排列,则返回b-a
arr = [5, 4, 2, 1, 3, 6, 8, 7, 8];
arr.sort(function (a, b) {
 return a-b;//升序
 return -(a - b);//降序
});
console.log(arr);

②.forEach()

​ -用来遍历数组
​ -它需要一个回调函数作为参数,这个回调函数会被调用多次
​ 数组中有几个元素,回调函数就会调用几次
​ 每次调用,都会将数组中的数据作为参数传递
​ -回调函数中有三个参数:
​ element当前的元素
​ index当前元素的索引
​ array被遍历的数组

回调函数: (方法需要一个函数作为参数时用)
-像这种函数,由我们创建但是不由我们调用的,我们称为回调函数
-数组中有几个元素函数就会执行几次,每次执行时,浏览器会将遍历到的元素,以实参的形式传递进来,我们可以来定义形参,来读取这些内容
-浏览器会在回调函数中传递三个参数:
        第一个参数,就是当前正在遍历的元素(element)
        第二个参数,就是当前正在遍历的元素的索引
        第三个参数,就是正在遍历的数组)(array)
//创建一个数组
var arr = ["孙悟空", "猪八戒", "沙和尚"];
arr.forEach(function(element,index,array){
 console.log("a="+element);
 console.log("b="+index);
 console.log("c="+array);
});
arr.forEach((element,index)=>console.log(index,element))

③.filter()

​ -将数组中符合条件的元素保存到一个新数组中返回
​ –需要一个回调函数作为参数,会为每一个元素去调用回调函数,并根据返回值来决定是否将元素添加到新数组中
​ -非破坏性方法,不会影响原数组

arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]
//获取数组中所有的偶数
let result = arr.filter(ele => ele % 2 === 0)
console.log(result);

④.map()

​ -根据当前数组生成一个新数组
​ -需要一个回调函数作为参数,
​ 回调函数的返回值会成为新数组中的元素
​ -非破坏性方法不会影响原数组

result = arr.map(ele => "hello")
console.log(result);
result = arr.map(ele =>ele*2)
console.log(result);
result = arr.map(ele =>"<li>"+ele+"</li>")
console.log(result);

⑤.educe()

​ -可以用来将一个数组中的所有元素整合为一个值
​ -参数:
​ 1.回调函数,通过回调函数来指定合并的规则
​ 2.可选参数,初始值

arr.reduce((a,b)=>{
 console.log(a,b);
 return a+b
})
result=arr.reduce((a,b)=>a+b,10)
console.log(result);


4.对象的复制

const arr=["孙悟空","猪八戒","沙和尚"]
const arr1=arr   //不是复制
console.log(arr===arr1);
console.log(arr);
console.log(arr1);
arr1[0]="唐僧"	//一个对象的属性改变,另一个对象的属性也会被迫改变
console.log(arr);
console.log(arr1);

//如何去复制一个对象复制必须要产生新的对象
//当调用slice时,会产生一个新的数组对象,从而完成对数组的复制
const arr2=arr.slice()
console.log(arr===arr2);
console.log(arr);
console.log(arr2);
arr2[0]="唐僧"	//当一个对象的属性改变时,另一个对象的属性不会改变
console.log(arr);
console.log(arr2); 
// 原始值不可变,在内存当中只有一份,不可能出现两个相同的原始值

1).浅拷贝与深拷贝

a.浅拷贝(shallow copy)

浅拷贝,数组中的元素指向的还是原来的对象。
-通常对对象的拷贝都是浅拷贝
-浅拷贝顾名思义,只对对象的浅层进行复制(只复制一层)
-如果对象中存储的数据是原始值,那么拷贝的深浅是不重要
-浅拷贝只会对对象本身进行复制,不会复制对象中的属性(或元素)

b.深拷贝(deep copy)

深拷贝,相当于把元素又重新new一份,内存地址变了
-深拷贝指不仅复制对象本身,还复制对象中的属性和元素
-因为性能问题,通常情况不太使用深拷贝

const arr=[{name:"孙悟空"},{name:"猪八戒"}]
const arr1=arr.slice()//浅拷贝
const arr2=structuredClone(arr)//专门用来深拷贝的方法
console.log(arr);
console.log(arr1);
console.log(arr2);

2).复制的方式

const arr=["孙悟空","猪八戒","沙和尚"]
const arr1=arr.slice()
console.log(arr===arr1);		//false
a. … (展开运算符)

-可以将一个数组中的元素展开到另一个数组中或者作为函数的参数传递
-通过它也可以对数组进行浅复制

const arr2=[...arr]
const arr3=["唐僧",...arr,"白龙马"]
console.log(arr3);

function sum(a,b,c){
return a+b+c
}
const arr4=[10,20,30]
//let result=sum(arr4[0],arr4[1],arr4[2])
//等同于
let result=sum(...arr4)
console.log(result);		//60

b.对象的复制
①.Object.assign
- Object.assign(目标对象,被复制的对象)
-将被复制对象中的属性复制到目标对象里,并将目标对象返回
-也可以使用展开运算符对对象进行复制
const obj={name:"孙悟空",age:18}
const obj1=Object.assign({},obj)
//等同于
const obj3={}
Object.assign(obj3,obj) 
console.log(obj1);
console.log(obj3);

const obj={name:"孙悟空",age:18}
const obj3={address:"花果山"}
Object.assign(obj3,obj)
console.log(obj1);
console.log(obj3);

const obj={name:"孙悟空",age:18}
const obj3={age:28} //被复制的对象会将复制的对象的重名属性进行覆盖
Object.assign(obj3,obj)
console.log(obj);
console.log(obj3);

②.Object.assign和…展开运算符
const obj2={...obj}
const obj2={address:"高老庄",...obj}
const obj2={...obj,age:29}   
const obj2={age:39,...obj}  //后面的属性覆盖前面的
console.log(obj);
console.log(obj2);


5.数组的去重

//创建一个数组
var arr = [1, 2, 1, 3, 2, 2, 4, 5, 5, 6, 7];

编写代码去除数组中重复的数字:

方法一:

console.log(arr); 
//分别获取数组中的元素
for (var i = 0; i < arr.length; i++) {
 //获取当前值后边的所有值
 for (var j = i + 1; j < arr.length - 1; j++) {
     //判断两个值是否相等
     if (arr[i] == arr[j]) {
         //出现相同元素,删除后边的元素
         arr.splice(j, 1);
         /* 
                     当arr[i]和arr[j]相同时,它会自动的删除当前j位置的元素,后边的元素会自动补位变成j位置的元素
                     而j位置已经比较过了,不会重复比较,所以会出现漏比较的情况

                     解决方法:
                         当删除一个元素以后,需要将该位置的元素再比较一遍
                         使j自减 
                 */
         j--;
     }
 }
}
console.log(arr); 

方法二:

console.log(arr); 
//获取数组中的元素
for(let i=0;i<arr.length;i++){
 const index=arr.indexOf(arr[i],i+1)
 if(index!=-1){
     //出现重复内容
     arr.splice(index,1)
     i-- 
 }
}
console.log(arr); 

方法三:

const newArr=[]
for(let ele of arr){    //ele表示数组中的元素
 if(newArr.indexOf(ele)===-1){
     newArr.push(ele)
 }
}
console.log(newArr);


6.排序

将[9,1,3,2,8,0,5,7,6,4]数组进行排序

1).冒泡排序

思路一:
9,1,3,2,8,0,5,7,6,4
-比较相邻的两个元素,然后根据大小来决定是否交换它们的位置
-例子:
第一次排序:1,3,2,8,0,5,7,6,4,9
第二次排序: 1,2,3,0,5,7,6,4,8,9
第三次排序:1,2,0,3,5,6,4,7,8,9…
倒数第二次 0,1,2,3,4,5,6,7,8,9

这种排序方式,被称为冒泡排序,冒泡排序是最慢的排序方式,数字少还可以用,不适用于数据量较大的排序

const arr = [9, 1, 3, 2, 8, 0, 5, 7, 6, 4]
for (let j = 0; j < arr.length - 1; j++) {
 for (let i = 0; i < arr.length - 1; i++) {
     //arr[i] 前边的元素 arr[i+1] 后边的元素
     if (arr[i] > arr[i + 1]) {
         //大数在前,小数在后,需要交换两个元素的位置
         let temp = arr[i]
         arr[i] = arr[i + 1]
         arr[i + 1] = temp
     }
 }
}

更新优化:

1,3,2,8,0,5,7,6,4,9 //9次
1,2,3,0,5,7,6,4,8,9 //8次
1,2,0,3,5,6,4,7,8,9 //7次
1,0,2,3,5,4,6,7,8,9 //6次

for (let j = 0; j < arr.length - 1; j++) {
 for (let i = 0; i < arr.length - 1-j; i++) {
     if (arr[i] > arr[i + 1]) {
         let temp = arr[i]
         arr[i] = arr[i + 1]
         arr[i + 1] = temp
     }
 }
}
console.log(arr);

2).选择排序

思路2:
-取出一个元素,然后将其他元素和该元素进行比较,如果其他元素比该元素小则交换两个元素的位置
-例子:
0, 9,3,2,8,1,5,7,6,4
0,1,9,3,8,2,5,7,6,4
0,1,2,9,8,3,5,7,6,4

选择排序:取出一个元素,然后将其他元素和该元素进行比较,如果其他元素比该元素小则交换两个元素的位置

const arr = [9, 1, 3, 2, 8, 0, 5, 7, 6, 4]
for (let i = 0; i < arr.length; i++) {
 for (let j = i+1; j < arr.length; j++) {
     if (arr[i] > arr[j]) {
         let temp = arr[i];
         arr[i] = arr[j];
         arr[j] = temp;
     }
 }
}
console.log(arr);

7.封装函数

const arr = [9, 1, 3, 2, 8, 0, 5, 7, 6, 4]
const arr1 = [9, 1, 3, 2, 8, 10, 5, 7, 6, 4]
function sort(array) {		//将排序的方法包装成函数以便调用
 const arr = [...array]
 for (let i = 0; i < arr.length; i++) {
     for (let j = i + 1; j < arr.length; j++) {
         if (arr[i] > arr[j]) {
             let temp = arr[i];
             arr[i] = arr[j];
             arr[j] = temp;
         }
     }
 }
 return arr
}
console.log(arr1);
console.log(sort(arr1));
class Person {
 constructor(name, age) {
     this.name = name;
     this.age = age;
 }
}
const personArr = [
 new Person("孙悟空", 18, "男"),
 new Person("猪八戒", 28, "男"),
 new Person("沙和尚", 28, "男"),
 new Person("蜘蛛精", 16, "女"),
 new Person("唐僧", 38, "男"),
 new Person("红孩儿", 8, "男")
]
创建一个函数,可以将personArr中的满18岁的Person提取出来,然后封装到一个新的数组newArr中并返回
 arr:
     形参,要提取信息的数组
function filter(arr) {
 //创建一个新的数组
 const newArr = [];
 //遍历arr,获取arr中Person对象
 for (let i = 0; i < arr.length; i++) {
     //判断Person对象的age是否大于等于18
     if ((arr[i].age) < 18) {
         //如果大于等于18,则将这个对象添加到newArr中
         newArr.push(arr[i]);
     }
 }
 //将新的数组返回
 return newArr;
}

更新函数:

目前我们的函数只能过滤出数组中age属性小于18的对象,
我们希望过滤更加灵活:
比如: 过滤数组中age大于18的对象
age大于60的对象age大于n的对象
过滤数组中name为xxx的对象
过滤数组中的偶数

一个函数的参数也可以是函数,如果将函数作为参数传递,那么我们就称这个函数为回调函数(callback)

function filter(arr,cb) {
 // console.log("-->",cb);
 // cb()
 //创建一个新的数组
 const newArr = [];
 //遍历arr,获取arr中Person对象
 for (let i = 0; i < arr.length; i++) {
     //判断Person对象的age是否大于等于18
     if (cb(arr[i])) {
         //如果大于等于18,则将这个对象添加到newArr中
         newArr.push(arr[i]);
     }
 }
 //将新的数组返回
 return newArr;
}
function fn(a){
 return a.age<18
}
console.log(filter(personArr,fn));

1).高阶函数

a.如果一个函数的参数或返回值是函数,则这个函数就称为高阶函数
b.为什么要将函数作为参数传递?(回调函数有什么作用?)
-将函数作为参数,意味着可以对另一个函数动态的传递代码

例子:

class Person {
 constructor(name, age) {
     this.name = name;
     this.age = age;
 }
}
const personArr = [
 new Person("孙悟空", 18, "男"),
 new Person("猪八戒", 28, "男"),
 new Person("沙和尚", 28, "男"),
 new Person("蜘蛛精", 16, "女"),
 new Person("唐僧", 38, "男"),
 new Person("红孩儿", 8, "男")
]
function filter(arr,cb) {
 // console.log("-->",cb);
 // cb()
 //创建一个新的数组
 const newArr = [];
 //遍历arr,获取arr中Person对象
 for (let i = 0; i < arr.length; i++) {
     //判断Person对象的age是否大于等于18
     if (cb(arr[i])) {
         //如果大于等于18,则将这个对象添加到newArr中
         newArr.push(arr[i]);
     }
 }
 //将新的数组返回
 return newArr;
}
function fn(a){
 return a.age<18
}
console.log(filter(personArr,fn));

//这种定义回调函数的形式比较少见,通常回调函数都是匿名函数:
console.log(filter(personArr,a=>a.name==="孙悟空"));
console.log(filter(personArr,a=>a.age>=18));
const arr=[1,2,3,4,5,6,7,8,9]
console.log(filter(arr,a=>a%2===0));

/* 
         希望在someFn()函数执行时,可以记录一条日志

         在不修改原函数的基础上,为其增加记录日志的功能
             可以通过高阶函数,来动态的生成一个新函数
     */
function someFn(a){
 console.log(a);
 return "Hello"
}
function outer(cb,a) {
 return ()=>{
     console.log("记录日志");
     return cb(a) 
 }
}
let result=outer(someFn,"hello!!!")
console.log(result);


8.闭包

创建一个函数,第一次调用时打印1,第二次调用打印2,以此类推

可以利用函数,来隐藏不希望被外部访问到的变量

1).闭包:

a.闭包就是能访问到外部函数作用域中变量的函数
b.什么时候使用:当我们需要隐藏一些不希望被别人访问的内容时就可以使用闭包
c.构成闭包的要件:
①.函数的嵌套
②.内部函数要引用外部函数中的变量
③.内部函数要作为返回值返回

let num=0    //全部变量只初始化一次
function fn() {
 num++
 console.log(num);
}
fn()

function outer() {
 let num=0   //位于函数作用域中 
 return ()=>{
     num++
     console.log(num);
 }
}
const newFn=outer()
console.log(newFn);
newFn()		//1

2).闭包的原理

函数在作用域,在函数创建时就已经确定的(词法作用域),和调用的位置无关,闭包利用的就是词法作用域

let a="全局变量a"
function fn() {
 console.log(a);
}
function fn2() {
 let a="fn2中的a"
 fn()
}
fn2() //全局变量a
function fn3() {
 let a="fn3中的a"
 function fn4() {
     console.log(a);
 }
 return fn4()
}
fn3() //fn3中的a
let fn4=fn3()
fn4  //fn3中的a

3).闭包的注意事项

闭包的生命周期:

​ 1.闭包在外部函数调用时产生,外部函数每次调用都会产生一个全新的闭包
​ 2.在内部函数丢失时销毁(内部函数被垃圾回收了,闭包才会消失)

注意事项:
     闭包主要用来隐藏一些不希望被外部访问的内容,
     这就意味着闭包需要占用一定的内存空间

相较于类来说,闭包比较浪费内存空间(类可以使用原型而闭包不能)
需要执行次数较少时,使用闭包
需要大量创建实例时,使用类

function outer() {
 let someVariable="someValue"
 return function () {
     console.log(someVariable);
 }
}
function  outer2() {
 let num=0
 return ()=>{
     num++
     console.log(num);
 }
}
const fn1=outer2()//独立闭包
fn1()
const fn2=outer2()//独立闭包
fn2()


9.递归函数

创建一个函数,可以用来求任意数的阶乘

如果用递归来解决阶乘的问题?
5 ! = 4! x 5
4! = 3! x 4
3! = 2 ! x 3
2! = 1! x 2
1! = 1

递归的核心思想就是将一个大的问题拆分为一个一个小的问题,小的问题解决了,大的问题也就解决了

编写递归函数,一定要包含两个要件:
a.基线条件 — 递归的终止条件
b.递归条件 — 如何对问题进行拆分

递归的作用和循环是一致的,不同点在于,递归思路的比较清晰简洁,循环的执行性能比较好
在开发中,一般的问题都可以通过循环解决,也是尽量去使用循环,少用递归,只在一些使用循环解决比较麻烦的场景下,才使用递归

function jiecheng(num) {
 //创建一个变量用来记录结果
 let result=1
 for (let  i= 2; i<=num; i++) {
     result *= i
 }
 return result
}
let result =jiecheng(3)
//console.log(result);
function jiecheng2(num) {
 //基线条件
 if (num===1) {
     return 1
 }
 //递归条件
 //num!=(num-1)!*num
 return jiecheng2(num-1)*num
}
result =jiecheng2(5)
console.log(result);	//120

递归练习:

一对兔子出生后的两个月后每个月都能生一对小兔子

编写一个函数,可以用来计算第n个月的兔子的数量

1 2 3 4 5 6 7 8 9 10 11 12
1 1 2 3 5 8 13 21 34 …
-规律,当前数等于前两个数之和(斐波那契数列)

//求斐波那契而数列中的第n个数
function fib(n) {
 //确定基线条件
 if (n<3) {
     return 1
 }
 //设置递归条件
 //第n个数=第n-1个数+第n-2个数
 return fib(n-1)+fib(n-2)
}
console.log(fib(10));

如果用递归来解决阶乘的问题?
5 ! = 4! x 5
4! = 3! x 4
3! = 2 ! x 3
2! = 1! x 2
1! = 1

递归的核心思想就是将一个大的问题拆分为一个一个小的问题,小的问题解决了,大的问题也就解决了

编写递归函数,一定要包含两个要件:
a.基线条件 — 递归的终止条件
b.递归条件 — 如何对问题进行拆分

递归的作用和循环是一致的,不同点在于,递归思路的比较清晰简洁,循环的执行性能比较好
在开发中,一般的问题都可以通过循环解决,也是尽量去使用循环,少用递归,只在一些使用循环解决比较麻烦的场景下,才使用递归

function jiecheng(num) {
 //创建一个变量用来记录结果
 let result=1
 for (let  i= 2; i<=num; i++) {
     result *= i
 }
 return result
}
let result =jiecheng(3)
//console.log(result);
function jiecheng2(num) {
 //基线条件
 if (num===1) {
     return 1
 }
 //递归条件
 //num!=(num-1)!*num
 return jiecheng2(num-1)*num
}
result =jiecheng2(5)
console.log(result);	//120

递归练习:

一对兔子出生后的两个月后每个月都能生一对小兔子

编写一个函数,可以用来计算第n个月的兔子的数量

1 2 3 4 5 6 7 8 9 10 11 12
1 1 2 3 5 8 13 21 34 …
-规律,当前数等于前两个数之和(斐波那契数列)

//求斐波那契而数列中的第n个数
function fib(n) {
 //确定基线条件
 if (n<3) {
     return 1
 }
 //设置递归条件
 //第n个数=第n-1个数+第n-2个数
 return fib(n-1)+fib(n-2)
}
console.log(fib(10));
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值