面试十题(3)
1. cookie 和 token 都存放在 header 中,为什么不会 劫持 token?
cookie
登录后服务端生成的sessionid,并在http请求里返回到客户端,同时服务端保存sessionid,以后客户端的每次http请求都带上cookie(sessionid),服务端会获取cookie(sessionid)然后验证用户的身份。所以拿到cookie就拿到了sessionid,就可验证通过。同时浏览器会自动携带cookie;
token
同样是登录后服务端返回一个token,客户端保存起来,在以后http请求里手动的加入到请求头里,服务端根据token 进行身份的校验。浏览器不会自动携带token。
CSRF 跨站点请求伪造
通过浏览器会自动携带同域cookie的特点。cookie的传递流程是用户在访问站点时,服务器端生成cookie,发送给浏览器端储存,当下次再访问时浏览器会将该网站的cookie发回给服务器端。 而浏览器不会自动携带 token,所以不会劫持 token。
XSS 跨域脚本攻击。
跨站脚本工攻击是指通过存在安全漏洞的Web网站注册用户的浏览器内运行非法的HTML标签或者JavaScript进行的一种攻击。 就是说,cookie和token都可能被拿到,所以都废了。
2. 合并两个数组
请把两个数组 ['A1', 'A2', 'B1', 'B2', 'C1', 'C2', 'D1', 'D2'] 和 ['A', 'B', 'C', 'D'],合并 为 ['A1', 'A2', 'A', 'B1', 'B2', 'B', 'C1', 'C2', 'C', 'D1', 'D2', 'D']。
let a1 = ['A1', 'A2', 'B1', 'B2', 'C1', 'C2', 'D1', 'D2']
let a2 = ['A', 'B', 'C', 'D'].map((item) => {
return item + 3
})
let a3 = [...a1, ...a2].sort().map((item) => {
if(item.includes('3')){
return item.split('')[0]
}
return item
})
3. 改造下面的代码,使之输出0-9,写出你能想到的所有解法。
for (var i = 0; i < 10; i++) {
setTimeout(() => {
console.log(i)
}, 1000)
}
解法一:
// 块级作用域
for (let i = 0; i < 10; i++){
setTimeout(() => {
console.log(i);
}, 1000)
}
解法二:
// 自执行函数,把当前的i传递给自执行函数,构建块级作用域
for (var i = 0; i < 10; i++){
(i => {
setTimeout(() => {
console.log(i)
}, 1000)
})(i)
}
解法三:
// 利用 setTimeout 函数的第三个参数,会作为回调函数的第一个参数传入
// ① 同步
// var a = 9
// setTimeout(a++, 3000)
// console.log(a)
// ② 异步
// var a = 9
// setTimeout('a++', 3000)
// console.log(a)
// 同步。setTimeout第一个参数自执行函数
for (var i = 0; i< 10; i++){
setTimeout(((i) => {
console.log(i);
})(i), 1000)
}
// setTimeout第三个参数会作为setTimeout回调函数的参数
for (var i = 0; i < 10; i++) {
setTimeout(console.log, 1000, i)
}
解法四:
// 利用try catch
for(var i = 0; i < 10; i++){
try{
throw i;
}catch(i){
setTimeout(() => {
console.log(i)
}, 1000)
}
}
解法五:
// promise
for (var i = 0; i < 10; i++) {
timeoutPromise(i);
}
function timeoutPromise(i) {
return new Promise((resolve) => {
setTimeout(() => {
console.log(i);
resolve();
}, 1000);
});
}
解法六:
// Generator函数
for (var i = 0; i < 10; i++) {
timeoutGenerator(i).next();
}
function* timeoutGenerator (i) {
yield setTimeout(() => {
console.log(i);
}, 1000);
}
解法七:
// async await
async function init () {
for (var i = 0; i < 10; i++) {
await timeoutPromise(i);
}
}
function timeoutPromise (i) {
return new Promise((resolve) => {
setTimeout(() => {
console.log(i);
resolve();
}, 1000);
});
}
init();
// 1秒打印1次
解法八:
// apply call bind
// 区别apply和call都是同步执行,bind是异步执行
// apply 参数是数组形式
// ① apply 同步
for (var i = 0; i < 10; i++) {
setTimeout(console.log.apply(null, [i]), 1000)
}
// ② call 同步
for (var i = 0; i < 10; i++) {
setTimeout(console.log.call(null, i), 1000)
}
// ③ 异步
for (var i = 0; i < 10; i++) {
setTimeout(console.log.bind(null, i), 1000)
}
4. Virtual DOM 真的比操作原生 DOM 快吗?谈谈你的 想法。
- 没有任何框架可以比纯手动的优化 DOM 操作更快,因为框架的 DOM 操作层需要应对任何上层 API 可能产生的操作,它的实现必须是普适的。
- Virtual DOM 真正的价值从来都不是性能,而是它:
- 为函数式的 UI 编程方式打开了大门;
- 可以渲染到 DOM 以外的 backend,比如 ReactNative。
5. 下面代码打印什么,为什么?
var b = 10;
(function b() {
b = 20
console.log(b)
console.log(window.b)
})()
// ƒ b() {
// b = 20
// console.log(b)
// console.log(window.b)
// 10
}
- 解析:
var b = 10;
(function b() {
// 内部作用域,会先去查找是有已有变量b的声明,有就直接赋值20,确实有了呀。发现了具名函数 function b(){},拿此b做赋值;
// IIFE的函数无法进行赋值(内部机制,类似const定义的常量),所以无效。
// (这里说的“内部机制”,想搞清楚,需要去查阅一些资料,弄明白IIFE在JS引擎的工作方式,堆栈存储IIFE的方式等)
b = 20;
console.log(b); // [Function b]
console.log(window.b); // 10,不是20
})();
所以严格模式下能看到错误:Uncaught TypeError: Assignment to constant variable
var b = 10;
(function b() {
'use strict'
b = 20;
console.log(b)
})()
// "Uncaught TypeError: Assignment to constant variable."
其他情况例子:
// 有window
var b = 10;
(function b() {
window.b = 20;
console.log(b); // function
console.log(window.b); // 20是必然的
})();
// 有var
var b = 10;
(function b() {
var b = 20; // IIFE内部变量
console.log(b); // 20
console.log(window.b); // 10
})();
6. 浏览器缓存读取规则
7. 聊聊 Vue 的双向数据绑定,Model 如何改变 View, View 又是如何改变 Model 的
- 待整理! link
8. 使用迭代的方式实现 flatten 函数。
let arr = [1, 2, [3, 4, 5, [6, 7], 8], 9, 10, [11, [12, 13]]];
- 递归方法(ES6)
const flatten = (array) =>
array.reduce(
(acc, cur) =>
Array.isArray(cur) ? [...acc, ...flatten(cur)] : [...acc, cur],
[]
);
- some方法
// some() 不会对空数组进行检测。
// some() 不会改变原始数组。
const flatten = function (arr) {
while (arr.some((item) => Array.isArray(item))) {
arr = [].concat(...arr);
}
return arr;
};
- 先转成字符串,再转成数组
arr.join(',').split(',').map(item => Number(item))
// 或者
arr.toString().split(',').map(item => Number(item))
- reduce用法。
9. 为什么 Vuex 的 mutation 和 Redux 的 reducer 中 不能做异步操作?
- Mutation必须是同步函数。 vuex和redux都是一种状态管理机制。然后他们会有自己的state(状态)和修改state的方法,修改state的方法涉及到同步和异步,vuex的处理方式是同步在mutation里面,异步在actions里面,然后redux的同步就是reducer,异步更多的是用户自己去通过中间件的方式去实现
10. 下面代码中 a 在什么情况下会打印 1?
var a = ???
if (a == 1 && a == 2 && a == 3) {
console.log(1)
}
// 问a等于什么时候 打印1
// 至少两种方法
// 考察对象和基础数据比较,先执行toString(),如果还不是普通数据,再执行valueOf()
// ①
var a = {
i: 1,
toString() {
return a.i++
}
}
// console.log(a == 1 && a == 2 && a == 3) // true
// ②
var a = [1,2,3]
a.toString = a.shift
// console.log(a == 1 && a == 2 && a == 3) // true