JS高级(三):严格模式、闭包、递归、深拷贝和浅拷贝
创始人
2024-01-28 19:22:43
0

JavaScript高级(三)

  • 一、严格模式
    • 1.开启严格模式
      • (1)为脚本开启严格模式
      • (2)为某个函数开启严格模式
    • 2.严格模式的一些规定
      • (1)禁止变量未声明就赋值
      • (2)禁止删除已声明的变量
      • (3)严格模式下this指向
      • (4)不允许形参有重名
      • (5)不允许在非函数代码块内声明函数
  • 二、闭包
    • 1.什么是闭包?
    • 2.闭包的作用
    • 3.闭包的一些应用
      • (1)点击li打印当前索引号(var)
      • (2)3秒后打印索引号(var)
      • (3)用闭包手撕防抖节流
  • 三、递归
    • 1.什么是递归?
    • 2.递归函数求阶乘
    • 3.递归求斐波那契数列第几个数是谁
    • 4.递归遍历数据
  • 四、深拷贝和浅拷贝
    • 1.浅拷贝
    • 2.手撕深拷贝

一、严格模式

IE10以上的浏览器才支持
在这里插入图片描述

1.开启严格模式

(1)为脚本开启严格模式


或者通过立即调用函数:


(2)为某个函数开启严格模式



2.严格模式的一些规定

(1)禁止变量未声明就赋值

正常模式下一个变量没有声明就赋值默认是全局变量,而严格模式下变量必须先用var或者let,const声明,然后再使用,否则会报错

(2)禁止删除已声明的变量

例如var a = 1; delete a;这种写法会报错

(3)严格模式下this指向

在严格模式下全局作用域的函数中,this指向undefined


注意:在严格模式下的定时器,this还是指向window,只有函数里this有变化

(4)不允许形参有重名

function dj(a,a) {  //严格模式下报错console.log(a+a);
}

(5)不允许在非函数代码块内声明函数

更多内容请见:MDN关于严格模式的描述

二、闭包

1.什么是闭包?

先复习一下:全局变量、局部变量和作用域链

闭包:能够访问另一个函数作用域中变量的函数。
原理:作用域链,当前作用域可以访问上级作用域中的变量

产生闭包的条件
1、函数套函数
2、内部函数访问外部函数局部变量,必须要访问了变量才会产生闭包

闭包就是将函数内部和函数外部连接起来的一座桥梁。

什么意思呢?举个例子:

函数inside访问了函数outside的作用域,那么inside函数就是一个闭包
function outside() {var n = 1;function inside() {console.log(n);  //1}inside();
}outside();
console.log(n); //n is not defined

关于闭包的定义有些歧义,有的认为inside函数是闭包,有的认为outside里面的函数+环境共同构成闭包,这个不用太纠结。

2.闭包的作用

闭包的主要作用:延伸了变量的作用范围

我们可以借助闭包,实现从外部访问outside函数内部的变量:

function outside() {var n = 1;return function() {console.log(n); }
}let get_n = outside();
get_n();  //1  外部访问函数内部的变量

闭包还有个特点就是,一般来说outside函数调用完变量n就会销毁,但是闭包的话延伸了变量的作用范围outside函数调用完n不会立即销毁,等到get_n函数调用完,n才销毁,即函数作用域中的变量在函数执行结束之后不被销毁

不过这也导致闭包有个缺点:

由于垃圾回收器不会将闭包中变量销毁,于是就造成了内存泄露,内存泄露积累多了就容易导致内存溢出。

3.闭包的一些应用

先看看什么是立即执行函数:

function fn(a) { console.log(a) }
fn(a);
===
(function(a) { console.log(a) })(a);

(1)点击li打印当前索引号(var)

复习:for循环中使用var的问题

创建一个立即执行函数,这样的话在里面添加点击事件就形成闭包,循环生成lis.length个立即执行函数,每个函数都拿到对应的i,这样的话函数调用完不会马上把当前i销毁,等到点击事件触发完后当前i才会销毁,这样就解决了问题(其实直接用let就可以了)

不过这样的缺点是四个立即执行函数,内存泄漏和内存溢出问题比较严重

//应用1:点击li打印当前索引号(用var声明)
for (var i = 0; i < lis.length; i++) {//创建一个立即执行函数,把i传进去(function (i) {lis[i].onclick = function() {console.log(i);}})(i);
}

(2)3秒后打印索引号(var)

和上面的一个意思,定义多个立即执行函数。
3秒后才访问变量i,那么i就不会立即被销毁,定时器结束调用后才销毁。

//应用2:3秒后打印索引号(用var声明)
for (var i = 0; i < lis.length; i++) {//创建一个立即执行函数,把i传进去(function (i) {setTimeout(() => {console.log(i);}, 3000);})(i);
}

(3)用闭包手撕防抖节流

还不会撕,先占个位

三、递归

1.什么是递归?

1、递归就是函数内部自己调用自己
2、递归函数的作用和循环差不多,就是无限套娃

递归很容易会发生栈溢出(不断调用不断开辟内存空间,函数放在堆里,函数的调用结果放在栈里,所以是栈溢出不是堆溢出),所以一定要加return才行

let num = 1;
function fn() {console.log('打印6句话');if (num === 6) {return;}num++;fn();
}
fn();

2.递归函数求阶乘

利用递归求1~n的阶乘
这个确实难想……好好理解理解代码吧。
4*cal(3) => 4*(3*cal(2)) => 4*(3*(2*cal(1))) => 4*(3*(2*1))

//利用递归求1~n的阶乘
function cal(n) {if(n===1) {return 1;}return n * cal(n-1);
}
console.log(cal(4));  //24

3.递归求斐波那契数列第几个数是谁

1、1、2、3、5、8、13、21、34……
这个也有点不太好想…………

//利用递归求斐波那契数列第几个数是谁(1,1,2,3,5,8,13,21...)
function fb(n) {if (n === 1 || n === 2) {return 1;}return fb(n - 1) + fb(n - 2)
}
console.log(fb(6));

执行过程:

fb(6) 
=> fb(5) + fb(4) 
=> (fb(4) + fb(3)) + (fb(3) + fb(2))
=> ((fb(3) + fb(2)) + (fb(2) + fb(1))) + ((fb(2) + fb(1)) + fb(2))
=> ((fb(2) + fb(1)) + 1) + (1 + 1)) + ((1 + 1) + 1)
=> 3 + 2 + 2 + 1

