1、手动实现一个repeat方法
function repeat(func,times,wait){
//todo
}
const repeatFunc = repeat(console.log,4,1000);
// repeatFunc(‘hello’); 调用repeatFunc方法,每个1秒打印一次,一共打印4次
解析:调用方法时可传参,说明repeat返回的是一个function
① 使用setTimeout实现
function repeat(func, times, wait) {
return function callback(val) {
func.call(this, val);
times-- > 1 && setTimeout(() => {
callback(val)
}, wait)
}
}
const repeatFunc = repeat(console.log, 4, 1000)
repeatFunc('hello')
② 使用setInterval实现
function repeat(func, times, wait) {
return function (val) {
let count = 0;
//func(val);如果一开始就打印 就先执行一遍再使用计时器,后面次数减1
let timer = setInterval(() => {
count++;
count < times ? '' : clearInterval(timer)
func(val);
}, wait)
}
}
const repeatFunc = repeat(console.log, 4, 1000)
repeatFunc('hello')
2、给定起始日期,返回中间所有月份
function getMonths(str1, str2) {
let arr = [];
let ms = Math.ceil((new Date(str1) - new Date(str2)) * -1 / 1000 / 3600 / 24 / 30)
console.log(ms)
for (let i = 1; i < ms- 1; i++) {
let ym= str1.split('-');
let mVal = (((~~ym[1] + i) % 12) ? ((~~ym[1] + i) % 12) : 12);
arr.push((~~ym[0] + (((~~ym[1] + i) % 12) ? Math.floor((~~ym[1] + i) / 12) : Math.floor((~~ym[1] + i) / 12) - 1)) + '-' + ( mVal>9?mVal:'0'+mVal))
}
return arr
}
getMonths('2020-01', '2020-06')
// 输出 ["2020-02", "2020-03", "2020-04", "2020-05"]
3、Promise.resolve(obj) ,obj有有几种可能?
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/resolve
4、如何把真实DOM转为虚拟DOM
https://blog.csdn.net/piano9425/article/details/104553403/
5、实现一个 函数将中文数字转成数字
function zhToNum(str) {
let numMap = {
'零': 0, '一': 1, '二': 2, '三': 3, '四': 4, '五': 5, '六': 6, '七': 7, '八': 8, '九': 9,
'壹': 1, '贰': 2, '叁': 3, '肆': 4, '伍': 5, '陆': 6, '柒': 7, '捌': 8, '玖': 9
}
let arr = []
for (let i of [...str]) {
arr.push(numMap[i])
}
return arr.join('')
}
zhToNum('零叁陆四玖八')
或者用Map来实现
function zhToNum(str){
let numMap = new Map([['零',0],['一',1],['二',2],['三',3],['四',4],['五',5],['六',6],['七',7],['八',8],['九',9],
['壹',1],['贰',2],['叁',3],['肆',4],['伍',5],['陆',6],['柒',7],['捌',8],['玖',9]])
let arr = []
for(let i of [...str]){
arr.push(numMap.get(i))
}
return arr.join('')
}
zhToNum('零叁陆四玖八')
6、for…in 和 object.keys 区别
for…in一般用于对象属性的遍历
let obj = {
a:1,
b:2,
}
for(let key in obj){
console.log(key)
}
// a
// b
Object.keys()方法会返回 一个对象自身可枚举属性的数组,数组中属性名称的顺序和for…in的顺序一致
let obj = {
a:1,
b:2,
}
console.log(Object.keys(obj))
// ["a", "b"]
两者之间最主要的区别就是Object.keys( )不会走原型链,而for in 会走原型链
Object.prototype.test = ‘01';
var obj= {
a:1,
b:2,
}
//Object.keys不会输出原型链中的数据;
console.log(Object.keys(obj))
// ["a", "b"]
for(var key in obj){
console.log(key)
}
// a
// b
// test //for in 会把原型链中test 输出
7、react中context的理解
https://segmentfault.com/a/1190000017758300
8、一个人一次只能走一级或者两级楼梯,问走到第80级楼梯一共有多少种方法?
注:动态规划的简单例子(斐波那契数列)
function climb(){
const res = [1,1,2]
if(n<3) return res[n];
for(let i=3;i<=n;i++){
res[i] = res[i-1]+res[i-2]
}
return res[n]
}
climb(80)
9、利用 Generator 函数和for…of循环,实现斐波那契数列
function* fibonacci() {
let [prev, curr] = [0, 1];
for (;;) {
yield curr;
[prev, curr] = [curr, prev + curr];
}
}
for (let n of fibonacci()) {
if (n > 1000) break;
console.log(n);
}
10、原生js实现图片懒加载
https://zhuanlan.zhihu.com/p/55311726
11、目前主流的js模块化实现的技术
流行的js模块化规范有CommonJS、AMD、CMD以及ES6的模块系统
各技术的区别
12、二分查找 迭代和递归方式实现
//二分查找的递归算法
public static int bSearch(int[] a, int x, int left, int right) {
//index记录找到元素的下标,初始状态是-1
int index = -1;
if(left <= right){
int mid = (left+right) / 2;
if(x > a[mid]) {
index = bSearch(a, x, mid+1, right);
} else if(x < a[mid]) {
index = bSearch(a, x, left, mid-1);
} else {
return mid;
}
}
return index;
}
/*非递归的折半查找*/
public static int binarySearch(int[] a, int x) {
int left = 0;
int right = a.length - 1;
while(left <= right) {
int mid = (left+ right) / 2;
if(x > a[mid]) {
left = mid+1;
} else if(x < a[mid]) {
right = mid-1;
} else {
return mid;
}
}
return -1;
}
每次迭代在循环内的所有工作话费O(1),因此分析需要确定循环的次数。循环从right-left=leng-1开始并在right-left<= -1结束。每次循环后right-left的值至少将该循环前的值折半;于是,循环的次数最多是[log(n-1)]+2。因此:运行的时间是O(log N),而递归的时间复杂度则需要O(N)。
13、尾递归
函数调用自身,称为递归。如果尾调用自身,就称为尾递归。
递归非常耗费内存,因为需要同时保存成千上百个调用帧,很容易发生“栈溢出”错误(stack overflow)。但对于尾递归来说,由于只存在一个调用帧,所以永远不会发生“栈溢出”错误。
function factorial(n) {
if (n === 1) return 1;
return n * factorial(n - 1);
}
factorial(5) // 120
上面代码是一个阶乘函数,计算n的阶乘,最多需要保存n个调用记录,复杂度 O(n) 。
如果改写成尾递归,只保留一个调用记录,复杂度 O(1) 。
function factorial(n, total) {
if (n === 1) return total;
return factorial(n - 1, n * total);
}
factorial(5, 1) // 120
Fibonacci 数列 非尾递归和尾递归实现:
//非尾递归
function Fibonacci (n) {
if ( n <= 1 ) {return 1};
return Fibonacci(n - 1) + Fibonacci(n - 2);
}
Fibonacci(10) // 89
Fibonacci(100) // 超时
Fibonacci(500) // 超时
//尾递归
function Fibonacci2 (n , ac1 = 1 , ac2 = 1) {
if( n <= 1 ) {return ac2};
return Fibonacci2 (n - 1, ac2, ac1 + ac2);
}
Fibonacci2(100) // 573147844013817200000
Fibonacci2(1000) // 7.0330367711422765e+208
Fibonacci2(10000) // Infinity
尾递归优化(递归转循环):
function tco(f) {
var value;
var active = false;
var accumulated = [];
return function accumulator() {
accumulated.push(arguments);
if (!active) {
active = true;
while (accumulated.length) {
value = f.apply(this, accumulated.shift());
}
active = false;
return value;
}
};
}
var sum = tco(function(x, y) {
if (y > 0) {
return sum(x + 1, y - 1)
}
else {
return x
}
});
sum(1, 100000)
// 100001
上面代码中,tco函数是尾递归优化的实现,它的奥妙就在于状态变量active。默认情况下,这个变量是不激活的。一旦进入尾递归优化的过程,这个变量就激活了。然后,每一轮递归sum返回的都是undefined,所以就避免了递归执行;而accumulated数组存放每一轮sum执行的参数,总是有值的,这就保证了accumulator函数内部的while循环总是会执行。这样就很巧妙地将“递归”改成了“循环”,而后一轮的参数会取代前一轮的参数,保证了调用栈只有一层。
14、全局上下文GO与函数上下文AO
前几天在网上逛帖子的时候,看了几个Js的题,发现都是关于GO与AO的一些应用,正好自己也重新回顾一下。
先看题:(PS:如果有兴趣做做题的小伙伴,可以在下方评论说出自己第一感觉的答案哦)
1.
var a = 1;
function a(){
console.log(“aa”);
};
function test(){};
console.log(a);
2.
function test(a){
console.log(a);
var a = 1;
console.log(a);
function a(){};
console.log(a);
var b = function(){};
console.log(b);
function d(){}
}
test(2);
答案:
第一个:输出a为:1
第二个:输出:function a(){},1,1,function (){}
下面简单的说一下GO与AO
GO:全局上下文,Global Object,它在全局代码执行前产生
产生的时候做了什么呢?分为以下几个步骤:
1:寻找变量声明
2:寻找函数声明,赋值
3:代码执行
AO:Activation Object 活跃对象,函数上下文,在函数执行前产生
产生的时候做了什么呢?分为以下几个步骤:
1:寻找形参和变量声明
2:实参赋值给形参
3:寻找函数声明,赋值
4:代码执行
分析第一题:
主要是GO
GO = {
a:undefined (寻找变量声明)
->function a(){} (寻找函数声明赋值)
->1 (代码执行)
test:function test(){} (寻找函数声明赋值)
}
所以代码执行到console时,此时a已经被赋值1,输出1
第二题:
首先是GO
GO = {
test:function test(){…} (寻找函数声明赋值)
}
当test函数要执行时,创建AO
AO = {
a:undefined (寻找形参和变量声明)
->2 (实参赋值给形参)
->function a(){…} (寻找函数声明,赋值)
->1 (代码执行)
b:undefined (寻找形参和变量声明)
->function(){…} (代码执行)
d:function d(){…} () (寻找函数声明,赋值)
}
首先看第一个a的输出:由于输出是在a=1之前,所以输出function a(){}
第二个a的输出:在a=1之后,赋值完成,所以输出1,
第三个a的输出:由于function a(){}在AO中已经赋过值,此时直接跳过,输出1
b输出:function(){…}
最后有道题,小伙伴们自己分析分析,看看结果和自己想的是不是一样(提示要同时考虑GO与AO哦)
a = 1;
function test(e){
function e(){};
arguments[0] = 2;
console.log(e);
if(a){
var b = 3;
}
var c;
a = 4;
var a;
console.log(b);
f = 5;
console.log©;
console.log(a);
}
var a;
test(1);
console.log(a);
console.log(f);
最后结果是:2,undefined,undefined,4,1,5
15、手动实现一个json.parse()。
① eval方法实现
var json = '{"a":"1", "b":2}';
var obj = eval("(" + json + ")"); // obj 就是 json 反序列化之后得到的对象
② Function方法实现
function parse(jsonStr){
return new Function('return'+jsonStr)()
}
parse('{"a":"2","b":""}')