Open kangkai124 opened 5 years ago
from https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/4
map() 方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。
var new_array = arr.map(function callback(currentValue[,index[, array]]) {
// Return element for new_array
}[, thisArg])
callback 回调函数需要三个参数:
currentValue
是callback 数组中正在处理的当前元素index
可选, 是callback 数组中正在处理的当前元素的索引array
可选, 是callback map 方法被调用的数组thisArg
可选, 执行 callback 函数时使用的this 值parseInt() 函数解析一个字符串参数,并返回一个指定基数的整数 (数学系统的基础)。
const intValue = parseInt(string[, radix]);
string | 必需。要被解析的字符串。 |
---|---|
radix | 可选。表示要解析的数字的基数。该值介于 2 ~ 36 之间。如果省略该参数或其值为 0,则数字将以 10 为基础来解析。如果它以 “0x” 或 “0X” 开头,将以 16 为基数。如果该参数小于 2 或者大于 36,则 parseInt() 将返回 NaN。 |
回到题目,对于每个迭代的 map
, parseInt()
接收两个参数:字符串和基数。
即返回的值是:
parseInt('1', 0) // 1,把 “1” 转换为十进制
parseInt('2', 1) // NaN,,因为基数小于 2
parseInt('3', 2) // NaN,3 超过二进制的位数的值
因此最后的答案是:
[1, NaN, NaN]
['1', '2', '3'].map(parseInt)
这段代码本来的目的是转换每个字符串为数字,但是由于 parseInt 参数不定的原因产生了意外的问题,所以使用 unary
(一元函数) 来改写这个例子:
const unary = fn => val => fn(val)
['1', '2', '3'].map(unary(parseInt))
// [1, 2, 3]
unary
函数在执行的过程中,会创建并且返回一个函数,该返回函数最多接受一个参数,然后忽略其余的任何参数。在只给出第一个参数的情况下,调用函数 unary 所提供的函数 fn。
var obj = {
'2': 3,
'3': 4,
'length': 2,
'splice': Array.prototype.splice,
'push': Array.prototype.push
}
obj.push(1)
obj.push(2)
console.log(obj)
from https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/76
对象,有 length 属性
push方法将值追加到数组中。
push 方法有意具有通用性。该方法和 call() 或 apply() 一起使用时,可应用在类似数组的对象上。push 方法根据 length 属性来决定从哪里开始插入给定的值。如果 length 不能被转成一个数值,则插入的元素索引为 0,包括 length 不存在时。当 length 不存在时,将会创建它。
简而言之,push 的作用就是: 1:push 方法根据 length 属性来决定从哪里开始插入给定的值。 2:push 是特意设计为通用的,Array.prototype.push 可以在一个对象上工作。
Array.from(), splice(), concat() 等
这个 obj
对象就是类数组,调用 push
方法的时候,将 this.length 作为 push 的插入位。该 length 为2, 所以从数组的 2+1 位,也就是下标为 2 的地方开始插入,相当于:
objArr[2] = 3
objArr[3] = 4
push 两项之后,其实此时 length 已经变为 2+2 = 4, 但是数组前两项由于没有内容,所以为空(Empty),打印出来就是 [ , , 1, 2] length为4
。
我在 chrome devtools 控制台打印如上,发现对象添加了splice属性后并没有调用就会变成类数组对象,原来是控制台中 DevTools 猜测类数组的一个方式,代码 在这里。
from https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/81
在严格模式下直接使用会有问题。
一个好的解决方案:
<input v-model="message">
// ...
computed: {
message: {
get () {
return this.$store.state.obj.message
},
set (value) {
this.$store.commit('updateMessage', value)
}
},
methods: {
mutations: {
updateMessage (state, message) {
state.obj.message = message
}
}
}
相关连接:computed 绑定过程
let params = [1,2,3,4]
xx.call(obj, ...params)
from https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/87
所谓的前端监控,就是由Web页面将用户信息(UA/鼠标点击位置/页面报错/停留时长/etc)上报给服务器的过程。
使用 1*1 像素的透明 gif 图片打点的原因总结如下:
<script type="text/javascript">
var thisPage = location.href;
var referringPage = (document.referrer) ? document.referrer : "none";
var beacon = new Image();
beacon.src = "http://www.example.com/logger/beacon.gif?page=" + encodeURI(thisPage)
+ "&ref=" + encodeURI(referringPage);
</script>
reference:为什么前端监控要用GIF打点
from https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/88
其实思路很简单,就是给 Number 原型上加方法
Number.prototype.add = function(n) {
return this.valueOf() + n;
};
Number.prototype.minus = function(n) {
return this.valueOf() - n;
};
但是根据一位同学的答案引出了 JS 的经典的浮点数陷阱问题,进而对此进行了一番深入学习。 学习内容笔记 在这里。
from https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/90
第一点,详细可以参考:记一次思否问答的问题思考:Vue为什么不能检测数组变动
10000 长度数组,只有两个下标有值
然后再对比长度相同,但是一个只有两个下标有值,一个是所有下标都有值:
var arr = [1]; arr[100000] = 1
function a(){
console.time()
arr.forEach(item=>{})
console.timeEnd()
}
a(); //default: 2.5810546875ms
a(); //default: 2.02099609375ms
var arr1 = new Array(100000).fill(1);
function b(){
console.time()
arr1.forEach(item=>{})
console.timeEnd()
}
b(); //default: 2.355224609375ms
b(); //default: 1.94189453125ms
两个要花费同样的时间来遍历,所以可以确定遍历性能跟下标有没有值没太大关系。
from https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/96
let obj = { 1:222, 2:123, 5:888 }
const result = Array.from({ length: 12 }, (_, ind) => obj[ind + 1] || null)
// const result = Array.from({ length: 12 }).map((_, ind) => obj[ind + 1] || null)
const obj = { 1: 222, 2: 123, 5: 888 };
const result = Array.from({ ...obj, length: 13 }, item => item || null).slice(1);
console.log(result);
let obj = {1:222, 2:123, 5:888};
obj.length = 13;
obj[Symbol.iterator] = Array.prototype[Symbol.iterator];
// obj ==> {1: 222, 2: 123, 5: 888, length: 12, Symbol(Symbol.iterator): ƒ}
let _obj = [...obj].slice(1);
// _obj ==> [222, 123, undefined, undefined, 888, undefined, undefined, undefined, undefined, undefined, undefined, undefined]
let newObj = _obj.map((item) => {if(item === undefined) {return null;} else {return item;}});
console.log(newObj);
为什么 Symbol.iterator 赋值后,obj 可以使用 slice 方法???
LazyMan('Tony');
// Hi I am Tony
LazyMan('Tony').sleep(10).eat('lunch');
// Hi I am Tony
// 等待了10秒...
// I am eating lunch
LazyMan('Tony').eat('lunch').sleep(10).eat('dinner');
// Hi I am Tony
// I am eating lunch
// 等待了10秒...
// I am eating diner
LazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(5).sleep(10).eat('junk food');
// Hi I am Tony
// 等待了5秒...
// I am eating lunch
// I am eating dinner
// 等待了10秒...
// I am eating junk food
链式调用,所以每个方法需要返回 this。sleep 和 sleepFirst 的区别在于队列中的顺序。
class LazyManClass {
constructor(name) {
this.name = name
this.queue = []
console.log(`Hi I am ${this.name}`);
// 由于 setTimeout 是异步的,当链式调用结束后,queue 队列成形,
// 此时调用 this.next() 开始按照 queue 队列执行方法
setTimeout(() => {
this.next()
}, 0)
}
eat(food) {
const fn = () => {
console.log(`I am eating ${food}`)
this.next()
}
this.queue.push(fn)
return this
}
sleep(time) {
const fn = () => {
setTimeout(() => {
console.log(`等待了${time}秒...`)
this.next()
}, time * 1000)
}
this.queue.push(fn)
return this
}
sleepFirst(time) {
const fn = () => {
setTimeout(() => {
console.log(`等待了${time}秒...`)
this.next()
}, time * 1000)
}
this.queue.unshift(fn)
return this
}
next() {
const fn = this.queue.shift()
fn && fn()
}
}
function LazyMan(name) {
return new LazyManClass(name)
}
class LazyManClass {
constructor(props) {
this.sub = []
console.log(`Hi I am ${props}`)
setTimeout(() => {
this.start()
}, 0)
}
eat(params) {
this.sub.push(function () {
console.log(`I am eating ${params}`)
})
return this
}
sleepFirst(s) {
this.sub.unshift(this.delay(s))
// 这边还没有返回 同步就继续执行了
return this
}
delay(s) {
return () => {
return new Promise(resolve => {
setTimeout(function () {
console.log(`等待了${s}秒...`)
resolve()
}, s * 1000)
})
}
}
sleep(s) {
this.sub.push(this.delay(s))
// 这边还没有返回 同步就继续执行了
return this
}
async start() {
for (const iterator of this.sub) {
await iterator()
}
}
}
function LazyMan(props) {
return new LazyManClass(props)
}
Thanks to Daily-Interview-Question,and all participants.