网络劫持有哪几种,如何防备?
⽹络劫持分为两种:
(1)DNS劫持: (输⼊京东被强制跳转到淘宝这就属于dns劫持)
- DNS强制解析: 通过批改运营商的本地DNS记录,来疏导⽤户流量到缓存服务器
- 302跳转的⽅式: 通过监控⽹络出⼝的流量,分析判断哪些内容是能够进⾏劫持解决的,再对劫持的内存发动302跳转的回复,疏导⽤户获取内容
(2)HTTP劫持: (拜访⾕歌然而⼀直有贪玩蓝⽉的⼴告),因为http明⽂传输,运营商会批改你的http响应内容(即加⼴告)
DNS劫持因为涉嫌守法,曾经被监管起来,当初很少会有DNS劫持,⽽http劫持仍然⾮常盛⾏,最无效的方法就是全站HTTPS,将HTTP加密,这使得运营商⽆法获取明⽂,就⽆法劫持你的响应内容。
寄生组合继承
题目形容:实现一个你认为不错的 js 继承形式
实现代码如下:
function Parent(name) { this.name = name; this.say = () => { console.log(111); };}Parent.prototype.play = () => { console.log(222);};function Children(name) { Parent.call(this); this.name = name;}Children.prototype = Object.create(Parent.prototype);Children.prototype.constructor = Children;// let child = new Children("111");// // console.log(child.name);// // child.say();// // child.play();
节流与防抖
- 函数防抖 是指在事件被触发 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则从新计时。这能够应用在一些点击申请的事件上,防止因为用户的屡次点击向后端发送屡次申请。
- 函数节流 是指规定一个单位工夫,在这个单位工夫内,只能有一次触发事件的回调函数执行,如果在同一个单位工夫内某事件被触发屡次,只有一次能失效。节流能够应用在 scroll 函数的事件监听上,通过事件节流来升高事件调用的频率。
// 函数防抖的实现function debounce(fn, wait) { var timer = null; return function() { var context = this, args = arguments; // 如果此时存在定时器的话,则勾销之前的定时器从新记时 if (timer) { clearTimeout(timer); timer = null; } // 设置定时器,使事件间隔指定事件后执行 timer = setTimeout(() => { fn.apply(context, args); }, wait); };}// 函数节流的实现;function throttle(fn, delay) { var preTime = Date.now(); return function() { var context = this, args = arguments, nowTime = Date.now(); // 如果两次工夫距离超过了指定工夫,则执行函数。 if (nowTime - preTime >= delay) { preTime = Date.now(); return fn.apply(context, args); } };}
异步任务调度器
形容:实现一个带并发限度的异步调度器 Scheduler,保障同时运行的工作最多有 limit
个。
实现:
class Scheduler { queue = []; // 用队列保留正在执行的工作 runCount = 0; // 计数正在执行的工作个数 constructor(limit) { this.maxCount = limit; // 容许并发的最大个数 } add(time, data){ const promiseCreator = () => { return new Promise((resolve, reject) => { setTimeout(() => { console.log(data); resolve(); }, time); }); } this.queue.push(promiseCreator); // 每次增加的时候都会尝试去执行工作 this.request(); } request() { // 队列中还有工作才会被执行 if(this.queue.length && this.runCount < this.maxCount) { this.runCount++; // 执行先退出队列的函数 this.queue.shift()().then(() => { this.runCount--; // 尝试进行下一次工作 this.request(); }); } }}// 测试const scheduler = new Scheduler(2);const addTask = (time, data) => { scheduler.add(time, data);}addTask(1000, '1');addTask(500, '2');addTask(300, '3');addTask(400, '4');// 输入后果 2 3 1 4
写代码:实现函数可能深度克隆根本类型
浅克隆:
function shallowClone(obj) { let cloneObj = {}; for (let i in obj) { cloneObj[i] = obj[i]; } return cloneObj;}
深克隆:
- 思考根底类型
援用类型
- RegExp、Date、函数 不是 JSON 平安的
- 会失落 constructor,所有的构造函数都指向 Object
- 破解循环援用
function deepCopy(obj) { if (typeof obj === 'object') { var result = obj.constructor === Array ? [] : {}; for (var i in obj) { result[i] = typeof obj[i] === 'object' ? deepCopy(obj[i]) : obj[i]; } } else { var result = obj; } return result;}
代码输入后果
function runAsync (x) { const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000)) return p}Promise.race([runAsync(1), runAsync(2), runAsync(3)]) .then(res => console.log('result: ', res)) .catch(err => console.log(err))
输入后果如下:
1'result: ' 123
then只会捕捉第一个胜利的办法,其余的函数尽管还会继续执行,然而不是被then捕捉了。
参考 前端进阶面试题具体解答
Cookie、LocalStorage、SessionStorage区别
浏览器端罕用的存储技术是 cookie 、localStorage 和 sessionStorage。
- cookie: 其实最开始是服务器端用于记录用户状态的一种形式,由服务器设置,在客户端存储,而后每次发动同源申请时,发送给服务器端。cookie 最多能存储 4 k 数据,它的生存工夫由 expires 属性指定,并且 cookie 只能被同源的页面访问共享。
- sessionStorage: html5 提供的一种浏览器本地存储的办法,它借鉴了服务器端 session 的概念,代表的是一次会话中所保留的数据。它个别可能存储 5M 或者更大的数据,它在以后窗口敞开后就生效了,并且 sessionStorage 只能被同一个窗口的同源页面所访问共享。
- localStorage: html5 提供的一种浏览器本地存储的办法,它个别也可能存储 5M 或者更大的数据。它和 sessionStorage 不同的是,除非手动删除它,否则它不会生效,并且 localStorage 也只能被同源页面所访问共享。
下面几种形式都是存储大量数据的时候的存储形式,当须要在本地存储大量数据的时候,咱们能够应用浏览器的 indexDB 这是浏览器提供的一种本地的数据库存储机制。它不是关系型数据库,它外部采纳对象仓库的模式存储数据,它更靠近 NoSQL 数据库。
点击刷新按钮或者按 F5、按 Ctrl+F5 (强制刷新)、地址栏回车有什么区别?
- 点击刷新按钮或者按 F5: 浏览器间接对本地的缓存文件过期,然而会带上If-Modifed-Since,If-None-Match,这就意味着服务器会对文件查看新鲜度,返回后果可能是 304,也有可能是 200。
- 用户按 Ctrl+F5(强制刷新): 浏览器不仅会对本地文件过期,而且不会带上 If-Modifed-Since,If-None-Match,相当于之前素来没有申请过,返回后果是 200。
- 地址栏回车: 浏览器发动申请,依照失常流程,本地查看是否过期,而后服务器查看新鲜度,最初返回内容。
Cookie有哪些字段,作用别离是什么
Cookie由以下字段组成:
- Name:cookie的名称
- Value:cookie的值,对于认证cookie,value值包含web服务器所提供的拜访令牌;
- Size: cookie的大小
- Path:能够拜访此cookie的页面门路。 比方domain是abc.com,path是
/test
,那么只有/test
门路下的页面能够读取此cookie。 - Secure: 指定是否应用HTTPS平安协定发送Cookie。应用HTTPS平安协定,能够爱护Cookie在浏览器和Web服务器间的传输过程中不被窃取和篡改。该办法也可用于Web站点的身份甄别,即在HTTPS的连贯建设阶段,浏览器会查看Web网站的SSL证书的有效性。然而基于兼容性的起因(比方有些网站应用自签订的证书)在检测到SSL证书有效时,浏览器并不会立刻终止用户的连贯申请,而是显示平安危险信息,用户仍能够抉择持续拜访该站点。
- Domain:能够拜访该cookie的域名,Cookie 机制并未遵循严格的同源策略,容许一个子域能够设置或获取其父域的 Cookie。当须要实现单点登录计划时,Cookie 的上述个性十分有用,然而也减少了 Cookie受攻打的危险,比方攻击者能够借此动员会话定置攻打。因此,浏览器禁止在 Domain 属性中设置.org、.com 等通用顶级域名、以及在国家及地区顶级域下注册的二级域名,以减小攻打产生的范畴。
- HTTP: 该字段蕴含
HTTPOnly
属性 ,该属性用来设置cookie是否通过脚本来拜访,默认为空,即能够通过脚本拜访。在客户端是不能通过js代码去设置一个httpOnly类型的cookie的,这种类型的cookie只能通过服务端来设置。该属性用于避免客户端脚本通过document.cookie
属性拜访Cookie,有助于爱护Cookie不被跨站脚本攻打窃取或篡改。然而,HTTPOnly的利用仍存在局限性,一些浏览器能够阻止客户端脚本对Cookie的读操作,但容许写操作;此外大多数浏览器仍容许通过XMLHTTP对象读取HTTP响应中的Set-Cookie头。 - Expires/Max-size : 此cookie的超时工夫。若设置其值为一个工夫,那么当达到此工夫后,此cookie生效。不设置的话默认值是Session,意思是cookie会和session一起生效。当浏览器敞开(不是浏览器标签页,而是整个浏览器) 后,此cookie生效。
总结: 服务器端能够应用 Set-Cookie 的响应头部来配置 cookie 信息。一条cookie 包含了5个属性值 expires、domain、path、secure、HttpOnly。其中 expires 指定了 cookie 生效的工夫,domain 是域名、path是门路,domain 和 path 一起限度了 cookie 可能被哪些 url 拜访。secure 规定了 cookie 只能在确保安全的状况下传输,HttpOnly 规定了这个 cookie 只能被服务器拜访,不能应用 js 脚本拜访。
单例模式
用意:保障一个类仅有一个实例,并提供一个拜访它的全局拜访点。
次要解决:一个全局应用的类频繁地创立与销毁。
何时应用:当您想管制实例数目,节俭系统资源的时候。
如何解决:判断零碎是否曾经有这个单例,如果有则返回,如果没有则创立。
实现:
var Singleton = (function() { // 如果在外部申明 SingletonClass 对象,则无奈在内部间接调用 var SingletonClass = function() { }; var instance; return function() { // 如果已存在,则返回 instance if(instance) return instance; // 如果不存在,则new 一个 SingletonClass 对象 instance = new SingletonClass(); return instance; }})();// 测试var a = new Singleton();var b = new Singleton();console.log(a === b); // true
原型
构造函数是一种非凡的办法,次要用来在创建对象时初始化对象。每个构造函数都有prototype(原型)(箭头函数以及Function.prototype.bind()没有)属性,这个prototype(原型)属性是一个指针,指向一个对象,这个对象的用处是蕴含特定类型的所有实例共享的属性和办法,即这个原型对象是用来给实例对象共享属性和办法的。每个实例对象的__proto__都指向这个构造函数/类的prototype属性。面向对象的三大个性:继承/多态/封装对于new操作符:1. new执行的函数, 函数外部默认生成了一个对象2. 函数外部的this默认指向了这个new生成的对象3. new执行函数生成的这个对象, 是函数的默认返回值ES5例子:function Person(obj) { this.name = obj.name this.age= obj.age}// 原型办法Person.prototype.say = function() { console.log('你好,', this.name )}// p为实例化对象,new Person()这个操作称为构造函数的实例化let p = new Person({name: '番茄', age: '27'})console.log(p.name, p.age)p.say()ES6例子:class Person{ constructor(obj) { this.name = obj.name this.age= obj.age } say() { console.log(this.name) }}let p = new Person({name: 'ES6-番茄', age: '27'})console.log(p.name, p.age)p.say()
代码输入后果
const async1 = async () => { console.log('async1'); setTimeout(() => { console.log('timer1') }, 2000) await new Promise(resolve => { console.log('promise1') }) console.log('async1 end') return 'async1 success'} console.log('script start');async1().then(res => console.log(res));console.log('script end');Promise.resolve(1) .then(2) .then(Promise.resolve(3)) .catch(4) .then(res => console.log(res))setTimeout(() => { console.log('timer2')}, 1000)
输入后果如下:
script startasync1promise1script end1timer2timer1
代码的执行过程如下:
- 首先执行同步带吗,打印出script start;
- 遇到定时器timer1将其退出宏工作队列;
- 之后是执行Promise,打印出promise1,因为Promise没有返回值,所以前面的代码不会执行;
- 而后执行同步代码,打印出script end;
- 继续执行上面的Promise,.then和.catch冀望参数是一个函数,这里传入的是一个数字,因而就会产生值浸透,将resolve(1)的值传到最初一个then,间接打印出1;
- 遇到第二个定时器,将其退出到微工作队列,执行微工作队列,按程序顺次执行两个定时器,然而因为定时器工夫的起因,会在两秒后先打印出timer2,在四秒后打印出timer1。
代码输入后果
console.log('1');setTimeout(function() { console.log('2'); process.nextTick(function() { console.log('3'); }) new Promise(function(resolve) { console.log('4'); resolve(); }).then(function() { console.log('5') })})process.nextTick(function() { console.log('6');})new Promise(function(resolve) { console.log('7'); resolve();}).then(function() { console.log('8')})setTimeout(function() { console.log('9'); process.nextTick(function() { console.log('10'); }) new Promise(function(resolve) { console.log('11'); resolve(); }).then(function() { console.log('12') })})
输入后果如下:
176824359111012
(1)第一轮事件循环流程剖析如下:
- 整体script作为第一个宏工作进入主线程,遇到
console.log
,输入1。 - 遇到
setTimeout
,其回调函数被散发到宏工作Event Queue中。暂且记为setTimeout1
。 - 遇到
process.nextTick()
,其回调函数被散发到微工作Event Queue中。记为process1
。 - 遇到
Promise
,new Promise
间接执行,输入7。then
被散发到微工作Event Queue中。记为then1
。 - 又遇到了
setTimeout
,其回调函数被散发到宏工作Event Queue中,记为setTimeout2
。
宏工作Event Queue | 微工作Event Queue |
---|---|
setTimeout1 | process1 |
setTimeout2 | then1 |
上表是第一轮事件循环宏工作完结时各Event Queue的状况,此时曾经输入了1和7。发现了process1
和then1
两个微工作:
- 执行
process1
,输入6。 - 执行
then1
,输入8。
第一轮事件循环正式完结,这一轮的后果是输入1,7,6,8。
(2)第二轮工夫循环从**setTimeout1**
宏工作开始:
- 首先输入2。接下来遇到了
process.nextTick()
,同样将其散发到微工作Event Queue中,记为process2
。 new Promise
立刻执行输入4,then
也散发到微工作Event Queue中,记为then2
。
宏工作Event Queue | 微工作Event Queue |
---|---|
setTimeout2 | process2 |
then2 |
第二轮事件循环宏工作完结,发现有process2
和then2
两个微工作能够执行:
- 输入3。
- 输入5。
第二轮事件循环完结,第二轮输入2,4,3,5。
(3)第三轮事件循环开始,此时只剩setTimeout2了,执行。
- 间接输入9。
- 将
process.nextTick()
散发到微工作Event Queue中。记为process3
。 - 间接执行
new Promise
,输入11。 - 将
then
散发到微工作Event Queue中,记为then3
。
宏工作Event Queue | 微工作Event Queue |
---|---|
process3 | |
then3 |
第三轮事件循环宏工作执行完结,执行两个微工作process3
和then3
:
- 输入10。
- 输入12。
第三轮事件循环完结,第三轮输入9,11,10,12。
整段代码,共进行了三次事件循环,残缺的输入为1,7,6,8,2,4,3,5,9,11,10,12。
代码输入问题
window.number = 2;var obj = { number: 3, db1: (function(){ console.log(this); this.number *= 4; return function(){ console.log(this); this.number *= 5; } })()}var db1 = obj.db1;db1();obj.db1();console.log(obj.number); // 15console.log(window.number); // 40
这道题目看清起来有点乱,然而实际上是考查this指向的:
- 执行db1()时,this指向全局作用域,所以window.number 4 = 8,而后执行匿名函数, 所以window.number 5 = 40;
- 执行obj.db1();时,this指向obj对象,执行匿名函数,所以obj.numer * 5 = 15。
僵尸过程和孤儿过程是什么?
- 孤儿过程:父过程退出了,而它的一个或多个过程还在运行,那这些子过程都会成为孤儿过程。孤儿过程将被init过程(过程号为1)所收养,并由init过程对它们实现状态收集工作。
- 僵尸过程:子过程比父过程先完结,而父过程又没有开释子过程占用的资源,那么子过程的过程描述符依然保留在零碎中,这种过程称之为僵死过程。
手写题:Promise 原理
class MyPromise { constructor(fn) { this.callbacks = []; this.state = "PENDING"; this.value = null; fn(this._resolve.bind(this), this._reject.bind(this)); } then(onFulfilled, onRejected) { return new MyPromise((resolve, reject) => this._handle({ onFulfilled: onFulfilled || null, onRejected: onRejected || null, resolve, reject, }) ); } catch(onRejected) { return this.then(null, onRejected); } _handle(callback) { if (this.state === "PENDING") { this.callbacks.push(callback); return; } let cb = this.state === "FULFILLED" ? callback.onFulfilled : callback.onRejected; if (!cb) { cb = this.state === "FULFILLED" ? callback.resolve : callback.reject; cb(this.value); return; } let ret; try { ret = cb(this.value); cb = this.state === "FULFILLED" ? callback.resolve : callback.reject; } catch (error) { ret = error; cb = callback.reject; } finally { cb(ret); } } _resolve(value) { if (value && (typeof value === "object" || typeof value === "function")) { let then = value.then; if (typeof then === "function") { then.call(value, this._resolve.bind(this), this._reject.bind(this)); return; } } this.state === "FULFILLED"; this.value = value; this.callbacks.forEach((fn) => this._handle(fn)); } _reject(error) { this.state === "REJECTED"; this.value = error; this.callbacks.forEach((fn) => this._handle(fn)); }}const p1 = new Promise(function (resolve, reject) { setTimeout(() => reject(new Error("fail")), 3000);});const p2 = new Promise(function (resolve, reject) { setTimeout(() => resolve(p1), 1000);});p2.then((result) => console.log(result)).catch((error) => console.log(error));
script标签中defer和async的区别
如果没有defer或async属性,浏览器会立刻加载并执行相应的脚本。它不会期待后续加载的文档元素,读取到就会开始加载和执行,这样就阻塞了后续文档的加载。
defer 和 async属性都是去异步加载内部的JS脚本文件,它们都不会阻塞页面的解析,其区别如下:
- 执行程序: 多个带async属性的标签,不能保障加载的程序;多个带defer属性的标签,依照加载程序执行;
- 脚本是否并行执行:async属性,示意后续文档的加载和执行与js脚本的加载和执行是并行进行的,即异步执行;defer属性,加载后续文档的过程和js脚本的加载(此时仅加载不执行)是并行进行的(异步),js脚本须要等到文档所有元素解析实现之后才执行,DOMContentLoaded事件触发执行之前。
陈说http
基本概念:HTTP,全称为 HyperText Transfer Protocol,即为超文本传输协定。是互联网利用最为宽泛的一种网络协议所有的 www 文件都必须恪守这个规范。http个性:HTTP 是无连贯无状态的HTTP 个别构建于 TCP/IP 协定之上,默认端口号是 80HTTP 能够分为两个局部,即申请和响应。http申请:HTTP 定义了在与服务器交互的不同形式,最罕用的办法有 4 种别离是 GET,POST,PUT, DELETE。URL 全称为资源描述符,能够这么认为:一个 URL 地址对应着一个网络上的资源,而 HTTP 中的 GET,POST,PUT,DELETE 就对应着对这个资源的查问,批改,削减,删除4个操作。HTTP 申请由 3 个局部形成,别离是:状态行,申请头(Request Header),申请注释。HTTP 响应由 3 个局部形成,别离是:状态行,响应头(Response Header),响应注释。HTTP 响应中蕴含一个状态码,用来示意服务器对客户端响应的后果。状态码个别由3位形成:1xx : 示意申请曾经承受了,持续解决。2xx : 示意申请曾经解决掉了。3xx : 重定向。4xx : 个别示意客户端有谬误,申请无奈实现。5xx : 个别为服务器端的谬误。比方常见的状态码:200 OK 客户端申请胜利。301 Moved Permanently 申请永恒重定向。302 Moved Temporarily 申请长期重定向。304 Not Modified 文件未修改,能够间接应用缓存的文件。400 Bad Request 因为客户端申请有语法错误,不能被服务器所了解。401 Unauthorized 申请未经受权,无法访问。403 Forbidden 服务器收到申请,然而回绝提供服务。服务器通常会在响应注释中给出不提供服务的起因。404 Not Found 申请的资源不存在,比方输出了谬误的URL。500 Internal Server Error 服务器产生不可预期的谬误,导致无奈实现客户端的申请。503 Service Unavailable 服务器以后不可能解决客户端的申请,在一段时间之后,服务器可能会恢复正常。大略还有一些对于http申请和响应头信息的介绍。
代码输入后果
function a() { console.log(this);}a.call(null);
打印后果:window对象
依据ECMAScript262标准规定:如果第一个参数传入的对象调用者是null或者undefined,call办法将把全局对象(浏览器上是window对象)作为this的值。所以,不论传入null 还是 undefined,其this都是全局对象window。所以,在浏览器上答案是输入 window 对象。
要留神的是,在严格模式中,null 就是 null,undefined 就是 undefined:
'use strict';function a() { console.log(this);}a.call(null); // nulla.call(undefined); // undefined
对requestAnimationframe的了解
实现动画成果的办法比拟多,Javascript 中能够通过定时器 setTimeout 来实现,CSS3 中能够应用 transition 和 animation 来实现,HTML5 中的 canvas 也能够实现。除此之外,HTML5 提供一个专门用于申请动画的API,那就是 requestAnimationFrame,顾名思义就是申请动画帧。
MDN对该办法的形容:
window.requestAnimationFrame() 通知浏览器——你心愿执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该办法须要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行。
语法: window.requestAnimationFrame(callback);
其中,callback是下一次重绘之前更新动画帧所调用的函数(即下面所说的回调函数)。该回调函数会被传入DOMHighResTimeStamp参数,它示意requestAnimationFrame() 开始去执行回调函数的时刻。该办法属于宏工作,所以会在执行完微工作之后再去执行。
勾销动画: 应用cancelAnimationFrame()来勾销执行动画,该办法接管一个参数——requestAnimationFrame默认返回的id,只须要传入这个id就能够勾销动画了。
劣势:
- CPU节能:应用SetTinterval 实现的动画,当页面被暗藏或最小化时,SetTinterval 依然在后盾执行动画工作,因为此时页面处于不可见或不可用状态,刷新动画是没有意义的,齐全是节约CPU资源。而RequestAnimationFrame则齐全不同,当页面解决未激活的状态下,该页面的屏幕刷新工作也会被零碎暂停,因而跟着零碎走的RequestAnimationFrame也会进行渲染,当页面被激活时,动画就从上次停留的中央继续执行,无效节俭了CPU开销。
- 函数节流:在高频率事件( resize, scroll 等)中,为了避免在一个刷新距离内产生屡次函数执行,RequestAnimationFrame可保障每个刷新距离内,函数只被执行一次,这样既能保障流畅性,也能更好的节俭函数执行的开销,一个刷新距离内函数执行屡次时没有意义的,因为少数显示器每16.7ms刷新一次,屡次绘制并不会在屏幕上体现进去。
- 缩小DOM操作:requestAnimationFrame 会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就实现,并且重绘或回流的工夫距离紧紧追随浏览器的刷新频率,一般来说,这个频率为每秒60帧。
setTimeout执行动画的毛病:它通过设定间隔时间来一直扭转图像地位,达到动画成果。然而容易呈现卡顿、抖动的景象;起因是:
- settimeout工作被放入异步队列,只有当主线程工作执行完后才会执行队列中的工作,因而理论执行工夫总是比设定工夫要晚;
- settimeout的固定工夫距离不肯定与屏幕刷新间隔时间雷同,会引起丢帧。