1.new的运行机制
new Animal("cat") = { var obj = {}; obj.__proto__ = Animal.prototype; var result = Animal.call(obj,"cat"); return typeof result === 'object'? result : obj; }
2.JS中判断数组的方法
一共六种:
arr instanceof Array 存在问题: instanceof 假定只有一个全局执行的环境。如果网页中包含多个框架,那实际上就存在两个以上不同的全局执行环境,从而存在两个以上不同版本的Array构造函数。如果你从一个框架向另一个框架传入一个数组,那么传入的数组与在第二个框架中原生创建的数组分别具有不同的构造函数 |
arr.constructor === Array |
Array.prototype.isPrototypeOf(arr) isPrototypeOf() 用于测试一个对象是否存在于另一个对象的原型链上 |
Object.getPrototypeOf(arr) === Array.prototype Object.getPrototypeOf() 方法返回指定对象的原型 |
Object.prototype.toString.call(arr) === '[object Array] 虽然Array也继承自Object,但js在Array.prototype上重写了toString,而我们通过toString.call(arr)实际上是通过原型链调用了。 |
Array.isArray(arr) |
3.手写instanceof
function myInstanceof (target,origin){ const proto = target.__proto__; if(proto){ if(origin.prototype === proto ){ return true }else{ return myInstanceof(proto,origin) } }else { return false } }
4.cookie
cookie用来和服务端通讯 其内容是字符串的形式,通过;分割 形式为:'a=100;b=200'
它本质上是一小段文本信息,它存储于访问者的计算机中,每当同一台计算机通过浏览器请求某个页面时,就会发送这个 cookie。它是「浏览器」提供的一种机制,
它将 document 对象的cookie 属性提供给 JavaScript,可以使用JavaScript来创建和取回 cookie 的值,因此我们可以通过document.cookie访问它。
cookie是存于用户硬盘的一个文件,这个文件通常对应于一个域名,也就是说,cookie可以跨越一个域名下的多个网页,但不能跨越多个域名使用。
它将 document 对象的cookie 属性提供给 JavaScript,可以使用JavaScript来创建和取回 cookie 的值,因此我们可以通过document.cookie访问它。
cookie是存于用户硬盘的一个文件,这个文件通常对应于一个域名,也就是说,cookie可以跨越一个域名下的多个网页,但不能跨越多个域名使用。
cookie中帮助Web 站点保存有关访问者的信息。如:保存用户登录信息 创建购物车 跟踪用户行为
cookie起作用的方法是:用户每次访问站点时,Web应用程序都可以读取 Cookie 包含的信息。当用户再次访问这个站点时,浏览器就会在本地硬盘上查找与该 URL 相关联的 Cookie。如果该 Cookie 存在,浏览器就将它添加到request header的Cookie字段中,与http请求一起发送到该站点。
domain 和 path 共同决定了cookie能被哪些页面共享 | domain 参数是用来控制 cookie对「哪个域」有效, 默认为设置 cookie的那个域。 path用来控制cookie发送的指定域的「路径」, 默认为"/",表示指定域下的所有路径都能访问。 |
expires/max-age 决定cookie的生命周期 | expires 表示的是失效时间,准确讲是「时刻」 max-age表示的是生效的「时间段」,以「秒」为单位。 |
secure | 默认情况为空,不指定 secure 选项, 即不论是 http 请求还是 https 请求,均会发送cookie。 指定后,cookie只有在使用SSL连接 (如HTTPS请求或其他安全协议请求的)时才会发送到服务器。 |
httponly(即http) | 禁止javascript读取,如果cookie中的一个参数带有httponly 则这个参数将不能被javascript获取;httponly可以防止xss会话劫持攻击。 以增强cookie的安全性。 (由于cookie中可能存放身份验证信息,放在cookie中容易泄***r /> 默认情况是不指定 httponly,即可以通过 js 去访问。 |
5.如何隐藏一个元素
display:none; 位置都没了
visibility:hidden 仅仅是不可见 位置仍旧存在
(vue 和 React 的方法明天补上)
6.ES5实现继承
有下面两个类,下面实现Man继承People:]
function People() { this.type = 'prople' } People.prototype.eat = function () { console.log('吃东西啦'); } function Man(name) { this.name = name; this.color = 'black'; }
有一下几种方法:
原型继承:
Man.prototype = new People();但原型是所有子类实例共享的,改变一个其他也会改变
构造继承:
function Man(name) { People.call(this); }不能继承父类原型,函数在构造函数中,每个子类实例不能共享函数,浪费内存
组合继承:
function Man(name) { People.call(this); } Man.prototype = People.prototype;父类原型和子类原型是同一个对象,无法区分子类真正是由谁构造
寄生组合继承:
在组合继承的基础上,子类继承一个由父类原型生成的空对象。
function Man(name) { People.call(this); } Man.prototype = Object.create(People.prototype, { constructor: { value: Man } })手写inherits函数:
function inherits(ctor, superCtor) { ctor.super_ = superCtor; ctor.prototype = Object.create(superCtor.prototype, { constructor: { value: ctor, enumerable: false, writable: true, configurable: true } }); };使用:
function Man() { People.call(this); //... } inherits(Man, People); Man.prototype.fun = ...
当然ES6中最方便:
class Animal { constructor (type) { this.type = type } walk () { console.log(`I am walking`) } static eat () { console.log(`I am eating`) } } class Dog extends Animal { constructor () { super('dog') } run () { console.log('I can run') } }
7.匿名函数
匿名函数就是没有名字的函数
之前在函数表达式和函数声明对比那里学过
赋值给变量的函数就是匿名函数
一下是一些查找的资料,直接贴过来了:
————————————————————
//匿名函数在其它应用场景括号可以省略 (function (){ //由于没有执行该匿名函数,所以不会执行匿名函数体内的语句。 console.log("啊啊啊啊"); }) 如果需要执行匿名函数,在匿名函数后面加上一个括号即可立即执行! (function (){ //此时会输出啊啊啊啊啊 console.log("啊啊啊啊啊"); })()匿名函数的应用场景:
事件 | <input type="button" value="点我啊!" id="sub"> <script> //获得按钮元素 var sub=document.querySelector("#sub"); //给按钮增加点击事件。 sub.onclick=function(){ alert("当点击按钮时会执行到我哦!"); } </script> |
对象 | var obj={ name:"啊啊啊", age:18, fn:function(){ return "我叫"+this.name+"今年"+this.age+"岁了!"; } }; console.log(obj.fn());//我叫啊啊啊今年18岁了! |
函数表达式 | //将匿名函数赋值给变量fn。 var fn=function(){ return "我是一只小小小小留下,怎么飞也飞不高!" } |
回调函数 | setInterval(function(){ console.log("我其实是一个回调函数,每次1秒钟会被执行一次"); },1000); |
闭包 | function fn(){ //返回匿名函数 return function(){ return "啊啊啊"; } } |
8.JS如何冻结对象,即对象属性只读
Object.freeze() 冻结指的是不能向这个对象添加新的属性,不能修改其已有属性的值,不能删除已有属性,以及不能修改该对象已有属性的可枚举性、可配置性、可写性。也就是说,这个对象永远是不可变的。该方法返回被冻结的对象。
const foo = Object.freeze({age:18}) foo.age = 19 //"use strict"模式下还会报错 console.log(foo.age) // 18
9.js中为什么0.1+0.2≠0.3?怎么实现等于0.3?
因为在JavaScript中的二进制的浮点数0.1和0.2并不是十分精确,在他们相加的结果并非正好等于0.3,而是一个比较接近的数字 0.30000000000000004 ,所以条件判断结果为false。
可以使用第三方的库来计算
可以通过来设置误差范围值来实现0.3
ES6中提供了一个属性:Number.EPSILON
function numbersequal(a,b){ return Math.abs(a-b)<Number.EPSILON; } var a=0.1+0.2, b=0.3; console.log(numbersequal(a,b)); //true
10.普通函数和箭头函数的区别
除了形式上的却别意外,箭头函数本质上就是匿名函数
因为箭头函数是匿名函数 所以不能作为构造函数 不能使用new
不能绑定arguments,但是可以使用rest参数...来解决
不绑定this 捕获上下文的this
箭头函数没有原型属性
11.script标签中async和defer
<script src="path/to/myModule.js" defer></script> <script src="path/to/myModule.js" async></script>
这是两种允许script异步加载的语法
两者区别是:
defer要等到整个页面在内存中正常渲染结束(DOM 结构完全生成,以及其他脚本执行完成),才会执行;
async一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染。
一句话,defer是“渲染完再执行”,async是“下载完就执行”。
另外,如果有多个defer脚本,会按照它们在页面出现的顺序加载,而多个async脚本是不能保证加载顺序的。
12.斐波那契数列
var fib = function(n) { let fibonacci = [0,1]; for(let i = 2; i <= n; i ++) { fibonacci[i] = (fibonacci[i - 1] + fibonacci[i - 2]) % (1e9 +7); } return fibonacci[n]; };
13.CDN是什么
CDN的本质就是缓存 将媒体资源 动静态图片 HTML 等内容缓存到距离我们更近的IDC中,从而让用户进行共享资源,实现缩短站点间的响应时间等等许久
14.JSONP
AJAX请求必须满足同源,而在Web页面上调用js文件时则不受是否跨域的影响(不仅如此,我们还发现凡是拥有”src”这个属性的标签都拥有跨域的能力,比如<\script>、<\img>、<\iframe>)
所以,可以在远程服务器上设法把数据装进js格式的文件里,供客户端调用和进一步处理。而且JSON这种纯字符数据格式可以简洁的描述复杂数据
最终得出解决方案——————web客户端通过与调用脚本一模一样的方式,来调用跨域服务器上动态生成的js格式文件(一般以JSON为后缀),显而易见,服务器之所以要动态生成JSON文件,目的就在于把客户端需要的数据装入进去。客户端对这个JSON文件调用成功之后,也就获得了自己所需的数据,剩下的就是按照自己需求进行处理和展现了
这种获取远程数据的方式看起来非常像AJAX,但其实并不一样————————
ajax和jsonp这两种技术在调用方式上”看起来”很像,目的也一样,都是请求一个url,然后把服务器返回的数据进行处理,因此jquery和ext等框架都把jsonp作为ajax的一种形式进行了封装。 |
但ajax和jsonp其实本质上是不同的东西。ajax的核心是通过XmlHttpRequest获取非本页内容,而jsonp的核心则是动态添加 |
最终,形成了一种非正式传输协议,也就是JSONP——该协议的一个要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。
简单例子:
客户端请求:
服务端:
因此,本地函数可以被跨域的远程js调用,并且还接收到了远程js带来的数据。
但如何让远程JS知道它应该调用的本地函数叫什么名字呢?————script的src可以使用参数指定 服务端的JS脚本就可以动态拼接了
比如:
<script type="text/javascript"> // 得到航班信息查询结果后的回调函数 var flightHandler = function(data){ alert('你查询的航班结果是:票价 ' + data.price + ' 元,' + '余票 ' + data.tickets + ' 张。'); }; // 提供jsonp服务的url地址(不管是什么类型的地址,最终生成的返回值都是一段javascript代码) var url = "http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998&callback=flightHandler"; // 创建script标签,设置其属性 var script = document.createElement('script'); script.setAttribute('src', url); // 把script标签加入head,此时调用开始 document.getElementsByTagName('head')[0].appendChild(script); </script>其中 在调用的url中传递code参数:告诉服务器我要查的是CA1998次航班的信息, 传递callback参数:告诉服务器,我的本地回调函数叫做flightHandler,所以请把查询结果传入这个函数中进行调用。
服务器就可以依照url生成一段这样的代码提供给客户端:
flightHandler({ "code": "CA1998", "price": 1780, "tickets": 5 })剩下的就是如何把代码封装一下,以便于与用户界面交互,从而实现多次和重复调用,比如使用jQuery:
jQuery(document).ready(function(){ $.ajax({ type: "get", async: false, url: "http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998", dataType: "jsonp", jsonp: "callback",//传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(一般默认为:callback) jsonpCallback:"flightHandler",//自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名,也可以写"?",jQuery会自动为你处理数据 success: function(json){ alert('您查询到航班信息:票价: ' + json.price + ' 元,余票: ' + json.tickets + ' 张。'); }, error: function(){ alert('fail'); } }); })使用jQuery时,不需要再写出回调函数,jquery会自动帮你生成回调函数并把数据取出来供success属性方法来调用
15.CORS
CORS是服务端的操作,通过设置http的header
出于安全原因,浏览器限制从脚本内发起的跨源HTTP请求。 例如,XMLHttpRequest和Fetch API遵循同源策略。
这意味着使用这些API的Web应用程序只能从加载应用程序的同一个域请求HTTP资源,除非响应报文包含了正确CORS响应头。
说白了!客户端想要提起跨域,就是要设置头!设置了头就行!
//指定服务器端允许进行跨域资源访问的来源域。 //可以用通配符*表示允许任何域的JavaScript访问资源, //但是在响应一个携带身份信息(Credential)的HTTP请求时,必需指定具体的域,不能用通配符 ctx.set("Access-Control-Allow-Origin", "http://127.0.0.1:8080"); //指定服务器允许进行跨域资源访问的请求方法列表,一般用在响应预检请求上 ctx.set("Access-Control-Allow-Methods", "OPTIONS,POST,GET,HEAD,DELETE,PUT"); //必需。指定服务器允许进行跨域资源访问的请求头列表,一般用在响应预检请求上 ctx.set("Access-Control-Allow-Headers", "x-requested-with, accept, origin, content-type"); //告诉客户端返回数据的MIME的类型,这只是一个标识信息,并不是真正的数据文件的一部分 ctx.set("Content-Type", "application/json;charset=utf-8"); //可选,单位为秒,指定浏览器在本次预检请求的有效期内,无需再发送预检请求进行协商, //直接用本次协商结果即可。 //当请求方法是PUT或DELETE等特殊方法或者Content-Type字段的类型是application/json时, //服务器会提前发送一次请求进行验证 ctx.set("Access-Control-Max-Age", 300); //可选。它的值是一个布尔值, //表示是否允许客户端跨域请求时携带身份信息(Cookie或者HTTP认证信息)。 //默认情况下,Cookie不包括在CORS请求之中。 //当设置成允许请求携带cookie时,需要保证"Access-Control-Allow-Origin"是服务器有的域名, //而不能是"*";如果没有设置这个值,浏览器会忽略此次响应。 ctx.set("Access-Control-Allow-Credentials", true); //可选。跨域请求时, //客户端xhr对象的getResponseHeader()方法只能拿到6个基本字段, //Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。 //要获取其他字段时,使用Access-Control-Expose-Headers,xhr.getResponseHeader('myData') //可以返回我们所需的值 ctx.set("Access-Control-Expose-Headers", "myData");
16.XMLHttpRequest和fetch
fetch是一项新的技术 同 XMLHttpRequest 非常类似,都是用来做网络请求。但是同复杂的XMLHttpRequest的API相比,fetch使用了Promise,这让它使用起来更加简洁,从而避免陷入”回调地狱”。
基本使用:
fetch('./api/some.json') .then(function(res) { if (res.status !== 200) { console.log('Looks like there was a problem. Status Code: ' + res.status); return; } // 处理响应中的文本信息 res.json().then(function(data) { console.log(data); }); }) .catch(function(err) { console.log('Fetch Error : %S', err); })但是 Promises缺少了一些重要的XMLHttpRequest的使用场景 比如:使用XMLHttpRequest你可以模拟进度(监听progress事件),也可以取消请求(使用abort()方法) 而fetch不可以
17.localStorage sessionStorage
不会随着http被发送出去
可以通过setItem 和 getIem的API进行操作
18.Restful API
是一种API设计方法 把每个url当作是唯一的资源
那么如何设计成资源:
尽量不用url参数 | (像一个方法) (是唯一的资源) |
用method表示操作类型 | 传统的: (此时还是把url当作功能 post就表示提交数据 get就表示获取数据) restful api: (此时用method来表示操作类型) |
简单来说:RESTful就是用URL定位资源,用HTTP的methods(GET,POST,DELETE,DETC)描述操作。
比如: DELETE http://api.qc.com/v1/friends: 删除某人的好友 (在http parameter指定好友id) POST http://api.qc.com/v1/friends: 添加好友 UPDATE http://api.qc.com/v1/profile: 更新个人资料 |
19.懒加载
比如新浪微博的图片加载 滑动到哪里 加载到哪里
简单版本:
preview.png是预览图 data-realsrc才是真实的图片地址
具体分析一下懒加载的原理:
一张图片就是一个<img>标签,而图片的来源主要是src属性。浏览器是否发起亲求就是根据是否有src属性决定的。既然这样,那么我们就要对<img>标签的src属性下手了,在没进入可视区域的时候,我们先不给这个<img>标签赋src属性,这样岂不是浏览器就不会发送请求了。
所以:我们知道了从<img>标签下手,那么更重要的就是可视区域的判断了,
实现步骤:
首先要了解几个需要用的API
document.documentElement.clientHeight//获取屏幕可视区域的高度 element.offsetTop//获取元素相对于文档顶部的高度 document.documentElement.scrollTop //获取浏览器窗口顶部与文档顶部之间的距离,也就是滚动条滚动的距离通过这三个API获得了三个值:可视区高度、元素相对于其父元素容器顶部的距离、浏览器窗口顶部与容器元素顶部的距离也就是滚动条滚动的高度。
如图所示:
也就是说:
offsetTop-scroolTop<clientHeight,则图片进入了可视区内,则被请求。
总结一下:
懒加载有两个要点
1.判断图片是否进入可视区域
2.操作图片的src 给src赋值一个预览图地址
图片:
<img src="loading.gif" data-src="https://cdn.pixabay.com/photo/2015/09/09/16/05/forest-931706_1280.jpg" alt=""> <img src="loading.gif" data-src="https://cdn.pixabay.com/photo/2014/08/01/00/08/pier-407252_1280.jpg" alt=""> <img src="loading.gif" data-src="https://cdn.pixabay.com/photo/2014/12/15/17/16/pier-569314_1280.jpg" alt=""> <img src="loading.gif" data-src="https://cdn.pixabay.com/photo/2010/12/13/10/09/abstract-2384_1280.jpg" alt=""> <img src="loading.gif" data-src="https://cdn.pixabay.com/photo/2015/10/24/11/09/drop-of-water-1004250_1280.jpg"
懒加载函数:
function lazyload() { //监听页面滚动事件 var seeHeight = window.innerHeight; //可见区域高度 var scrollTop =document.documentElement.scrollTop||document.body.scrollTop; //滚动条距离顶部高度 for (var i = n; i < img.length; i++) { console.log(img[i].offsetTop, seeHeight, scrollTop); if (img[i].offsetTop < seeHeight + scrollTop) { if (img[i].getAttribute("src") == "loading.gif") { img[i].src = img[i].getAttribute("data-src"); } n = i + 1; } } }
var img = document.getElementsByTagName("img"); var n = 0; //存储图片加载到的位置,避免每次都从第一张图片开始遍历 lazyload(); //页面载入完毕加载可是区域内的图片 // 节流函数,保证每200ms触发一次 function throttle(event, time) { let timer = null; return function (...args) { if (!timer) { timer = setTimeout(() => { timer = null; event.apply(this, args); }, time); } } } window.addEventListener('scroll', throttle(lazyload, 200))
20.再写节流和防抖
// 防抖function debounce(fn, delay = 500) {// timer 是闭包中的 不会对外暴露let timer = nullreturn function () {//返回的是个函数哦!!!if (timer) {clearTimeout(timer)}timer = setTimeout(() => {fn.apply(this, arguments) //进行绑定timer = null}, delay)}}const input1 = document.getElementById('input1')input1.addEventListener('keyup', debounce(function (e) {console.log(e.target)console.log(input1.value)}, 600))
// 节流 function throttle(fn, delay = 100) { let timer = null //同理,也是闭包 return function () { if (timer) { return } timer = setTimeout(() => { fn.apply(this, arguments) //进行绑定 timer = null }, delay) } } div1.addEventListener('drag', throttle(function (e) { console.log(e.offsetX, e.offsetY) },200))
21.XSS攻击
XSS Cross Site Scripting 跨站脚本攻击 | 比如: 发表一篇博客,在其中嵌入<script>脚本 用脚本内容获取cookie 发送到自己的服务器 用这种方式就能收割访问者cookie | 预防: 将特殊字符通通替换 如把 < 变为 < > 变为 > 这样 <script>就变成了 < script> 会直接显示在页面上 而不是作为脚本运行 (可以使用xss工具做替换) |
<script>alert(“hey!you are attacked”)</script>那么留言板界面的网页代码就会变成形如以下:
<html> <head> <title>留言板</title> </head> <body> <div id=”board” <script>alert(“hey!you are attacked”)</script> </div> </body> </html>那么这个时候问题就来了,当浏览器解析到用户输入的代码那一行时会发生什么呢?答案很显然,浏览器并不知道这些代码改变了原本程序的意图,会照做弹出一个信息框。
说白了,XSS攻击就是想办法“教唆”用户的浏览器去执行一些这个网页中原本不存在的前端代码,从而窃取网页浏览中的cookie值(这个时候就可以通过设置httponlu 来保证同一cookie不能被滥用)或者劫持流量实现恶意跳转
那么如何避免呢:
- 首先是过滤。对诸如<script>、<img>、<a>等标签进行过滤。
- 其次是编码。像一些常见的符号,如<>在输入的时候要对其进行转换编码,这样做浏览器是不会对该标签进行解释执行的,同时也不影响显示效果。
- 最后是限制。通过以上的案例我们不难发现xss攻击要能达成往往需要较长的字符串,因此对于一些可以预期的输入可以通过限制长度强制截断来进行防御。
22.CSRF攻击
XSRF Cross-site request forgery 跨站请求伪造 | 你正在购物 看重某个商品 id100 付费接口是/pay?id=100 但没有任何验证 我攻击你 给你发送一封邮件 邮件中隐藏着 <img src = /pay?id=200/> 你一看邮件 就帮我买了 | 预防: 使用post接口 增加验证(密码 验证码 指纹) |
具体流程为:
- 受害者登录http://a.com,并保留了登录凭证(Cookie)。
- 攻击者引诱受害者访问了http://b.com。
- http://b.com 向 http://a.com 发送了一个请求:http://a.com/act=xx。浏览器会默认携带http://a.com的Cookie。
- http://a.com接收到请求后,对请求进行验证,并确认是受害者的凭证,误以为是受害者自己发送的请求。
- http://a.com以受害者的名义执行了act=xx。
- 攻击完成,攻击者在受害者不知情的情况下,冒充受害者,让http://a.com执行了自己定义的操作。
由于
- CSRF(通常)发生在第三方域名。
- CSRF攻击者不能获取到Cookie等信息,只是使用。
- 阻止不明外域的访问
- 同源检测
- Samesite Cookie
- 提交时要求附加本域才能获取的信息
- CSRF Token
- 双重Cookie验证
23.查找、添加、删除、移动DOM节点
查找 | getElementsByTagName() //通过标签名称 getElementsByName() //通过元素的Name属性的值(IE容错能力较强,会得到一个数组,其中包括id等于name值的) getElementById() //通过元素Id,唯一性 等等等 |
添加 | <div id="app"></div> <script> let app = document.querySelector('#app') let span = document.createElement('span') span.innerHTML = '1111' app.appendChild(span) </script> 注:若使用appendChild添加已有节点,则会起到移动的效果 |
删除 | 可以先获取父元素:parentElement 再移除它自己removeChild() |
移动 | 使用appendChild添加已有节点,则会起到移动的效果 |
24.手写字符串trim方法
String.prototype.trim = function(){ return this.replace(/^\s+/,'').replace(/\s+$/,'') }
25.如何捕获JS中的异常
手动捕获:try...catch
自动捕获:window.onerror
1 | window.onerror = function(message, source, lineno, colno, error) { ... } |
- 对于跨域的JS 如CDN的,不会有详细的报错信息
- 对于压缩的JS 还要配合sourceMap 反差到未压缩代码的行和列
26.获取url参数
传统方式 | 查找location.search function query(name) { const search = location.search.substr(1) //截取一下 // search: 'a=10&b=20&c=30' const reg = new RegExp(`(^|&)${name}=([^&]*)(&|$)`, 'i') //i 表示大小写不区分 const res = search.match(reg) //没匹配到 if (res === null) { return null } return res[2]//res[2]才是要取的参数 } query('b')//20 ^|&)${name}=([^&]*)(&|$)`[^&]:排异 不要用&符号 (&|$):&或者结尾 |
新API | URLSearchParam // URLSearchParams function query(name) { const search = location.search const p = new URLSearchParams(search) return p.get(name) } console.log( query('b') ) |
接下来进一步将url解析为JS
传统方法通过拆分来实现
27.插入1000个DOM如何优化
<ul id="root"></ul> <script> var root = document.getElementById('root') var fragment = document.createDocumentFragment() for(let i = 0; i < 1000; i++){ let li = document.createElement('li') li.innerHTML = '我是li标签' fragment.appendChild(li) } root.appendChild(fragment); </script>createDocumentFragment是创建一个文档片段 可以把他看作是游离在DOM树以外
28.requestAnimationFrame
window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行
function animate() { curWidth = curWidth + 3 $div1.css('width', curWidth) if (curWidth < maxWidth) { window.requestAnimationFrame(animate) // 时间不用自己控制 } } animate()
29.vue路由问题
先回顾一下网页url的组成部分:
前端路由有两种模式:hash 和 h5
hash | hash 虽然出现在 URL 中,但不会被包含在 http 请求中 对后端完全没有影响,因此改变 hash 不会重新加载页面。 也就是:
hash的变化有三种方式:
通过window.onhashchange这个API监听hash变化 window.onhashchange = (event) => { console.log('old url', event.oldURL) console.log('new url', event.newURL) console.log('hash:', location.hash) } |
h5 | history 利用了 html5 history interface 中 新增的 pushState() 和 replaceState() 方法 这两个方法应用于浏览器记录栈, 在当前已有的 back、forward、go 基础之上, 它们提供了对历史记录修改的功能。只是当它们执行修改时, 虽然改变了当前的 URL ,但浏览器不会立即向后端发送请求。 也就是:
切换路由的API 【注意】用 pushState 方式,浏览器不会刷新页面 const state = { name: 'page1' } console.log('切换路由到', 'page1') history.pushState(state, '', 'page1')监听浏览器前进后退的API window.onpopstate = (event) => { // 重要!! console.log('onpopstate', event.state, location.pathname) }浏览器前进后退的时候会执行函数 后端只返回一个html的主文件 所有的路由跳转都由前端来做 |
路由模式 | hash模式 H5 history H5需要serve端配合无论如何都会返回一个html文件 而不是一个特定的文件 所以不会返回404错误 需要处理一下 |
路由配置 | 动态配置: 懒加载:和之前异步组件非常类似 |
30.$nextTick用法
31.proxy
原本的object.defineproxy需要对每个代理的属性都编写对应的getter setter 需要一次性递归 而且把无法监听新增属性
使用proxy可以解决object.defineproxy存在的问题 不调用不递归 什么时候调用这一层了 才递归
32.回流和重绘
回流:在渲染阶段,将dom tree和cssom结合起来 形成render tree 除此以外,还需要计算它们在设备视口(viewport)内的确切位置和大小,这个计算的阶段就是回流。比如,一个div的width设置为50% ,回流这个阶段,我们就需要根据视口具体的宽度,将其转为实际的像素值
重绘:过构造渲染树和回流阶段,我们知道了哪些节点是可见的,以及可见节点的样式和具体的几何信息(位置、大小),那么我们就可以将渲染树的每个节点都转换为屏幕上的实际像素,这个阶段就叫做重绘节点。
当当页面布局和几何信息发生变化的时候,就需要回流。比如以下情况:
- 添加或删除可见的DOM元素
- 元素的位置发生变化
- 元素的尺寸发生变化(包括外边距、内边框、边框大小、高度和宽度等)
- 内容发生变化,比如文本变化或图片被另一个不同尺寸的图片所替代。
- 页面一开始渲染的时候(这肯定避免不了)
- 浏览器的窗口尺寸变化(因为回流是根据视口的大小来计算元素的位置和大小的)
33.http https TCU
必须要记住!!http不存在连接这个概念,只有请求和响应,它的产物是数据包,因此需要一个传输的通道,就是TCP连接 说白了,http请求就是在TCP连接的基础上发送的,并且一个TCP连接中允许发送多次请求
http流程:
- 客户端发送请求报文
- 服务器处理请求
- 服务器访问web资源
- 构造应答
- 发送应答
https是在HTTP基础上,形成了HTTPS:https://<主机>:<端口>/<路径> 端口不在是80 而是443
它在应用层和传输层之间有一个子层:SSL(安全套接层)
- 提供数据安全和数据完整服务
- 对传输层数据进行加密后传输
SSL安全参数握手(重点) | 协商加密和解密的密钥 具体过程如下: |
- 从443端口进行TCP连接
- SSL生成密钥加密传输
- 客户端发送数据
- 服务端发送数据