谈谈闭包和闭包使用场景
什么是闭包
- 概念闭包是作用域的一种特殊应用
触发闭包的情况
- 函数被当做返回值被返回
- 函数被当做参数被传递
- 自执行匿名函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 情况1 函数被当成参数被返回
function fn(){
const a = 100
return function(){
console.log(a);
}
}
const a = 500
const cd = fn()
cd()
// 情况2:函数被当做参数传递
function fn(cd){
const a= 100
cd()
}
const a = 500
fn(function (){
console.log(a);//500 向外层作用域找
})
//情况3 自执行匿名函数
(function(index){
console.log(index);
})(10)
</script>
</body>
</html>
闭包的应用
- 隐藏变量
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>闭包隐藏变量</title>
</head>
<body>
<script>
function fn(){
const data = {}
return {
set:function(key,val){
data[key] = val
},
get:function(val){
return data[val]
}
}
}
//隐藏了变量data 在外层作用域中无法调用data
let json = fn()
json.set("hello",111)
json.get("hello")
console.log(json.get("hello"));
</script>
</body>
</html>
- 解决for i 的问题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>闭包函数应用场景</title>
<style>
button {
width: 100px;
height: 100px;
}
</style>
</head>
<body>
<button>1</button>
<button>2</button>
<button>3</button>
<button>4</button>
<script>
// 闭包可以解决for...i问题
// 方法1 自执行函数
var btn = document.getElementsByTagName("button")
// for (var i = 0; i < btn.length; i++) {
// (function (i) {
// btn[i].onclick = function () {
// console.log(i);
// }
// })(i)
// }
// 方法2 let方法可以创建块级作用域 拥有自己的作用域
for(let i =0;i<btn.length;i++){
btn[i].onclick = function(){
console.log(i);
}
}
</script>
</body>
</html>
作用域
- 全局作用域 局部作用域
自由变量
- 不在自己作用域里的变量 就是自由变量
- 自由变量的值 在函数定义的地方向上层查找 与函数调用位置无关
javascript是如何执行的
- 自上而下 从左到右一行一行的执行
- 如果中间有一行报错 下面的代码不执行
- 先执行同步代码 再执行异步代码(settimeout、ajax)
event loop
- 同步代码
- 遇到异步 会先记录下代码 (放入到webapi中)等待执行时机(settimeout,ajax)时机到了 将之前的代码放入 callback queue(回调函数调用栈
- 当 call stack 为空(同步代码执行完)event loop开始工作
- Event Loop轮询查找 callback queue(回调函数调用栈)中是否有可执行的代码 如果有 将代码移动到callstack中执行
- event loop如果没有找到可以执行的代码 则会继续轮询查找
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 代码执行顺序:从左到右 从上到下
// 如果有一行报错 后面代码不执行
// 先执行同步代码 再执行异步代码
//event loop过程
// 1.同步代码 一行一行放在call stack(调用栈)中执行
// 2.遇到异步 会先记录下代码 先将异步代码放入到webAPI中 等待执行时机(settimeout ajax)时机到了 将之前的代码放入callback queue(回调函数队列)
// 3.当call stack为空 (同步diamante执行完)event loop开始工作
// 4.event loop轮询查找 是否有可执行的代码 如果有将代码移动到 call stack中执行
// 5.event loop如果没有找到可以执行的代码 则会继续轮询查找
console.log("stark");
setTimeout(()=>{
console.log("settimeout");
},2000)
console.log("end");
</script>
</body>
</html>
么是宏任务和微任务?有什么区别
什么是宏任务 什么是微任务
- 微任务:promise async....await
- 宏任务 settimeout setinterval Ajax Dom
二者的区别
- 先执行微任务 再执行宏任务
宏任务 微任务 Don渲染的关系
- call stack清空 触发event loop
- 执行微任务
- 尝试Dom渲染
- 执行宏任务
完整的event loop过程
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@说人话的前端 - bilibili</title>
<style>
div {
width: 200px;
height: 200px;
border: 5px solid red;
}
</style>
</head>
<body>
<h1>宏任务与微任务</h1>
<div id="div1"></div>
</body>
<script>
console.log("start");
console.log("end");
setTimeout(() => {
// alert("setTimeout");
console.log("setTimeout");
}, 0);
Promise.resolve().then(() => {
// alert("Promise");
console.log("promise");
});
</script>
</html>
手写promise加载图片
出题目的
- 考察image对象
- 考察promise
- 考察async await
知识点
image对象
- new image()————声明一个image对象
- onload ————当图片加载成功时执行
- oneerror ————当图片加载失败时执行
- src ————设置图片路径
promise
- 作用 解决回调地狱的问题
- 语法 :new Promise().then().catch()
async await
- 作用 promise的语法糖 可以增加代码可读性(用同步的思维写代码)
- 语法
(async function(){
try{
const oimg = await loadImg("https://robohash.org/1")
document.body.appendChild(oimg)
const oimg2 = await loadImg("https://robohash.org/2")
document.body.appendChild(oimg2)
}catch(e){
console.log(e);
}
})()
第一种写法
const op = new Promise((resolve,reject)=>{
const img = new Image()
img.src = "https://robohash.org/1"
img.onload = ()=>{
resolve(img)
}
img.onerror = ()=>{
reject("图片加载失败")
}
})
op.then((res)=>{
document.body.append(res)
}).catch((rej)=>{
console.log(rej);
})
第二种写法
function loadImg(oimg) {
return new Promise((resolve, reject) => {
const img = new Image()
img.src = oimg
img.onload = () => {
resolve(img)
}
img.onerror = () => {
reject("图片加载失败")
}
})
}
loadImg("https://robohash.org/1").then((res) => {
document.body.appendChild(res)
return loadImg("https://robohash.org/2")
}).then((res)=>{
document.body.append(res)
})
.catch((rej) => {
console.log(rej);
})
function loadImg(src){
return new Promise((resolve,reject)=>{
const img = new Image()
img.src = src
img.onload = ()=>{
resolve(img)
}
img.onerror = ()=>{
reject("图片加载错误")
}
})
}
(async function(){
try{
const oimg = await loadImg("https://robohash.org/1")
document.body.appendChild(oimg)
const oimg2 = await loadImg("https://robohash.org/2")
document.body.appendChild(oimg2)
}catch(e){
console.log(e);
}
})()
promise阅读代码题
知识点
- JS执行顺序 自上而下 先同步再异步 先微任务再宏任务
- new promise() = promise.resolve()
- then和catch内部没有throw new error相当于resolve
- async function 单独使用相当于返回promise.resolve
- await后面的代码都是异步的
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Promise阅读代码</title>
</head>
<body>
<script>
// Promise.resolve().then(()=>{
// console.log(1);
// }).catch(()=>{
// console.log(2);
// })//输出结果 1
/*
考点1
then和catch内部没有 throw new error 相当于 resolve
*/
// Promise.resolve().then(() => {
// console.log(1);
// }).catch(() => {
// console.log(2);
// }).then(() => {
// console.log(3);
// })
// 变种2
// Promise.resolve().then(()=>{
// console.log(1);
// throw new Error()
// }).catch(()=>{
// console.log(3);
// }).then(()=>{
// console.log(2);
// })//输出结果 1 3 2
// 变种3
// Promise.reject()
// .then(()=>{
// console.log(1);
// }).catch(()=>{
// console.log(2);
// }).then(()=>{
// console.log(3);
// }) //输出结果 2 3
//变种 4
// Promise.reject()
// .then(()=>{
// console.log(1);
// }).catch(()=>{
// console.log(2);
// throw new Error()
// }).then(()=>{
// console.log(3);
// }) //输出结果2
// .catch(()=>{
// console.log(4);
// })//输出结果 2 4
// 考点2 async function =>相当于返回一个promise.resolve
// async function fn(){
// return 100
// }
// (async function(){
// const a = fn()
// const b = await fn()
// console.log(a);//async 单独使用返回一个promise对象
// console.log(b);//await async和await一起使用表示返回的值
// })()
// fn().then(()=>{
// console.log(0);
// }).catch(()=>{
// console.log(1);
// })
// 考点3 代码执行顺序
// async function fn1(){
// console.log("fn1 start");
// await fn2();
// console.log("fn1 end");
// }
// async function fn2(){
// console.log("fn2 start");
// }
// console.log("start");
// fn1()
// console.log("end");
/*
执行顺序:
1.start
2.fn1 start
3.fn2 start
4.end
5.fn1 end
*/
// 变种1
async function fn1() {
console.log("fn1 start");
await fn2()
console.log("fn1 end");
await fn3()
console.log("fn3 end");
}
async function fn2() {
console.log("fn2");
}
async function fn3() {
console.log("fn3");
}
console.log("start");
fn1()
console.log("end");
//await后面的代码全是异步
/*
执行结果:
1.start
2.fn1 start
3.fn2
4.end
5.fn1 end
6.fn3
7.fn3 end
*/
//考点4 promise与settimeout执行顺序
// console.log("start");
// settimeout(()=>{
// console.log("settimeout");
// })//宏任务
// console.log("end");
//执行结果 start end settimeout
// 变种1
async function fn1() {
console.log("fn1 start");
await fn2()
console.log("fn1 end");//微任务
}
async function fn2() {
console.log("fn2");
}
console.log("start");
setTimeout(() => {
console.log("settimeout");
})//宏任务
fn1()
console.log("end");
/*
执行结果
1.start
2.fn1 start
3.fn2
4.end
5.fn1 end
6.settimeout
*/
</script>
</body>
</html>
for..in、for..of 有什么区别?
- for...in遍历后得到key 遍历可枚举数据:数组 字符串 对象
- for...of遍历后得到value 遍历可迭代数据:数组 set map 字符串
是否可枚举
Object.getOwnPropertyDescriptors(obj)===>enumerble:true
是否可迭代
arrsymbol.iterator ===>next()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//for...in遍历得到key 可枚举的数据:数组 字符串 对象
//for...of得到的是value 可迭代的数据:数组 字符串 set map
const arr = [10, 20, 30]
for (let key in arr) {
console.log(key);
}
for (let val of arr) {
console.log(val);
}
// 是否可枚举
// Object.getOwnPropertyDescriptor(Array)
//是否可迭代
// arr[Symbol.iterator]() -> next()
const obj = {
name: "abc",
age: 20
}
for (let key in obj) {
console.log(obj[key]);
}
const set = new Set([10, 20, 30])
for (let val of set) {
console.log(val);
}
const map = new Map([
["a",10],
["b",20],
["c",30],
])
for(let val in map){
console.log(val);
}
</script>
</body>
</html>