前言
我是歌谣 放弃很容易 但是坚持一定很酷 微信公众号关注前端小歌谣带你进入前端巅峰交流群 今天继续对前端知识的小结
异步和同步转换第二种
<!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>
async function fangfang() {
return new Promise((resolve)=>{
setTimeout(() => {
resolve("我给你钱")
},1000)
})
}
function geyao() {
return "我去买东西"
}
async function test() {
let qian=await fangfang();
let dongxi=await geyao();
console.log(qian,"qian")
console.log(dongxi,"dongxi")
}
test()
</script>
</body>
</html>
弹出a标签
<!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>
var a=document.getElementsByTagName("a");
for(var i=0;i<a.length;i++){
a[i].onclick=(function(i){
console.log(i)
})(i)
}
</script>
</body>
</html>
强制缓存
//强制缓存
<!-- Cache-Control: 在 Response Headers 中。 控制强制缓存的逻辑。 例如
Cache-Control: max-age=3153600(单位是秒) Cache-Control 有哪些值:
max-age:缓存最大过期时间。
no-cache:可以在客户端存储资源,每次都必须去服务端做新鲜度校验,来决定从服务端获取新的资源(200)还是使用客户端缓存(304)。
no-store:永远都不要在客户端存储资源,永远都去原始服务器去获取资源。 -->
//协商缓存
<!-- 服务端缓存策略。 服务端判断客户端资源,是否和服务端资源一样。
一致则返回 304,否则返回 200 和最新的资源。 资源标识: 在 Response
Headers 中,有两种。 Last-Modified:资源的最后修改时间。
Etag:资源的唯一标识(一个字符串,类似于人类的指纹)。 Last-Modified:
服务端拿到 if-Modified-Since
之后拿这个时间去和服务端资源最后修改时间做比较,如果一致则返回 304
,不一致(也就是资源已经更新了)就返回 200 和新的资源及新的
Last-Modified。 Etag: 其实 Etag 和 Last-Modified 一样的,只不过 Etag
是服务端对资源按照一定方式(比如
contenthash)计算出来的唯一标识,就像人类指纹一样,传给客户端之后,客户端再传过来时候,服务端会将其与现在的资源计算出来的唯一标识做比较,一致则返回
304,不一致就返回 200 和新的资源及新的 Etag。 两者比较: 优先使用 Etag。
Last-Modified 只能精确到秒级。 如果资源被重复生成,而内容不变,则 Etag
强缓存和协商缓存
<!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>
// 缓存可分为强缓存和协商缓存。
// 浏览器进行资源请求时,会判断response headers是否命中强缓存,如果命中,直接从本地读取缓存,不会向服务器发送请求,
// 当强缓存没有命中时,会发送请求到服务端,判断协商缓存是否命中,如果命中,服务器将请求返回,不会返回资源,告诉浏览器从本地读取缓存。如何不命中,服务器直接返回资源
// 区别: 强缓存命中,不会请求服务器,直接请求缓存;协商缓存命中,会请求服务器,不会返回内容,然后读取缓存;
// 强缓存又分为Expires 和 Cache-Control
// Expires,该值是一个GMT时间格式个字符串,浏览器进行第一次请求时,服务器会在返回头部加上Expires,下次请求,如果在这个时间之前则命中缓存,
// app.get('/', (req, res) => {
// const cssContent = path.join(__dirname, './html/index.html')
// fs.readFile(cssContent, function (err, data) {
// res.setHeader(
// 'Expires',
// new Date(Date.now() + 2592000000).toUTCString()
// )
// res.end(data)
// })
// })
// Cache-Control ,该值是利用max-age判断缓存的生命周期,是以秒为单位,如何在生命周期时间内,则命中缓存
// app.get('/', (req, res) => {
// const cssContent = path.join(__dirname, './html/index.html');
// fs.readFile(cssContent, function(err, data) {
// res.setHeader("Cache-Control", "max-age=0");
// res.end(data);
// })
// });
// 协商缓存
// 协商缓存利用Last-Modified , If-Modified-Since 和 ETag , If-None-Match来实现
// Last-Modified , If-Modified-Since
// Last-Modified: 表示为为实体头部部分,response返回,表示为资源的最后更新时间
// If-Modified-Since:通过比较两次的时间判断,资源在请求期间是否有修改,假如没有修改,则命中协商缓存,浏览器从缓存中读取资源,如果没有命中,资源有过修改,返回新的Last-Modified时间和服务器资源
// app.get('/', (req, res) => {
// const cssContent = path.join(__dirname, './html/index.html')
// fs.stat(cssContent, (err, start) => {
// if (req.headers['if-modified-since'] === start.mtime.toUTCString()) {
// res.writeHead(304, 'Not Modified');
// res.end();
// } else {
// fs.readFile(cssContent, function (err, data) {
// let lastModified = start.mtime.toUTCString();
// res.setHeader('Last-Modified', lastModified);
// res.writeHead(200, 'OK');
// res.end(data);
// })
// }
// })
// });
// 复制代码ETag , If-None-Match
// 有些情况下仅判断最后修改日期来验证资源是否有改动是不够的:
// 1,存在周期性重写某些资源,但资源实际包含的内容并无变化;
// 2,被修改的信息并不重要,如注释等;
// 3,Last-Modified无法精确到毫秒,但有些资源更新频率有时会小于一秒。
// ETag:为相应头部字段,表示资源内容的唯一标识,随服务器response返回;
// If-None-Match: 服务器比较请求头中的If-None-Match和当前资源中的etag是否一致,来判断资源是否修改过,如果没有修改,则命中缓存,浏览器从缓存中读取资源,如果修改过,服务器会返回新的etag,并返回资源;
// app.get('/home', (req, res) => {
// const cssContent = path.join(__dirname, './html/index.html')
// fs.stat(cssContent, (err, start) => {
// let etag = md5(cssContent);
// if (req.headers['if-none-match'] === etag) {
// res.writeHead(304, 'Not Modified');
// res.end();
// } else {
// fs.readFile(cssContent, function (err, data) {
// res.setHeader('Etag', etag);
// res.writeHead(200, 'OK');
// res.end(data);
// })
// }
// })
// });
</script>
</body>
</html>
性能优化
代码层面: 1防抖和节流(resize,scroll,input)。 减少回流(重排)和重绘。 事件委托。 css 放 ,js 脚本放 最底部。 减少 DOM 操作。 按需加载,比如 React 中使用 React.lazy 和 React.Suspense ,通常需要与 webpack 中的 splitChunks 配合。 构建方面: 压缩代码文件,在 webpack 中使用 terser-webpack-plugin 压缩 Javascript 代码;使用 css-minimizer-webpack-plugin 压缩 CSS 代码;使用 html-webpack-plugin 压缩 html 代码。 开启 gzip 压缩,webpack 中使用 compression-webpack-plugin ,node 作为服务器也要开启,使用 compression。 常用的第三方库使用 CDN 服务,在 webpack 中我们要配置 externals,将比如 React, Vue 这种包不打倒最终生成的文件中。而是采用 CDN 服务。 其它: 使用 http2。因为解析速度快,头部压缩,多路复用,服务器推送静态资源。 使用服务端渲染。 图片压缩。 使用 http 缓存,比如服务端的响应中添加 Cache-Control / Expires 。
手写apply
<!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>手写apply</title>
</head>
<body>
<script>
Function.prototype.myApply = function (context, args) {
//这里默认不传就是给window,也可以用es6给参数设置默认参数
context = context || window
args = args ? args : []
//给context新增一个独一无二的属性以免覆盖原有属性
const key = Symbol()
context[key] = this
//通过隐式绑定的方式调用函数
const result = context[key](...args)
//删除添加的属性
delete context[key]
//返回函数调用的返回值
return result
}
var name = 'geyao'
var fangfang = {
name: 'fangfang',
fang: function () {
console.log(this.name)
},
fun: function () {
console.log(this,"this")
setTimeout(
function () {
this.fang()
}.myApply(this),
100
)
},
}
fangfang.fun() //fangfang
</script>
</body>
</html>
手写bind
<!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>手写bind</title>
</head>
<body>
<script>
Function.prototype.mybind = function (target) {
//target:改变返回函数执行时的this指向
var obj = target || window;
console.log(arguments,"arguments")
var args = [].slice.call(arguments, 1); //获取bind时传入的绑定实参
var args1 = [].slice.call(arguments, 0); //获取bind时传入的绑定实参
console.log(args,"args")
console.log(args1,"args1")
var self = this; //要bind的函数
console.log(self,"self")
var _fn = function () {
var _args = [].slice.call(arguments, 0); //新函数执行时传递的实际参数
console.log(_args,"_args")
return self.apply(obj, args.concat(_args));
}
return _fn
}
var name = 'fangfang';
var obj = {
name: 'geyao'
};
function fn(a, b, c) {
console.log(a + b + c + this.name);
return a + b + c + this.name
};
fn.mybind(obj,"我的","老婆","是")(); // 我的老婆是geyao
</script>
</body>
</html>
手写bind2
<!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>手写call</title>
</head>
<body>
<script>
Function.prototype.mybind = function (target) {
console.log(arguments,"arguments")
var args = [].slice.call(arguments, 1); //获取bind时传入的绑定实参
console.log(args,"args")
var self = this; //要bind的函数
console.log(this,"this")
var temp = function(){};//作为中间函数,用于实现继承
//target不存在this默认window,当new调用时无需修改this指向
var obj = this instanceof temp ? this : (target || window);
var _fn = function () {
var _args = [].slice.call(arguments, 0); //新函数执行时传递的实际参数
console.log(_args,"_args")
console.log(args.concat(_args),"args.concat(_args)")
return self.apply(obj, args.concat(_args));
}
//让中间函数的原型指向,要bind函数的原型
temp.prototype = self.protoype;
//让新函数的原型指向中间temp的对象,然后找到要bind函数的原型
_fn.prototype = new temp();//这样新函数生成的对象的constructor就能找到旧的函数
return _fn
}
var name = 'fangfang';
var obj = {
name: 'geyao'
};
function fn(a, b, c) {
console.log(a + b + c + this.name);
return a + b + c + this.name
};
fn.mybind(obj)(1,2,3); // 我的老婆是geyao
</script>
</body>
</html>
手写call
<!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>手写call</title>
</head>
<body>
<script>
Function.prototype.myCall = function () {
//如果是undefined和null 则指向window 不是对象用object转为对象
let params = Array.from(arguments) //得到所以实参数组
let _obj = params.splice(0, 1)[0] //获取第一位作为对象,即this指向
//Symbol()表示独一无二的值
_obj.fn = this
var result = obj.fn(...params)
//删除属性
delete obj.fn
return result
}
var name = 'fangfang'
var obj = {
name: 'geyao',
}
function fn(a, b, c) {
console.log(a + b + c + this.name)
return a + b + c + this.name
}
fn.myCall(obj, '我的', '老婆', '是') // 我的老婆是geyao
</script>
</body>
</html>
手写call
<!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>手写call</title>
</head>
<body>
<script>
Function.prototype.mycall = function (obj) {
//判断是否为null或者undefined,如果是则默认window;如果传递的参数不是对象用Object转为对象
obj = obj ? Object(obj) : window
var args = []
//注意此处for循环的i是从1开始,因为第1个参数是obj
for (var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments' + '[' + i + ']')
}
console.log(args, 'args') //[ "arguments[1]", "arguments[2]", "arguments[3]" ]
//最重要的一步!说明了两个问题:1.mycall内部的this是指向调用者fn函数(对象的);2.Obj.func就是fn函数,
// obj对象调用了fn函数,因此fn函数内部的this指向obj
console.log(this, 'this') //function fn(a, b, c)
obj.fn = this
// console.log(obj.fn(args),"obj.fn")
// console.log(eval("obj.fn("+args+")"),"eval")
var result = eval('obj.fn(' + args + ')')
delete obj.fn
console.log(result, 'result') // 我的老婆是geyao
return result
}
var name = 'fangfang'
var obj = {
name: 'geyao',
}
function fn(a, b, c) {
console.log(a + b + c + this.name)
return a + b + c + this.name
}
fn.mycall(obj, '我的', '老婆', '是') // 我的老婆是geyao
</script>
</body>
</html>
总结
我是歌谣 最好的种树是十年前 其次是现在 加油 歌谣