网络劫持有哪几种,如何防备?

⽹络劫持分为两种:

(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

代码的执行过程如下:

  1. 首先执行同步带吗,打印出script start;
  2. 遇到定时器timer1将其退出宏工作队列;
  3. 之后是执行Promise,打印出promise1,因为Promise没有返回值,所以前面的代码不会执行;
  4. 而后执行同步代码,打印出script end;
  5. 继续执行上面的Promise,.then和.catch冀望参数是一个函数,这里传入的是一个数字,因而就会产生值浸透,将resolve(1)的值传到最初一个then,间接打印出1;
  6. 遇到第二个定时器,将其退出到微工作队列,执行微工作队列,按程序顺次执行两个定时器,然而因为定时器工夫的起因,会在两秒后先打印出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
  • 遇到Promisenew Promise间接执行,输入7。then被散发到微工作Event Queue中。记为then1
  • 又遇到了setTimeout,其回调函数被散发到宏工作Event Queue中,记为setTimeout2
宏工作Event Queue微工作Event Queue
setTimeout1process1
setTimeout2then1

上表是第一轮事件循环宏工作完结时各Event Queue的状况,此时曾经输入了1和7。发现了process1then1两个微工作:

  • 执行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
setTimeout2process2
then2

第二轮事件循环宏工作完结,发现有process2then2两个微工作能够执行:

  • 输入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

第三轮事件循环宏工作执行完结,执行两个微工作process3then3

  • 输入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指向的:

  1. 执行db1()时,this指向全局作用域,所以window.number 4 = 8,而后执行匿名函数, 所以window.number 5 = 40;
  2. 执行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的固定工夫距离不肯定与屏幕刷新间隔时间雷同,会引起丢帧。