数组去重
ES5 实现:
function unique(arr) {var res = arr.filter(function(item, index, array) {return array.indexOf(item) === index
})
return res
}
ES6 实现:
var unique = arr => [...new Set(arr)]
浏览器乱码的起因是什么?如何解决?
产生乱码的起因:
- 网页源代码是
gbk
的编码,而内容中的中文字是utf-8
编码的,这样浏览器关上即会呈现html
乱码,反之也会呈现乱码; html
网页编码是gbk
,而程序从数据库中调出出现是utf-8
编码的内容也会造成编码乱码;- 浏览器不能自动检测网页编码,造成网页乱码。
解决办法:
- 应用软件编辑 HTML 网页内容;
- 如果网页设置编码是
gbk
,而数据库贮存数据编码格局是UTF-8
,此时须要程序查询数据库数据显示数据后退程序转码; - 如果浏览器浏览时候呈现网页乱码,在浏览器中找到转换编码的菜单进行转换。
代码输入后果
const first = () => (new Promise((resolve, reject) => {console.log(3);
let p = new Promise((resolve, reject) => {console.log(7);
setTimeout(() => {console.log(5);
resolve(6);
console.log(p)
}, 0)
resolve(1);
});
resolve(2);
p.then((arg) => {console.log(arg);
});
}));
first().then((arg) => {console.log(arg);
});
console.log(4);
输入后果如下:
3
7
4
1
2
5
Promise{<resolved>: 1}
代码的执行过程如下:
- 首先会进入 Promise,打印出 3,之后进入上面的 Promise,打印出 7;
- 遇到了定时器,将其退出宏工作队列;
- 执行 Promise p 中的 resolve,状态变为 resolved,返回值为 1;
- 执行 Promise first 中的 resolve,状态变为 resolved,返回值为 2;
- 遇到 p.then,将其退出微工作队列,遇到 first().then,将其退出工作队列;
- 执行里面的代码,打印出 4;
- 这样第一轮宏工作就执行完了,开始执行微工作队列中的工作,先后打印出 1 和 2;
- 这样微工作就执行完了,开始执行下一轮宏工作,宏工作队列中有一个定时器,执行它,打印出 5,因为执行曾经变为 resolved 状态,所以
resolve(6)
不会再执行; - 最初
console.log(p)
打印出Promise{<resolved>: 1}
;
什么是 XSS 攻打?
(1)概念
XSS 攻打指的是跨站脚本攻打,是一种代码注入攻打。攻击者通过在网站注入歹意脚本,使之在用户的浏览器上运行,从而盗取用户的信息如 cookie 等。
XSS 的实质是因为网站没有对恶意代码进行过滤,与失常的代码混合在一起了,浏览器没有方法分辨哪些脚本是可信的,从而导致了恶意代码的执行。
攻击者能够通过这种攻击方式能够进行以下操作:
- 获取页面的数据,如 DOM、cookie、localStorage;
- DOS 攻打,发送正当申请,占用服务器资源,从而使用户无法访问服务器;
- 毁坏页面构造;
- 流量劫持(将链接指向某网站);
(2)攻打类型
XSS 能够分为存储型、反射型和 DOM 型:
- 存储型指的是歹意脚本会存储在指标服务器上,当浏览器申请数据时,脚本从服务器传回并执行。
- 反射型指的是攻击者诱导用户拜访一个带有恶意代码的 URL 后,服务器端接收数据后处理,而后把带有恶意代码的数据发送到浏览器端,浏览器端解析这段带有 XSS 代码的数据后当做脚本执行,最终实现 XSS 攻打。
- DOM 型指的通过批改页面的 DOM 节点造成的 XSS。
1)存储型 XSS 的攻打步骤:
- 攻击者将恶意代码提交到⽬标⽹站的数据库中。
- ⽤户关上⽬标⽹站时,⽹站服务端将恶意代码从数据库取出,拼接在 HTML 中返回给浏览器。
- ⽤户浏览器接管到响应后解析执⾏,混在其中的恶意代码也被执⾏。
- 恶意代码窃取⽤户数据并发送到攻击者的⽹站,或者假冒⽤户的⾏为,调⽤⽬标⽹站接⼝执⾏攻击者指定的操作。
这种攻打常⻅于带有⽤户保留数据的⽹站性能,如论坛发帖、商品评论、⽤户私信等。
2)反射型 XSS 的攻打步骤:
- 攻击者结构出非凡的 URL,其中蕴含恶意代码。
- ⽤户关上带有恶意代码的 URL 时,⽹站服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器。
- ⽤户浏览器接管到响应后解析执⾏,混在其中的恶意代码也被执⾏。
- 恶意代码窃取⽤户数据并发送到攻击者的⽹站,或者假冒⽤户的⾏为,调⽤⽬标⽹站接⼝执⾏攻击者指定的操作。
反射型 XSS 跟存储型 XSS 的区别是:存储型 XSS 的恶意代码存在数据库⾥,反射型 XSS 的恶意代码存在 URL ⾥。
反射型 XSS 破绽常⻅于通过 URL 传递参数的性能,如⽹站搜寻、跳转等。因为须要⽤户被动关上歹意的 URL 能力⽣效,攻击者往往会联合多种⼿段诱导⽤户点击。
3)DOM 型 XSS 的攻打步骤:
- 攻击者结构出非凡的 URL,其中蕴含恶意代码。
- ⽤户关上带有恶意代码的 URL。
- ⽤户浏览器接管到响应后解析执⾏,前端 JavaScript 取出 URL 中的恶意代码并执⾏。
- 恶意代码窃取⽤户数据并发送到攻击者的⽹站,或者假冒⽤户的⾏为,调⽤⽬标⽹站接⼝执⾏攻击者指定的操作。
DOM 型 XSS 跟前两种 XSS 的区别:DOM 型 XSS 攻打中,取出和执⾏恶意代码由浏览器端实现,属于前端 JavaScript ⾃身的安全漏洞,⽽其余两种 XSS 都属于服务端的安全漏洞。
什么是原型什么是原型链?
<!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>
</body>
<script>
function Person () {} var person = new Person(); person.name = 'Kevin'; console.log(person.name) // Kevin
// prototype
function Person () {} Person.prototype.name = 'Kevin'; var person1 = new Person(); var person2 = new Person(); console.log(person1.name)// Kevin
console.log(person2.name)// Kevin
// __proto__
function Person () {} var person = new Person(); console.log(person.__proto__ === Person.prototype) // true
//constructor
function Person() {} console.log(Person === Person.prototype.constructor) // true
// 综上所述
function Person () {} var person = new Person() console.log(person.__proto__ == Person.prototype) // true
console.log(Person.prototype.constructor == Person) // true
// 顺便学习一下 ES5 得办法, 能够取得对象得原型
console.log(Object.getPrototypeOf(person) === Person.prototype) // true
// 实例与原型
function Person () {} Person.prototype.name = 'Kevin'; var person = new Person(); person.name = 'Daisy'; console.log(person.name) // Daisy
delete person.name; console.log(person.name) // Kevin
// 原型得原型
var obj = new Object(); obj.name = 'Kevin', console.log(obj.name) //Kevin
// 原型链
console.log(Object.prototype.__proto__ === null) //true
// null 示意 "没用对象" 即该处不应该有值
// 补充
function Person() {} var person = new Person() console.log(person.constructor === Person) // true
// 当获取 person.constructor 时,其实 person 中并没有 constructor 属性, 当不能读取到 constructor 属性时, 会从 person 的原型
// 也就是 Person.prototype 中读取时, 正好原型中有该属性, 所以
person.constructor === Person.prototype.constructor
//__proto__
// 其次是__proto__,绝大部分浏览器都反对这个非标准的办法拜访原型,然而它并不存在于 Person.prototype 中,实际上,它
// 是来自与 Object.prototype, 与其说是一个属性,不如说是一个 getter/setter, 当应用 obj.__proto__时,能够了解成返回了
// Object.getPrototypeOf(obj)
总结:1、当一个对象查找属性和办法时会从本身查找, 如果查找不到则会通过__proto__指向被实例化的构造函数的 prototype 2、隐式原型也是一个对象, 是指向咱们构造函数的原型 3、除了最顶层的 Object 对象没有__proto_,其余所有的对象都有__proto__, 这是隐式原型 4、隐式原型__proto__的作用是让对象通过它来始终往上查找属性或办法,直到找到最顶层的 Object 的__proto__属性,它的值是 null, 这个查找的过程就是原型链
</script>
</html>
数组扁平化
数组扁平化就是将 [1, [2, [3]]] 这种多层的数组拍平成一层 [1, 2, 3]。应用 Array.prototype.flat 能够间接将多层数组拍平成一层:
[1, [2, [3]]].flat(2) // [1, 2, 3]
当初就是要实现 flat 这种成果。
ES5 实现:递归。
function flatten(arr) {var result = [];
for (var i = 0, len = arr.length; i < len; i++) {if (Array.isArray(arr[i])) {result = result.concat(flatten(arr[i]))
} else {result.push(arr[i])
}
}
return result;
}
ES6 实现:
function flatten(arr) {while (arr.some(item => Array.isArray(item))) {arr = [].concat(...arr);
}
return arr;
}
参考 前端进阶面试题具体解答
事件总线(公布订阅模式)
class EventEmitter {constructor() {this.cache = {}
}
on(name, fn) {if (this.cache[name]) {this.cache[name].push(fn)
} else {this.cache[name] = [fn]
}
}
off(name, fn) {let tasks = this.cache[name]
if (tasks) {const index = tasks.findIndex(f => f === fn || f.callback === fn)
if (index >= 0) {tasks.splice(index, 1)
}
}
}
emit(name, once = false, ...args) {if (this.cache[name]) {
// 创立正本,如果回调函数内持续注册雷同事件,会造成死循环
let tasks = this.cache[name].slice()
for (let fn of tasks) {fn(...args)
}
if (once) {delete this.cache[name]
}
}
}
}
// 测试
let eventBus = new EventEmitter()
let fn1 = function(name, age) {console.log(`${name} ${age}`)
}
let fn2 = function(name, age) {console.log(`hello, ${name} ${age}`)
}
eventBus.on('aaa', fn1)
eventBus.on('aaa', fn2)
eventBus.emit('aaa', false, '布兰', 12)
// '布兰 12'
// 'hello, 布兰 12'
防抖节流
题目形容: 手写防抖节流
实现代码如下:
// 防抖
function debounce(fn, delay = 300) {
// 默认 300 毫秒
let timer;
return function () {
const args = arguments;
if (timer) {clearTimeout(timer);
}
timer = setTimeout(() => {fn.apply(this, args); // 扭转 this 指向为调用 debounce 所指的对象
}, delay);
};
}
window.addEventListener(
"scroll",
debounce(() => {console.log(111);
}, 1000)
);
// 节流
// 设置一个标记
function throttle(fn, delay) {
let flag = true;
return () => {if (!flag) return;
flag = false;
timer = setTimeout(() => {fn();
flag = true;
}, delay);
};
}
window.addEventListener(
"scroll",
throttle(() => {console.log(111);
}, 1000)
);
instanceof
题目形容: 手写 instanceof 操作符实现
实现代码如下:
function myInstanceof(left, right) {while (true) {if (left === null) {return false;}
if (left.__proto__ === right.prototype) {return true;}
left = left.__proto__;
}
}
说一下常见的 HTTP 状态码? 说一下状态码是 302 和 304 是什么意思?你在我的项目中呈现过么?你是怎么解决的?
<!-- 状态码:由 3 位数字组成,第一个数字定义了响应的类别 -->
<!-- 1xx:批示音讯, 示意申请已接管,持续解决 -->
<!-- 2xx:胜利, 示意申请已被胜利接管,解决 -->
<!-- 200 OK:客户端申请胜利
204 No Content:无内容。服务器胜利解决,但未返回内容。个别用在只是客户端向服务器发送信息,而服务器不必向客户端返回什么信息的状况。不会刷新页面。206 Partial Content:服务器曾经实现了局部 GET 申请(客户端进行了范畴申请)。响应报文中蕴含 Content-Range 指定范畴的实体内容
-->
<!-- 3xx 重定向 -->
<!-- 301 Moved Permanently:永恒重定向,示意申请的资源曾经永恒的搬到了其余地位。302 Found:长期重定向,示意申请的资源长期搬到了其余地位
303 See Other:长期重定向,应应用 GET 定向获取申请资源。303 性能与 302 一样,区别只是 303 明确客户端应该应用 GET 拜访
307 Temporary Redirect:长期重定向,和 302 有着雷同含意。POST 不会变成 GET
304 Not Modified:示意客户端发送附带条件的申请(GET 办法申请报文中的 IF…)时,条件不满足。返回 304 时,不蕴含任何响应主体。尽管 304 被划分在 3XX,但和重定向一毛钱关系都没有
-->
<!-- 4xx:客户端谬误 -->
<!-- 400 Bad Request:客户端申请有语法错误,服务器无奈了解。401 Unauthorized:申请未经受权,这个状态代码必须和 WWW-Authenticate 报头域一起应用。403 Forbidden:服务器收到申请,然而回绝提供服务
404 Not Found:申请资源不存在。比方,输出了谬误的 url
415 Unsupported media type:不反对的媒体类型
-->
<!-- 5xx:服务器端谬误,服务器未能实现非法的申请。-->
<!-- 500 Internal Server Error:服务器产生不可预期的谬误。503 Server Unavailable:服务器以后不能解决客户端的申请,一段时间后可能恢复正常,-->
代码输入后果
Promise.resolve(1)
.then(res => {console.log(res);
return 2;
})
.catch(err => {return 3;})
.then(res => {console.log(res);
});
输入后果如下:
1
2
Promise 是能够链式调用的,因为每次调用 .then
或者 .catch
都会返回一个新的 promise,从而实现了链式调用, 它并不像个别工作的链式调用一样 return this。
下面的输入后果之所以顺次打印出 1 和 2,是因为 resolve(1)
之后走的是第一个 then 办法,并没有进 catch 里,所以第二个 then 中的 res 失去的实际上是第一个 then 的返回值。并且 return 2 会被包装成resolve(2)
,被最初的 then 打印输出 2。
Promise.resolve
Promise.resolve = function(value) {
// 1. 如果 value 参数是一个 Promise 对象,则一成不变返回该对象
if(value instanceof Promise) return value;
// 2. 如果 value 参数是一个具备 then 办法的对象,则将这个对象转为 Promise 对象,并立刻执行它的 then 办法
if(typeof value === "object" && 'then' in value) {return new Promise((resolve, reject) => {value.then(resolve, reject);
});
}
// 3. 否则返回一个新的 Promise 对象,状态为 fulfilled
return new Promise(resolve => resolve(value));
}
首屏和白屏工夫如何计算
首屏工夫的计算,能够由 Native WebView 提供的相似 onload 的办法实现,在 ios 下对应的是 webViewDidFinishLoad,在 android 下对应的是 onPageFinished 事件。
白屏的定义有多种。能够认为“没有任何内容”是白屏,能够认为“网络或服务异样”是白屏,能够认为“数据加载中”是白屏,能够认为“图片加载不进去”是白屏。场景不同,白屏的计算形式就不雷同。
办法 1:当页面的元素数小于 x 时,则认为页面白屏。比方“没有任何内容”,能够获取页面的 DOM 节点数,判断 DOM 节点数少于某个阈值 X,则认为白屏。办法 2:当页面呈现业务定义的错误码时,则认为是白屏。比方“网络或服务异样”。办法 3:当页面呈现业务定义的特征值时,则认为是白屏。比方“数据加载中”。
事件是如何实现的?
基于公布订阅模式,就是在浏览器加载的时候会读取事件相干的代码,然而只有理论等到具体的事件触发的时候才会执行。
比方点击按钮,这是个事件(Event),而负责处理事件的代码段通常被称为事件处理程序(Event Handler),也就是「启动对话框的显示」这个动作。
在 Web 端,咱们常见的就是 DOM 事件:
- DOM0 级事件,间接在 html 元素上绑定 on-event,比方 onclick,勾销的话,dom.onclick = null,同一个事件只能有一个处理程序,前面的会笼罩后面的。
- DOM2 级事件,通过 addEventListener 注册事件,通过 removeEventListener 来删除事件,一个事件能够有多个事件处理程序,按程序执行,捕捉事件和冒泡事件
- DOM3 级事件,减少了事件类型,比方 UI 事件,焦点事件,鼠标事件
如何阻止事件冒泡
- 一般浏览器应用:event.stopPropagation()
- IE 浏览器应用:event.cancelBubble = true;
常见浏览器所用内核
(1)IE 浏览器内核:Trident 内核,也是俗称的 IE 内核;
(2)Chrome 浏览器内核:统称为 Chromium 内核或 Chrome 内核,以前是 Webkit 内核,当初是 Blink 内核;
(3)Firefox 浏览器内核:Gecko 内核,俗称 Firefox 内核;
(4)Safari 浏览器内核:Webkit 内核;
(5)Opera 浏览器内核:最后是本人的 Presto 内核,起初退出谷歌大军,从 Webkit 又到了 Blink 内核;
(6)360 浏览器、猎豹浏览器内核:IE + Chrome 双内核;
(7)搜狗、漫游、QQ 浏览器内核:Trident(兼容模式)+ Webkit(高速模式);
(8)百度浏览器、世界之窗内核:IE 内核;
(9)2345 浏览器内核:如同以前是 IE 内核,当初也是 IE + Chrome 双内核了;
(10)UC 浏览器内核:这个众口不一,UC 说是他们本人研发的 U3 内核,但如同还是基于 Webkit 和 Trident,还有说是基于火狐内核。
寄生组合继承
题目形容: 实现一个你认为不错的 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();
Set,Map 解构
ES6 提供了新的数据结构 Set。它相似于数组,然而成员的值都是惟一的,没有反复的值。Set 自身是一个构造函数,用来生成 Set 数据结构。ES6 提供了 Map 数据结构。它相似于对象,也是键值对的汇合,然而“键”的范畴不限于字符串,各种类型的值(包含对象)都能够当作键。
事件委托的应用场景
场景:给页面的所有的 a 标签增加 click 事件,代码如下:
document.addEventListener("click", function(e) {if (e.target.nodeName == "A")
console.log("a");
}, false);
然而这些 a 标签可能蕴含一些像 span、img 等元素,如果点击到了这些 a 标签中的元素,就不会触发 click 事件,因为事件绑定上在 a 标签元素上,而触发这些外部的元素时,e.target 指向的是触发 click 事件的元素(span、img 等其余元素)。
这种状况下就能够应用事件委托来解决,将事件绑定在 a 标签的外部元素上,当点击它的时候,就会逐级向上查找,晓得找到 a 标签为止,代码如下:
document.addEventListener("click", function(e) {
var node = e.target;
while (node.parentNode.nodeName != "BODY") {if (node.nodeName == "A") {console.log("a");
break;
}
node = node.parentNode;
}
}, false);
代码输入后果
var A = {n: 4399};
var B = function(){this.n = 9999};
var C = function(){var n = 8888};
B.prototype = A;
C.prototype = A;
var b = new B();
var c = new C();
A.n++
console.log(b.n);
console.log(c.n);
输入后果:9999 4400
解析:
- console.log(b.n),在查找 b.n 是首先查找 b 对象本身有没有 n 属性,如果没有会去原型(prototype)上查找,当执行 var b = new B()时,函数外部 this.n=9999(此时 this 指向 b) 返回 b 对象,b 对象有本身的 n 属性,所以返回 9999。
- console.log(c.n),同理,当执行 var c = new C()时,c 对象没有本身的 n 属性,向上查找,找到原型(prototype)上的 n 属性,因为 A.n++(此时对象 A 中的 n 为 4400),所以返回 4400。