摘要:本篇分享了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的工作流程。
vuex.png8、罗列 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);
}
本周结束,下周继续!!!
网友评论