1.判断一个函数是否标记为 async
function isAsyncFunction(func){
const str=Object.prototype.toString.call(func);
return str=='[object AsyncFunction]';
}
2.两个大整数之和
/**
* 1.将两个数字对齐
* 2.从后往前循环,按位相加同时加上进位
* 3.计算进位
* 4.到最后一位时,如果有进位则加上进位
*/
function sum(a,b){
const len =Math.max(a.length,b.length);
a=a.padStart(len,'0');
b=b.padStart(len,'0');
let carry=0;
let result='';
for(let i=len-1;i>=0;i--){
const sum=+a[i] + +b[i] +carry;
result=(sum%10) + result;
carry=Math.floor(sum/10);
}
if(carry){
result=carry + result;
}
return result;
}
3.模拟微队列
function runMicroTask(func){
//promise 存在的情况
if(typeof Promise !=='undefined'){
Promise.resolve().then(func);
return ;
}
//使用MutationObserver
if(typeof MutationObserver!=='undefined'){
var observer=new MutationObserver(func);
var textNode=document.createTextNode("1");
observer.observe(textNode,{characterData: true});
textNode.data='2';
return;
}
// node 环境
if(process && process.nextTick){
process.nextTick(func);
return;
}
setTimeout(func);
}
4.剩余参数
当函数参数不定的时候,这个时候可以使用剩余参数
function sum(...args){
let r=0;
for(let i=0;i<args.length;i++){
r+=args[i];
}
return r;
}
sum(1,2)
sum(1,2,3)
如果函数不止一个参数,剩余参数只作为最后一个参数使用。
5. 多层循环跳出
如果有两层循环,当第二层循环满足条件时需要跳出循环,可以jslabel 跳出循环。
outer: for(i=0;i<10;i++){
console.info("i",i);
for(j=0;j<10;j++){
console.info("j",j);
if(j==5){
break outer;
}
}
}
6.如何实现滚动加载的效果原理是什么?
7. initial、unset、revert 分别是什么意思
属性 | 说明 |
---|---|
initial | css的默认值,比如 line-height:normal;等价于 line-height:initial; |
unset | 不设置样式,意思是能继承就继承,能使用默认就使用默认,默认值指的是w3c的默认值 |
revert | 回到浏览器默认的样式 |
8. 输出顺序
console.info("1");
setTimeout(()=>{
console.info("2");
});
(async ()=>{
await 1 ;
console.info("3");
})()
console.info("4");
console.log('1');
setTimeout(() => {
console.log('2');
},0);
Promise.resolve().then(() => {
console.log('5');
})
new Promise((resolve) => {
console.log('3');
resolve();
}).then(() => {
console.log('4');
})
9. this 的指向
this的指向是调用的时候确定的。
调用方式 | 示例 | 函数中的this指向 |
---|---|---|
通过new 调用 | new Person | 指向新对象 |
直接调用 | method() | 全局对象 |
通过对象调用 | obj.method() | 前面的对象 |
call,apply,bind | method.call(ctx) | 第一个参数 |
function Fn(){
console.info(this);
}
let newFn=Fn.bind(1);
newFn()
10.什么是变量提升?
console.info(a);
var a=10;
//相当于如下代码
var a;
console.info(a);
a=10;
function m(){
console.info(a,b,c);
var a=1;
var b=()=>{};
function c(){};
}
// 输出什么
m();
11. 对象深度克隆
function deepClone (data) {
let cache= new WeakMap()
function _deepClone(obj) {
// 如果不是对象或者是null,直接返回
if (obj == null || typeof obj !== 'object') return obj;
// 如果已经存在于hash中,直接返回
if (cache.has(obj)) return cache.get(obj);
let clone = Array.isArray(obj) ? [] : {};
cache.set(obj, clone);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
clone[key] = _deepClone(obj[key]);
}
}
return clone;
}
return _deepClone(data);
}
12.变量作用域
写出如下代码的输出
var a=1;
function m(){
a++;
}
function m2(){
var a=3;
m();
console.info(a);
}
m2();
console.info(a);
13. 手写 bind
function fn(a,b,c,d){
console.info(a,b,c,d);
console.info(this);
}
const newFn=fn.bind('ctx',1,2);
newFn(3,4);
Function.prototype.myBind=function(ctx){
var args=Array.prototype.slice.call(arguments,1);
var fn=this;
return function A(){
var restArgs=Array.prototype.slice.call(arguments);
var allArgs=args.concat(restArgs);
//使用new 的方式创建
if(Object.getPrototypeOf(this)===A.prototype){
//return new fn(...allArgs);
//相当于new 一个fn。
var obj={};
Object.setPrototypeOf(obj,fn.prototype);
fn.apply(obj,allArgs);
return obj;
}
else{
return fn.apply(ctx,allArgs)
}
}
}
const newFn=fn.myBind('ctx',1,2);
newFn(3,4);
14. 元素尺寸
尺寸 | 说明 |
---|---|
clientWidth | content + padding |
offsetWidth | content + padding +scroll + border |
scrollWidth | visible + invisible |
var rect=getBoundingClientRect() | 可以获取画出来最终的大小,包括 transform变换 |
15. new 操作符做了什么,手写 new 实现.
function createObject(fn,...args){
//创建新对象
let obj={};
//将新对象原型指向函数原型
Object.setPrototypeOf(obj,fn.prototype);
//改变this指向
var result =fn.apply(obj,args);
//如果构造函数返回对象,则返回该对象,否则返回新对象。
return result instanceof Object ? result:obj;
}
16. 写一个防抖函数
function debounce(func, wait) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => {
func.apply(this, args);
}, wait);
};
}
17. 写一个节流函数
function createThrottle(func, wait) {
let lastTime = 0;
let timer = null;
// 返回实际会被调用的节流函数
return function throttled(...args) {
const now = Date.now();
// 如果距离上次执行时间小于wait,则不执行并重新计算下一次可能执行的时间点
if (now - lastTime < wait) {
clearTimeout(timer);
timer = setTimeout(() => {
lastTime = now;
func.apply(this, args);
}, wait - (now - lastTime));
} else {
// 否则立即执行,并更新最后执行时间
lastTime = now;
func.apply(this, args);
}
};
}
18. await 面试题
async function asy1(){
console.log(1);
await asy2();
console.log(2);
}
asy2=async () =>{
await setTimeout((_)=>{
Promise.resolve().then(()=>{
console.info(3);
})
console.log(4);
},0)
}
asy3= async ()=>{
Promise.resolve().then(()=>{
console.log(6);
})
}
asy1();
console.log(7);
asy3()
19.打印结果
考察对象的键值
const obj={a:0};
obj['1']=0;
obj[++obj.a]=obj.a++;
const values=Object.values(obj);
obj[values[1]]=obj.a;
console.info(obj)
20. 常见的宏任务微任务
-
宏任务
script (可以理解为外层同步代码)
setTimeout/setInterval
setImmediate(Node.js)
I/O
UI事件 -
微任务
Promise
process.nextTick(Node.js)
Object.observe
MutaionObserver
postMessage
console.log('1');
setTimeout(() => {
console.log('2');
},0);
Promise.resolve().then(() => {
console.log('5');
})
new Promise((resolve) => {
console.log('3');
resolve();
}).then(() => {
console.log('4');
})
21. flat 函数
考虑我们有如下一个数组
const arr = [1, 2, [3, 4], [5, 6, [7, 8]]]
这个数组有很多层,我们现在需要将它变成一层的应该怎么做呢?reduce和递归我们很容易写出这个方法:
const flat = (arr, initVal) => {
const startVal = initVal || [];
return arr.reduce((prevRes, item) => {
// 如果里层还是数组,递归调用自身
if(Array.isArray(item)){
return flat(item, prevRes);
}else{
return prevRes.concat(item);
}
}, startVal)
}
const arr = [1, 2, [3, 4], [5, 6, [7, 8]]];
const flatArr = flat(arr);
console.log(flatArr); // [1, 2, 3, 4, 5, 6, 7, 8]
实现方法2
const arr = [1, 2, [3, 4], [5, 6, [7, 8]]]
function flat(arr,rtn=[]){
for(let i=0;i<arr.length;i++){
let item=arr[i];
if(Array.isArray(item)){
flat(item,rtn);
}
else{
rtn.push(item)
}
}
}
let rtn=[];
flat(arr,rtn);
console.info(rtn)
方法3
function flat(arr){
let rtn=[];
for(let i=0;i<arr.length;i++){
let item=arr[i];
if(Array.isArray(item)){
rtn.push(...flat(item));
}
else{
rtn.push(item)
}
}
return rtn;
}
let rtn=flat(arr);
22.reduce 函数使用及实现
22.1数据转换
let ary=[{name:"aa",age:20},{name:"bb",age:20}];
let rtn=ary.reduce((res, current) => {
res[current.name] = current.age;
return res;
}, {})
console.info(rtn)
将数据 转换成 {aa:20,bb:20} 的结构。
22.2生成新的数组
let rtn=ary.reduce((res, current) => {
res.push(current.name);
return res;
}, [])
将数据转成 ["aa","bb"]
22.3数组求和
let ary=[1,3,4,5];
let rtn=ary.reduce((res, current) => {
return res+current;
}, 0)
console.info(rtn)
22.4 如何实现自己的 reduce 函数
/**
* array 需要运算的数组
* reducer: 回调函数
* initialValue: 初始值
*/
function customReduce(array, reducer, initialValue) {
// 检查数组是否为空,且没有提供初始值,如果是,则抛出错误
if (array.length === 0 && initialValue === undefined) {
throw new TypeError("Reduce of empty array with no initial value");
}
let accumulator = initialValue;
let startIndex = 0;
// 遍历数组,从startIndex开始
for (let i = 0; i < array.length; i++) {
accumulator = reducer(accumulator, array[i], i, array);
}
return accumulator;
}