javascript
场景题
- 模板字符串处理
- 如果对应的键值不存在则不处理
- |后面跟着的是filter函数,均在filters中存在
- 如果结果是对象则对接过进行 JSON.stringify()
- 如果结果是函数则进行 toString()
var str = `
a
{{obj.a | filter | filter2}
b
{obj.b.c}
c
{obj.c.d}
`
var obj = {
a:function(){},
b:{c:{e:123}},
c:{}
}
var g = {
filter(str) {return 'aaa' + str },
filter2(str) {return str + 'bbb'}
}
// 实现
function parseTemplate(str,obj,g){
// 将输入的字符串按行切割成流
let strList = str.split('\n')
// 保存转换完成的结果
let result = []
// 寻找花括号对,返回一个二维数组,每个一维数组包含花括号左右的索引
function findBrackets (s) {
let result = []
let item = []
for (let i=0; i<s.length; i++) {
if (s[i] === '{') {
item[0] = i
} else if (s[i] === '}') {
item[1] = i
}
if (item[0]!==undefined && item[1]!==undefined && item[0] < item[1]) {
result.push(item)
item = []
}
}
return result
}
// 对模版字符串进行处理
function cope(s, obj, g) {
// 按 | 分割,并去除两头的空白字符
let item = s.split('|').map((d)=>d.trim())
// 取出对象的属性链
let properties = item[0].split('.').slice(1)
// 取出filters函数链
let filters = item.slice(1)
// 随属性链依次递进得到属性值
while (properties.length) {
let key = properties.shift()
obj = obj[key]
// 如果不存在,则不处理
if (!obj) {
return "{" + item[0] + "}"
}
}
// 转换为字符串
if (Object.prototype.toString.call(obj) === "[object Object]") {
obj = JSON.stringify(obj)
} else if (typeof obj === 'function') {
obj = obj.toString()
}
// 经过filters函数链处理返回最终结果
return filters.reduce((pre, cur) => {
return g[cur](pre)
}, obj)
}
// 处理每一条输入流
while (strList.length) {
let line = strList.shift()
let brackets = findBrackets(line)
if (!brackets.length) {
result.push(line)
} else {
let temp = ''
let start = 0
// 对每一对花括号中的内容进行转换
while (brackets.length) {
let bracket = brackets.shift()
temp += line.substring(start, bracket[0])
let s = line.substring(bracket[0]+1, bracket[1])
temp += cope(s, obj, g)
start = bracket[1] + 1
}
temp += line.substring(start)
result.push(temp)
}
}
return result.join("\n")
}
parseTemplate(str,obj,g)
替换后的结果为
// a
// {aaafunction () { }bbb
// b
// {"e":123}
// c
// {obj.c.d}
理论
- 什么是原型,什么是原型链
- 原型
- 每个函数(除了箭头函数)在定义后会自动生成一个伴生对象prototype
- 以这个函数为构造函数生成的实例,会在prototype上寻找属性跟方法
- 原型链
- 原型是一个对象,拥有__proto__隐藏属性,指向其构造函数的原型,这样依次链接,直到最顶层的Object的原型为止的整个线性结构
- 剔除数组首元素的方法有哪些
x = [1, 2, 3]
x.shift()
x.splice(0, 1)
x = x.slice(1)
[, ...x] = x
- promise的状态有哪些
/**
* 待定 pending
* 完成 fulfilled
* 拒绝 rejected
* 同一时间只能存在一种状态,且状态一旦改变就不能再变
**/
- promise有什么特点
- 内部需要使用resolve或者reject函数来改变promise的状态
- 拥有两个常用方法then跟catch,当结果成功返回执行then回调,否则执行catch回调
- 回调函数并不需要在状态改变前就绑定,状态变化之后绑定的回调也能得到执行
- then、catch方法的返回值是promise对象(不是则包装成promise对象)
- 可能对then、catch进行链式调用,将回调地域的深度写法转换为长度写法
- 什么是回调地域
- 在某些场景下,异步任务必须按次序执行,例如AJAX请求,这样就必须将一个请求写在另一个请求成功的回调中,如果请求比较多,那么回调就会嵌套的越来越深,对代码阅读和维护都会造成影响
- for in 遍历数组有哪些问题
- 遍历的是数组的索引,而不是值
- 遍历到的索引是string类型
- 会遍历到给数组添加的属性的键
- 会遍历到原型链上添加的属性
- 因此最好用传统的for方式来遍历数组
- 观看示例,输出结果是什么,并阐明理由
- 例1
const promise = new Promise((resolve, reject) => { console.log(1) resolve() console.log(2) }) promise.then(() => { console.log(3) }) console.log(4) // 1,2,4,3 // new Promise回调立即执行,打印1,遇到resolve改变promise状态,但并不让出线程,继续向下执行输出2, // 第二句,给promise绑定then回调,第三句,打印4,同步代码执行完毕,再执行回调打印3
- 例2
function fn(){ for (let i = 0; i < 4; i++) { setTimeout(function(){ console.log(i) },1000) } } fn() // 0, 1, 2, 3 // 使用 let 声明的变量拥有块级作用域,定时器回调引用变量i使得生存周期延长,并且每个回调引用的i不是同一个变量
- 例3
let a = 0 let b = async () => { a = a + await 10 console.log('2', a) } b() a++ console.log('1', a) // 1, 1 // 2, 10 // 执行b时,执行到a = a + await 10,让出线程,表达式执行顺序从左到右,因此在让出线程之前a的值已经确定,即0 // b 让出后,a++,并打印出,1,1。回到await处继续执行,0 + 10 = 0,再打印2, 10 // 如果新的表达式的顺序(await 10) + a,则打印2, 11