面试十题(3)

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 真正的价值从来都不是性能,而是它:
    1. 为函数式的 UI 编程方式打开了大门;
    1. 可以渲染到 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

面试十题(3)

上一篇:源码分析oldwals日志的删除流程


下一篇:P22 中值定理