网络劫持有哪几种,如何防备?
⽹络劫持分为两种:
(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: ' 1
2
3
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 start
async1
promise1
script end
1
timer2
timer1
代码的执行过程如下:
- 首先执行同步带吗,打印出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')
})
})
输入后果如下:
1
7
6
8
2
4
3
5
9
11
10
12
(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); // 15
console.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 协定之上,默认端口号是 80
HTTP 能够分为两个局部,即申请和响应。
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); // null
a.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的固定工夫距离不肯定与屏幕刷新间隔时间雷同,会引起丢帧。
发表回复