阅读 107

十道前端面试题第【02】篇

摘要:本篇分享了5道算法题、手写bind函数、跨域解决方案、Vuex工作流程、ES6新特性、学习CSS布局等。

1、运行下面代码,请依次写出控制台打印的顺序。

console.log(1)
setTimeout(()=>console.log(2), 0)
new Promise((resolve,reject)=>{
  console.log(3)
  setTimeout(()=>resolve(4), 0)
}).then(res=>{
  console.log(res)
  console.log(5)
})
requestAnimationFrame(()=>console.log(6))
console.log(7)
复制代码

答案:有可能是1 3 7 6 2 4 5,也有可能是1 3 7 2 4 5 6。

2、封装方法 arr2tree,把下面数组转化成树结构。

//  输入:源数组
var arr = [
  { id: 0, name: '新闻' },
  { id: 1, name: '体育', pid: 0 },
  { id: 2, name: '篮球', pid: 1 },
  { id: 3, name: '足球', pid: 1 },
  { id: 4, name: 'NBA', pid: 2 },
  { id: 5, name: 'CBA', pid: 2 },
  { id: 6, name: '娱乐', pid: 0 },
  { id: 7, name: '小品', pid: 6 },
  { id: 8, name: '相声', pid: 6 },
]

// 输出:树结构
var tree = [
  { 
    "id": 0, "name": "新闻", 
    "children": [
      { 
        "id": 1, "name": "体育", "pid": 0, 
        "children": [
          { 
            "id": 2, "name": "篮球", "pid": 1, 
            "children": [
              { "id": 4, "name": "NBA", "pid": 2 }, 
              { "id": 5, "name": "CBA", "pid": 2 }
            ] 
          }, 
          { "id": 3, "name": "足球", "pid": 1 }
        ] 
      }, 
      { 
        "id": 6, "name": "娱乐", "pid": 0, 
        "children": [
          { "id": 7, "name": "小品", "pid": 6 }, 
          { "id": 8, "name": "相声", "pid": 6 }
        ] 
      }
    ] 
  }
]
复制代码

参考答案:


function arr2tree(sourceArr) {
  let obj = {}
  for (let i = 0; i < sourceArr.length; i++) {
    obj[sourceArr[i].id] = sourceArr[i]
  }
  const result = []
  sourceArr.forEach(node => {
    if (!obj[node.pid]) {
      result.push(node)
      return
    }
    obj[node.pid].children = obj[node.pid].children || []
    obj[node.pid].children.push(node)
  })
  return result
}
复制代码

3、观察下方示例代码中的输入与输出关系,封装 add() 方法。

// 输入 源方法 => 输出结果
add(1);       // 1
add(1)(2);    // 3
add(1)(2)(3); // 6
add(1)(2,3);  // 6
add(1,2)(3);  // 6
add(1,2,3);   // 6

// 参考答案
function add() {
  let args = [].slice.call(arguments); 
  let fn = function(){
   let fn_args = [].slice.call(arguments)
   return add.apply(null,args.concat(fn_args))
  }
  fn.toString = function(){
    return args.reduce((a,b)=>a+b)
  }
  return fn
}
// 测试:
add(1,2)(3)(4,5)(6) // 21
复制代码

4、封装函数,实现千位分隔符。

// 输入 源方法 => 保留三位小数
parseToMoney(1234.56); // return '1,234.56'
parseToMoney(123456789); // return '123,456,789'
parseToMoney(1087654.32123); // return '1,087,654.321'

// 参考答案
function parseToMoney(num) {
  num = parseFloat(num.toFixed(3));
  let [integer, decimal] = String.prototype.split.call(num, '.')
  integer = integer.replace(/\d(?=(\d{3})+$)/g, '$&,')
  return integer + '.' + (decimal ? decimal : '')
}
复制代码

5、封装方法求任意两个Number数组的交集。

举例:输入 num1 = [1, 2, 2, 1]nums = [2, 2, 3],返回 [2, 2]

function union (arr1, arr2) {
  return arr1.filter(item => arr2.indexOf(item)>-1)
}
// 测试
const a = [1, 2, 2, 1];
const b = [2, 3, 2];
union(a, b)   // [2, 2]
复制代码

6、谈一谈 bind、call、apply的区别,并封装实现一个 bind 函数。

  • call 和 apply 都是为了解决改变 this 的指向。作用都是相同的,只是传参的方式不同。除了第一个参数外,call 可以接收一个参数列表,apply 只接受一个参数数组。
let a = { value: 1 }
function getValue(name, age) {
    console.log(name)
    console.log(age)
    console.log(this.value)
}
getValue.call(a, 'geek', '10')
getValue.apply(a, ['geek', '30'])
复制代码
  • bind 的实现对比其他两个函数略微地复杂了一点,因为 bind 需要返回一个函数,需要判断一些边界问题,参考代码如下。
Function.prototype.myBind = function (context) {
  if (typeof this !== 'function') {
    throw new TypeError('Error')
  }
  const _this = this
  const args = [...arguments].slice(1)
  // 返回一个函数
  return function F() {
    // 因为返回了一个函数,我们可以 new F(),所以需要判断
    if (this instanceof F) {
      return new _this(...args, ...arguments)
    }
    return _this.apply(context, args.concat(...arguments))
  }
}
复制代码

7、请绘制并描述Vuex的工作流程。

2463290-1d0363776f331f3b.webp

8、罗列 ES6 的新特性。

9、常用的 CSS 布局属性(方案)有哪些?

10、什么是跨域?什么是浏览器同源策略?有哪些常用的解决跨域方案?手动封装一个JSONP的跨域请求函数。

  • 因为浏览器出于安全考虑,有同源策略。也就是说,如果协议、域名或者端口有一个不同就是跨域,Ajax 请求会失败。
  • 常用的跨域解决方案有:JSONP、CORS、代理。
  • JSONP跨域请求函数封装如下:
// 使用JSONP(函数封装)
function jsonp(url, jsonpCallback, success) {
  let script = document.createElement('script')
  script.src = url
  script.async = true
  script.type = 'text/javascript'
  window[jsonpCallback] = function (data) {
    success && success(data)
  }
  document.body.appendChild(script)
}
// 测试示例
jsonp('http://xxx', 'callback', function (value) {
  console.log(value)
})

// 使用JSONP(不封装)
let script = document.createElement('script');
script.src = 'http://www.baidu.cn/login?username=JasonShu&callback=callback';
document.body.appendChild(script);
function callback (res) {
  console.log(res);
}
复制代码
文章分类
前端
文章标签