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 回到浏览器默认的样式

https://www.bilibili.com/video/BV1ai4y1W7UF/?spm_id_from=333.337.search-card.all.click&vd_source=4f17e4156d79a1f8753e8f8e7b6f8e02

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.变量作用域

写出如下代码的输出

image

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. 常见的宏任务微任务

  1. 宏任务
    script (可以理解为外层同步代码)
    setTimeout/setInterval
    setImmediate(Node.js)
    I/O
    UI事件

  2. 微任务
    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;
}
posted on 2024-03-01 12:05  自由港  阅读(14)  评论(0编辑  收藏  举报