Day3
数组补充:arr.reduce()方法可用于数组求和,第一个参数为一个匿名函数【匿名函数的参数第一个为存储总和的值,第二个为当前遍历内容】,第二个参数为初始值。
对象补充
- Object.fromEntries(array)能够将数组转化为对象,但是前提是要以key: value形式
函数
Rest参数(就是
...
)有点像我们当时学的解构一样,…也可以接收任意数量的参数
function sumAll(...args) { // 数组名为 args let sum = 0; for (let arg of args) sum += arg; return sum; } alert( sumAll(1) ); // 1 alert( sumAll(1, 2) ); // 3 alert( sumAll(1, 2, 3) ); // 6
但是Rest参数必须放在函数参数的末尾,否则没有意义
箭头函数没有this,当然也没有arguments
,arguments很像rest但是其是类数组,没数组的方法Spread语法
也为
...
的语法,作用是 展平原因:Spread 语法内部使用了迭代器来收集元素,与
for..of
的方式相同。注意:不过
Array.from(obj)
和[...obj]
存在一个细微的差别:
Array.from
适用于类数组对象也适用于可迭代对象。- Spread 语法只适用于可迭代对象。
用于: 1. 复制array和object、替换原先的Object.assign方法
变量作用域/ 闭包
对于
if
,for
和while
等,在{...}
(代码块)中声明的变量也仅在内部可见:闭包:**闭包 是指一个函数可以记住其外部变量并可以访问这些变量。**JavaScript 中的函数会自动通过隐藏的
[[Environment]]
属性记住创建它们的位置,所以它们都可以访问外部变量。可以从词法环境进行理解,访问变量是一层一层的沿着词法环境寻找
var和const / let 的区别
var
与let/const
有两个主要的区别:
var
声明的变量没有块级作用域,它们仅在当前函数内可见,或者全局可见(如果变量是在函数外声明的)。var
变量声明在函数开头就会被处理(脚本启动对应全局变量)【变量提升】。全局对象(var,但是不推荐)
// 将当前用户信息全局化,以允许所有脚本访问它 window.currentUser = { name: "John" }; // 代码中的另一个位置 alert(currentUser.name); // John // 或者,如果我们有一个名为 "currentUser" 的局部变量 // 从 window 显式地获取它(这是安全的!) alert(window.currentUser.name); // John
函数对象NFE
function sayHi() { alert("Hi"); // 计算调用次数 sayHi.counter++; } sayHi.counter = 0; // 初始值 sayHi(); // Hi sayHi(); // Hi alert( `Called ${sayHi.counter} times` ); // Called 2 times // 请注意函数对象属性和函数的局部变量不一样
一种新的函数定义方式:
let sayHi = function func(who) { if (who) { alert(`Hello, ${who}`); } else { func("Guest"); // 现在一切正常 } }; let welcome = sayHi; sayHi = null; welcome(); // Hello, Guest(嵌套调用有效)
这种方式主要是针对函数内部嵌套的,这样会防止外部对函数进行更改导致的错误。同时func函数是在函数内部作用域内的不会影响外部作用域,也不会被外部看见
new Function创建函数
let func = new Function ([arg1, arg2, ...argN], functionBody); 语法格式
使用
new Function
创建的函数,它的[[Environment]]
指向全局词法环境,而不是函数所在的外部词法环境。因此,我们不能在new Function
中直接使用外部变量。不过这样是好事,这有助于降低我们代码出错的可能。调度(setTimeOut/setInterval)
setTimeOut是让一个函数在指定时间之后进行执行,setInterval是指定函数在重复的时间间隔一致被调用
(1) setTimeOut
function sayHi() { alert('Hello'); } setTimeout(sayHi, 1000); ------------------ function sayHi(phrase, who) { alert( phrase + ', ' + who ); } setTimeout(sayHi, 1000, "Hello", "John"); // Hello, John
但请注意在此函数中传的被执行的函数是函数的引用,因此不要在后面带括号
清除定时器的函数为
clearTimeOut(id)
,在这个函数的参数为定时器id(2) setInterval
与settimeout一致,也有clearinterval(id),不同就是这个玩意会重复执行,同时setinterval和settimeout并不会冲突。当这两个使用alert进行弹窗时,即使alert显示了计时器也还在计时。
(3)settimeout实现setinterval一样的效果【!!!命名内部函数然后就嵌套settimeout,在内部需要命名函数】
let aa = setTimeout(function ss() { alert("hello") setTimeout(ss, 1000) }, 1000)
// 实际场景:我们要实现一个服务(server),每间隔 5 秒向服务器发送一个数据请求,但如果服务器过载了,那么就要降低请求频率,比如将间隔增加到 10、20、40 秒等。用这个方式比interval好很多 let delay = 5000; let timerId = setTimeout(function request() { ...发送请求... if (request failed due to server overload) { // 下一次执行的间隔是当前的 2 倍 delay *= 2; } timerId = setTimeout(request, delay); }, delay);
(4)嵌套的
setTimeout
相较于setInterval
能够更精确地设置两次执行之间的延时 后者是开始调用与开始调用直接间隔为所设置的时间,这就导致了函数执行时间也会占用所设置的时间,导致间隔时间会小于所设置的时间。但是前者是函数执行完毕以后才会调用,是真正的间隔时间。
(5)零延时调度
setTimeout(func, 0)
(与setTimeout(func)
相同)用来调度需要尽快执行的调用,但是会在当前脚本执行完成后进行调用。// 示例 function printNumbers(from, to) { let current = from; let time = setTimeout(function go() { alert(current) if (current < to) { time = setTimeout(go, 1000); } current++ }, 1000); } printNumbers(0, 4)
防抖与节流
function debounce(fn, time) { let timer return function () { clearTimeout(timer) timer = setTimeout(() => { fn.apply(this, arguments) }, time); } } function thro(fn, delay) { let timer = null return function () { if (!timer) { timer = setTimeout(() => { fn.apply(this, arguments) timer = null }, delay); } } }
防抖与节流
function debounce(fn, time) { let timer return function () { clearTimeout(timer) timer = setTimeout(() => { fn.apply(this, arguments) }, time); } } function thro(fn, delay) { let timer = null return function () { if (!timer) { timer = setTimeout(() => { fn.apply(this, arguments) timer = null }, delay); } } }