4.递归遍历数据

先模拟一套后台数据:

let data = [{id: 1,name:'家电',good: [{id: 11,gname: '冰箱'},{id: 12,gname: '洗衣机'}]},{id: 2,name: '服饰'}
]

实现输入id号就返回对应的数据对象,怎么搞?

首先forEach遍历,如果是一级数据id直接输出该对象。
不是一级数据id就要看一下对象里有没有goods数组,有的话要递归调用getId,把goodsid传进去,再输出该对象。

function getId(data,id) {data.forEach(el => {if(el.id === id) {console.log(el);} else if(el.goods) {  //如果有goods数组再递归getId(el.goods, id); //递归调用传入good数组} });
}
getId(data,12);

四、深拷贝和浅拷贝

1、浅拷贝只拷贝一层,对象级别只拷贝地址
2、深拷贝拷贝多层,每一级都会拷贝

1.浅拷贝

先定义两个对象

const obj = {name: 'zzy',age: 18,grade:{math: 100}
}
const copyObj = {}

把obj浅拷贝给copyObj ,有两种方式

//1.通过for...in实现浅拷贝
for(let k in obj) {console.log(k) //'name', 'age'copyObj[k] = obj[k];
}
console.log(copyObj);
//2.通过Object.assign(target, copywho)实现浅拷贝
Object.assign(copyObj,obj);
console.log(copyObj);

由于浅拷贝只拷贝一层,所以objgrade对象拷贝过来的是地址,如果copyObj对象中修改了grade.math,那么obj中的grade.math也会变。

2.手撕深拷贝

手撕深拷贝,利用递归实现

//利用递归手写深拷贝
function deepCopy(newObj, oldObj) {for (let k in oldObj) {//判断属性值属于哪种数据类型//1.如果是数组(判断数组要在对象前,因为数组也是对象)if(oldObj[k] instanceof Array) {newObj[k] = [];//赋值空,然后再把数组里的元素一个一个拷贝进来deepCopy(newObj[k], oldObj[k]); }//2.如果是对象else if(oldObj[k] instanceof Object) {newObj[k] = {};//赋值空,然后再把对象里的属性一个一个拷贝进来deepCopy(newObj[k], oldObj[k]);}//3.如果是普通数据类型,直接浅拷贝else {newObj[k] = oldObj[k];}}return newObj;
}
deepCopy(copyObj, obj);
copyObj.grade.math = 99;
console.log(obj);  //math100
console.log(copyObj); //math99

递归是真tm恶心啊兄弟们

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
一帆风顺二龙腾飞三阳开泰祝福语... 本篇文章极速百科给大家谈谈一帆风顺二龙腾飞三阳开泰祝福语,以及一帆风顺二龙腾飞三阳开泰祝福语结婚对应...
美团联名卡审核成功待激活(美团... 今天百科达人给各位分享美团联名卡审核成功待激活的知识,其中也会对美团联名卡审核未通过进行解释,如果能...