函数式编程 && 命令式编程
函数式编程
把具体的操作过程“封装”到一个函数中,我们无需关注内部是如何处理的(How),只需要关注处理的结果(What)即可;
- 使用便捷,开发效率高
-
命令式编程
具体如何去处理,是由自己实现及掌控的,关注How的过程!
操作灵活,可以自主把控处理的每一个步骤
- 处理性能一般比函数式编程式要好「例如:forEach循环要慢于for循环」
- 总结:处理的数据量“较多”的情况下,使用命令式编程来提高性能!操作逻辑较为复杂,需要自己灵活把控处理步骤的情况下,也使用命令式编程!其余情况,优先推荐函数式编程!
匿名函数具名化
含义:给匿名函数强制设置一个名字 特点:原本应该是匿名函数「例如:自执行函数、函数表达式、回调函数等」,但是我们会为其设置一个名字
- 更符合规范
- 有助于实现递归
-
举例
let arr = [10, 20, 30, 40, 50]
//函数式编程
arr.forEach((item, index) => {
// 本质循环五次
if(index%2===0){
console.log(item, index)
}
})
// 命令式
for (let i = 0; i < arr.length; i += 2) {
// 本质循环三次
console.log(arr[i], i)
}
// 需求:迭代五次
// for (let i = 0; i < 5; i++) { } 命令式编程
new Array(5).fill(null).forEach(() => {//函数式编程
// ...
})
匿名函数
没有名字(或没必要设置名字)的函数,以下:
函数表达式:
const xxx = function(){}
回调函数(或自执行函数):
fn(()=>{ ... })
(function(){ ... })()
.....
(function fn() {
// 在函数产生的私有上下文中,可以使用fn:存储的是当前函数本身
// fn = 100 //且直接修改其值,是不会生效的
// let fn = 100 //但是可以基于其它方式声明一下这个名字,那么此时名字就不再代表函数本身了
console.log('我是自执行函数', fn)
})()
// console.log(fn) //Uncaught ReferenceError: fn is not defined 设置的名字不会被其外部上下文(宿主环境)所声明,所以在外面无法使用!
"use strict"//使用严格模式
let i = 0
~function fn() {
if (i >= 2) return
i++
console.log(i)
// console.log(arguments.callee) //函数本身 BUG:在严格模式下,会报错 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
fn()
}()
堆栈内存
![7}G1{9C9RW(L5HR2M~IO%E_tmb.jpg :::info
- 浏览器:执行 html/css/js 代码
- html/css -> W3C GUI线程
- js -> ECMA-262 JS引擎线程「谷歌的引擎 V8」
- 浏览器内核
- 谷歌:blink「webkit分支」
- Safari:webkit
- 火狐:Gecko
- IE:Trident
- Edge:Chromeium
- 欧朋:之前是 Presto,从14版本后是 webkit
- 国产浏览器:之前是 Trident ,后来是 webkit
- 移动端浏览器:目前都是 webkit
webkit内核的垃圾回收机制:自动回收,浏览器会定期查找没有被使用的内存,把其释放掉!!
IE浏览器用的是引用计数法
:::闭包
let x = 5;
const fn = function fn() {
return function (y) {
console.log(y + (++x));
}
};
let f = fn(6);
f(7);
fn(8)(9);
f(10);
console.log(x);
练习题1
let a = 0,
b = 0;
function A(a) {
A = function (b) {
alert(a + b++);
};
alert(a++);
}
A(1);
A(2)
练习题2
下⾯代码是否可以,每隔1000MS依次输出 0 1 2 3 4 ?如果不可以,说明为啥?以及如何解决? ```javascript for (var i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i);
}, (i + 1) * 1000);
}
解决方案1:把 var 改为 let
```javascript
for (let i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i);
}, (i + 1) * 1000);
}
- 使用 let 会产生块级私有上下文,而在每一轮产生的 私有的块级上下文 中,会创建一个小函数「callback」,其作用域就是本轮产生的私有上下文,把 callback 赋值给了全局的定时器,所以此 块级上私有下文 不会被释放「也就是闭包」
迭代5轮,产生5个闭包,每个闭包中都有私有变量i,分别存储0~4的值
第一轮/闭包1:i = 0
第二轮/闭包2:i = 1
…当定时器到时候,执行 callback 函数的时候,输出的 i 不是 callback 中私有的,则按照作用域链,找到对应的闭包,使用其存储下来的私有 i 的值
=> 这样就实现了:每间隔1秒,分别输出0~4
解决方案2:不基于 let 产生闭包,而是自己把函数执行,让其产生闭包
for (var i = 0; i < 5; i++) {
(function (i) {
setTimeout(function () {
console.log(i)
}, (i + 1) * 1000)
})(i);
}
解决方案3:基于定时器内部的闭包处理机制
for (var i = 0; i < 5; i++) {
setTimeout(
function (i) {
console.log(i)
},
(i + 1) * 1000,
// 定时器一但设置第三个及以后的实参,其内部就会产生一个闭包,先把传递的实参值保存下来
// 当定时器到达时间,执行 callback 函数的时候,会把之前闭包中存储的那些实参值,传递给 callback
i
)
}
回忆选项卡
<div id="box">
<button index="0">按钮1</button>
<button index="1">按钮2</button>
<button index="2">按钮3</button>
</div>
<script>
let buttons = Array.from(document.querySelectorAll('#box button'))
</script>
方案1:基于闭包解决「性能最差」
for (let i = 0; i < buttons.length; i++) {
buttons[i].onclick = function () {
console.log('此按钮索引:', i)
}
}
for (var i = 0; i < buttons.length; i++) {
(function (i) {
buttons[i].onclick = function () {
console.log('此按钮索引:', i)
}
})(i);
}
for (var i = 0; i < buttons.length; i++) {
buttons[i].onclick = (function (i) {
return function () {
console.log('此按钮索引:', i)
}
})(i);
}
//forEach本质也是闭包
buttons.forEach((button, index) => {
button.onclick = function () {
console.log('此按钮索引:', index)
}
})
方案2:自定义属性
let i = 0
for (; i < buttons.length; i++) {
buttons[i].myindex = i
buttons[i].onclick = function () {
console.log('此按钮索引:', this.myindex)
}
}
方案3:事件委托「比上面的两种方式,性能可以提高 40%~60% 以上」
let box = document.querySelector('#box')
box.onclick = function (ev) {
let target = ev.target,
targetTag = target.tagName
if (targetTag === 'BUTTON') {
console.log('此按钮索引:', target.getAttribute('index'))
}
}