Open Jogiter opened 6 years ago
__proto__
属性,指向了创建该对象的构造函数的原型,__proto__
将对象连接起来组成了原型链。是一个用来实现继承和共享属性的有限的对象链。Q1:
setTimeout(function() {
setTimeout(function() { console.log(1) }, 100)
console.log(2)
setTimeout(function() { console.log(3) }, 0)
}, 0)
setTimeout(function () {
console.log(4)
}, 100)
console.log(5)
// 输出顺序: 5 2 3 4 1
Q2:
console.log('script start')
let promise1 = new Promise(function (resolve) {
console.log('promise1')
resolve()
console.log('promise1 end')
}).then(function () {
console.log('promise2')
})
setTimeout(function(){
console.log('settimeout')
})
console.log('script end')
// 输出顺序: script start->promise1->promise1 end->script end->promise2->settimeout
Q3:
async function async1(){
console.log('async1 start');
await async2();
console.log('async1 end')
}
async function async2(){
console.log('async2')
}
console.log('script start');
async1();
console.log('script end')
// 输出顺序:script start->async1 start->async2->script end->async1 end
Q4:
function* generator(i) {
console.log('inside before')
yield i;
yield i + 10;
console.log('inside after')
}
var gen = generator(10);
console.log('outside before')
console.log(gen.next().value);
console.log(gen.next().value);
console.log('outside after')
gen.next();
/**
* outside before
* inside before
* 10
* 20
* outside after
* inside after // 如果不加最后一个gen.next(); 就不会有这一行
*/
Q: 完成一个 sum 函数,使调用后输出 6
考点:高阶函数,高级柯里化,参见 博客:higher-order-function
sum(1)(2)(3).valueOf()
function curryingHelper(fn, ...args) {
return (...newArgs) => {
return fn.apply(this, args.concat(newArgs));
}
}
// fn.length:获取 fn 的参数个数
function betterCurryingHelper(fn, length = fn.length) {
return (...args) => {
let allArgsFulfilled = args.length >= length
// 如果参数全部满足,就可以终止递归调用
if (allArgsFulfilled) {
return fn.apply(this, args);
} else {
let argsNeedFulfilled = [fn].concat(args);
return betterCurryingHelper(curryingHelper.apply(this, argsNeedFulfilled), length - args.length)
}
}
}
function add(a, b, c) {
return {
valueOf() {
return a + b + c
}
}
}
var sum = betterCurryingHelper(add)
sum(1)(2)(3).valueOf()
改造下面的代码,使之输出0 - 9,写出你能想到的所有解法。
for (var i = 0; i< 10; i++){
setTimeout(() => {
console.log(i);
}, 1000)
}
解:
// 解一
for (var i = 0; i< 10; i++){
setTimeout((i) => {
console.log(i);
}, 1000, i)
}
// 解二
for (let i = 0; i< 10; i++){
setTimeout(() => {
console.log(i);
}, 1000)
}
// 解三
for (var i = 0; i< 10; i++){
setTimeout((function(i) {
console.log(i);
})(i), 1000)
}
下面的代码打印什么内容,为什么?
非匿名自执行函数,函数名只读。
var b = 10;
(function b(){
b = 20;
console.log(b);
})();
/**
ƒ b() {
b = 20;
console.log(b)
}
*/
下面代码中 a 在什么情况下会打印 1?
var a = ?;
if(a == 1 && a == 2 && a == 3){
conso.log(1);
}
答案解析 因为==会进行隐式类型转换 所以我们重写toString方法就可以了
var a = {
i: 1,
toString() {
return a.i++;
}
}
if( a == 1 && a == 2 && a == 3 ) {
console.log(1);
}
var a = {
i: 1,
valueOf() {
return a.i++;
}
}
if( a == 1 && a == 2 && a == 3 ) {
console.log(1);
}
[3, 15, 8, 29, 102, 22].sort()
// [102, 15, 22, 29, 3, 8]
[3, 15, 8, 29, 102, 22].sort((a, b) => a - b)
// [3, 8, 15, 22, 29, 102]
实现 (5).add(3).minus(2) 功能,参考 Daily-Interview-Question#88
简易版
Number.prototype.add = function(n) {
return this.valueOf() + n;
};
Number.prototype.minus = function(n) {
return this.valueOf() - n;
};
完整版
Number.MAX_SAFE_DIGITS = Number.MAX_SAFE_INTEGER.toString().length-2
Number.prototype.digits = function(){
let result = (this.valueOf().toString().split('.')[1] || '').length
return result > Number.MAX_SAFE_DIGITS ? Number.MAX_SAFE_DIGITS : result
}
Number.prototype.add = function(i=0){
if (typeof i !== 'number') {
throw new Error('请输入正确的数字');
}
const v = this.valueOf();
const thisDigits = this.digits();
const iDigits = i.digits();
const baseNum = Math.pow(10, Math.max(thisDigits, iDigits));
const result = (v * baseNum + i * baseNum) / baseNum;
if(result>0){ return result > Number.MAX_SAFE_INTEGER ? Number.MAX_SAFE_INTEGER : result }
else{ return result < Number.MIN_SAFE_INTEGER ? Number.MIN_SAFE_INTEGER : result }
}
Number.prototype.minus = function(i=0){
if (typeof i !== 'number') {
throw new Error('请输入正确的数字');
}
const v = this.valueOf();
const thisDigits = this.digits();
const iDigits = i.digits();
const baseNum = Math.pow(10, Math.max(thisDigits, iDigits));
const result = (v * baseNum - i * baseNum) / baseNum;
if(result>0){ return result > Number.MAX_SAFE_INTEGER ? Number.MAX_SAFE_INTEGER : result }
else{ return result < Number.MIN_SAFE_INTEGER ? Number.MIN_SAFE_INTEGER : result }
}
阅读链接:
缓存对于前端性能优化来说是个很重要的点,良好的缓存策略可以降低资源的重复加载提高网页的整体加载速度。
通常浏览器缓存策略分为两种:强缓存和协商缓存。
实现强缓存可以通过两种响应头实现:Expires
和 Cache-Control
。强缓存表示在缓存期间不需要请求,state code 为 200
Expires: Wed, 22 Oct 2018 08:41:00 GMT
Expires
是 HTTP / 1.0
的产物,表示资源会在 Wed, 22 Oct 2018 08:41:00 GMT
后过期,需要再次请求。并且 Expires
受限于本地时间,如果修改了本地时间,可能会造成缓存失效。
Cache-control: max-age=30
Cache-Control
出现于 HTTP / 1.1
,优先级高于 Expires
。该属性表示资源会在 30 秒后过期,需要再次请求。
如果缓存过期了,我们就可以使用协商缓存来解决问题。协商缓存需要请求,如果缓存有效会返回 304。 协商缓存需要客户端和服务端共同实现。
算法
1.快排
function quickSort(arr){
//如果数组<=1,则直接返回
if(arr.length<=1){return arr;}
var pivotIndex=Math.floor(arr.length/2);
//找基准,并把基准从原数组删除
var pivot=arr.splice(pivotIndex,1)[0];
//定义左右数组
var left=[];
var right=[];
//比基准小的放在left,比基准大的放在right
for(var i=0;i<arr.length;i++){
if(arr[i]<=pivot){
left.push(arr[i]);
}
else{
right.push(arr[i]);
}
}
//递归
return quickSort(left).concat([pivot],quickSort(right));
}
2.给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序
示例:
输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
说明:
Answer:
思路:双指针
设定一个慢指针一个快指针,快指针每次+1, 当慢指针的值不等于0的时候也往后移动,当慢指针等于0并且快指针不等于0的时候,交换快慢指针的值,慢指针再+1
function moveZero(arr) {
let i = 0
let j = 0
while (j < arr.length) {
if (arr[i] !== 0) {
i++
} else if (arr[j] !== 0) {
;[arr[i], arr[j]] = [arr[j], arr[i]]
i++
}
j++
}
}
时间复杂度O(n),n是数组长度,空间复杂度O(1)
array.flat(depth)
var arr1 = [1, 2, [3, 4]];
arr1.flat();
// 反嵌套一层数组
arr1.reduce((acc, val) => acc.concat(val), []);// [1, 2, 3, 4]
// 或使用 ...
const flatSingle = arr => [].concat(...arr);
// 使用 reduce、concat 和递归无限反嵌套多层嵌套的数组
var arr1 = [1,2,3,[1,2,3,4, [2,3,4]]];
function flattenDeep(arr1) {
return arr1.reduce((acc, val) => Array.isArray(val) ? acc.concat(flattenDeep(val)) : acc.concat(val), []);
}
flattenDeep(arr1);
// [1, 2, 3, 1, 2, 3, 4, 2, 3, 4]
参考 MDN:Array 的一些用法:
修改 array.length
var fruits = [];
fruits.push('banana', 'apple', 'peach');
fruits[5] = 'mango'; // 给超出当前数组大小的下标赋值
console.log(Object.keys(fruits)); // ['0', '1', '2', '5']
console.log(fruits.length); // 6
console.log(fruits); // ["banana", "apple", "peach", empty × 2, "mango"]
fruits.length = 2; // 为 length 赋一个更小的值则会删掉一部分元素
console.log(Object.keys(fruits)); // ['0', '1']
console.log(fruits.length); // 2
Array.from(arrayLike[, mapFn[, thisArg]])
Array.from('foo') // ["f", "o", "o"]
function f() {
return Array.from(arguments);
}
f(1, 2, 3); // [1, 2, 3]
Array.from({length: 5}, (v, i) => i); // [0, 1, 2, 3, 4]
Array.from({length: 5}, () => 6); // [6, 6, 6, 6, 6]
Array(5).fill(6); // [6, 6, 6, 6, 6]
Array(5).join('6').split(''); // ["6", "6", "6", "6"]
// !Objects by reference.
var arr = Array.from({length: 3}).fill({}) // [{}, {}, {}];
arr[0].hi = "hi"; // [{ hi: "hi" }, { hi: "hi" }, { hi: "hi" }]
// !Objects by reference.
var arr = Array(3).fill([]) // [[], [], []];
arr[0].push("hi"); // [["hi"], ["hi"], ["hi"]]
// es6
Array.from({length: 3}, () => {}) // [undefined, undefined, undefined]
Array.from({length: 3}, () => ({})) // [{}, {}, {}]
Array.from({length: 3}, () => [])) // [[], [], []];
Q: 求两个数组的交集并集?
测试用例:
var nums1 = [1], nums2 = [1,1];
var nums1 = [1, 2, 2, 1], nums2 = [2, 2];
var nums1 = [1, 2, 2, 1], nums2 = [2, 2, 1];
// 交集
const intersect = (nums1, nums2) => {
const map = {}
const res = []
for (let n of nums1) {
if (map[n]) {
map[n]++
} else {
map[n] = 1
}
}
for (let n of nums2) {
if (map[n] > 0) {
res.push(n)
map[n]--
}
}
return res
}
// 并集
const union = (nums1, nums2) => Array.from(new Set(nums1.concat(nums2)))
const union = (nums1, nums2) =>nums1.concat(nums2).reduce((acc, val, index) => acc.includes(val) ? acc : acc.concat(val), [])
javascript 判断一个数字是否为质数
function isPrime(element) {
var start = 2;
while (start <= Math.sqrt(element)) {
if (element % start++ < 1) {
return false;
}
}
return element > 1;
}
正则表达式(待验证)
function isPrimeNum2(num){
return !/^.?$|^(..+?)\1+$/.test(Array(num + 1).join('1'))
}
100 元的红包分给 10 个人抢,每个人最少 6 元,最多 12 元,求怎么分配?
function sharing(total, count, max, min = 0) {
var array = Array(count).fill(min)
var left = total - count * min
var once
var index
while (left > 0) {
once = Math.floor(Math.random() * (max - min))
index = Math.floor(Math.random() * (count - 1))
if (array[index] + once <= max) {
array[index] += once
left -= once
}
}
return array
}
var x = sharing(100, 10, 12, 6)
console.log(x) // [12, 10, 10, 11, 11, 9, 10, 12, 9, 6]
Fibonacci 实现方式
递归非常耗费内存,因为需要同时保存成千上百个调用帧,很容易发生“栈溢出”错误(stack overflow)。
function Fibonacci (n) {
if ( n <= 1 ) {return 1};
return Fibonacci(n - 1) + Fibonacci(n - 2);
}
但对于尾递归来说,由于只存在一个调用帧,所以永远不会发生“栈溢出”错误。
function Fibonacci2 (n , ac1 = 1 , ac2 = 1) {
if( n <= 1 ) {return ac2};
return Fibonacci2 (n - 1, ac2, ac1 + ac2);
}
迭代版本:(n = 10e6, time: 66.8ms)
function iterFib(n) {
let last = 1;
let nextLast = 1;
let result = 1;
for (let i = 2; i < n; ++i) {
result = last + nextLast;
nextLast = last;
last = result;
}
return result;
}
动态规划:(n = 10e6, time: 410.4ms)
function dynFib(n) {
let val = [];
for (let i = 0; i <= n; ++i) {
val[i] = 0;
}
if (n === 1 || n === 2) {
return 1;
} else {
val[1] = 1;
val[2] = 2;
for (let i = 3; i <= n; ++i) {
val[i] = val[i - 1] + val[i - 2];
}
}
return val[n - 1]
}
function main(fn, x){
console.time('t')
fn(x)
console.timeEnd('t')
}
main(Fibonacci2, 400) // t: 0.02099609375 ms
main(iterFib, 400) // t: 0.010009765625 ms
main(dynFib, 400) // t: 0.097900390625 ms
vue-router
源码阅读。通过 mixins
的方式,在 beforeCreate
生命周期里,给 vm 添加了 _route
和 _router
属性;然后在 vue.prototype
上添加了 $router
和 $route
属性vuex
源码阅读。通过 mixins
的方式,在 beforeCreate
生命周期里,给 vm 添加了 $store
属性。mixins
:按照官网文档中给出的合并策略如下
data
数据对象在内部会进行递归合并,并在发生冲突时以组件数据优先methods
、components
和 directives
,将被合并为同一个对象。两个对象键名冲突时,取组件对象的键值对。Vue.extend()
也使用同样的策略进行合并。proxy reactive 实现 diff 算法 vdom 算法优化
基础问题:
new 适用于使用构造函数创建对象并继承原型链的场景,而 Object.create 适用于直接创建对象并继承指定对象的属性和方法的场景。根据具体的需求和场景,选择合适的创建方式。
Object.create(null) 创建的对象没有原型链,不继承任何属性和方法。 Object.create({}) 创建的对象具有原型链,继承传入对象的属性和方法。
es6 新增语法有哪些
手写 promise (async、await、generator、Iterator)
事件循环:async/await、setTimeout、nextTick、promise 的执行顺序
sleep 函数:
function sleepSync(delay) {
let endTime = new Date().getTime() + parseInt(delay);
while (new Date().getTime() < endTime);
}
function sleepAsync(delay) {
return new Promise((resolve) => setTimeout(resolve, delay));
}
面试过程中,需要回答和提问的问题(参考 tech-interview-handbook)
面试题: