JS知识点总结


文章目录

  • JS知识点总结
  • 一、如何监听数组的改变?
  • 二、用setTimeout代替setInterval
  • 三、实现Promise.prototype.finally
  • 四、什么是伪数组,怎么将伪数组转换为数组
  • 1.什么是伪数组
  • 2.将伪数组转换为数组的方法
  • 1. 解构赋值
  • 2. Array.from
  • 3.Array.prototype.slice


一、如何监听数组的改变?

利用Proxy可以来监听数组的length属性,当push或者pop的时候,会触发setter

const proxyArr = new Proxy(unkownArr, {
    set(target, prop, value, receiver) {
        if (prop === 'length') {
            console.log('set new value', value);
            return false;
        }
        console.log('receiver', receiver);
        return Reflect.set(...arguments);
    }
});

那要监听数组某一项的变化呢?那就是prop !== 'length'的情况了:

const proxyArr = new Proxy(unkownArr, {
    set(target, prop, value, receiver) {
        if (prop !== 'length') {
            console.log('set new value', value);
            return false;
        }
        console.log('receiver', receiver);
        return Reflect.set(...arguments);
    }
});

二、用setTimeout代替setInterval

首先,为什么要用setTimeout代替setInterval呢?如果单单从事件循环的原因来看的话,这两者不都会因为JS主线程执行的原因造成一定的延迟吗?但是,思考一下setInterval的作用你会发现,setInterval的作用主要是按照一定的间隔时间把任务放进宏队列中。但是如果JS引擎线程执行时间过长,会导致JS定时器线程将setInterval多个任务在JS从宏队列中取任务前放入到宏队列中去,这就会导致JS引擎在执行的时候,这些任务就没有了原来应该有的时间间隔

因此,我们利用setTimeout在当前任务进入队列后去手动触发下一个定时器,实现setInterval

function myInterval(fn, time) {
    // 定义一个函数,内部开启定时器,调用自己
    let timer = null;
    const intervalFn = () => {
        fn();
        if (timer) {
            clearTimeout(timer);
        }
        timer = setTimeout(intervalFn, time);
    };

    timer = setTimeout(intervalFn, time);
	
	return timer;
}

今天面试碰到这样一个场景题:点击一个按钮,触发轮询(1s1次),每次请求一个接口,将接口返回的值作为新状态更新。若返回状态为finished则停止轮询。

这里涉及到定时器相关的问题为,要给定时器增加一个可以手动关闭的参数,不能直接传一个boolean值给myInterval,这样没有办法再外部进行控制,故传一个引用值对象进去即可:

function myInterval(fn, time, isFinished) {
    // 定义一个函数,内部开启定时器,调用自己
    let timer = null;
    const intervalFn = () => {
        fn();
        if (timer) {
            clearTimeout(timer);
        }
        // 用来控制停止计时器
        if (!isFinished.done) {
            timer = setTimeout(intervalFn, time);
        }
    };

    timer = setTimeout(intervalFn, time);
}
const isFinished = {
    done: false
};
const timer = myInterval(() => {
    console.log('myInterval');
}, 200, isFinished);

setTimeout(() => {
    isFinished.done = true;
}, 3000);

三、实现Promise.prototype.finally

finally的作用是在.then.catch之后调用,执行必须要执行的代码,.finally中没有传入参数。如果在.then.catch之前调用.finally,不会影响后面的.then.catch。这说明finally内部拦截了(通过调用this.then)前面promisevaluereason,把valuereason传给下一个promise

Promise.prototype.finally = function(callback) {
    // 获得前面的promise或者拦截前面的promise
    return this.then(
        // finally前面是成功,就执行成功的回调,并把前面的参数向下传递
        value => {
            // 嵌套一层Promise.resolve保证向下传递
            return Promise.resolve(callback()).then(() => value)
        },
        reason => {
            return Promise.resolve(callback()).then(() => { throw reason })
        }
    )
}

四、什么是伪数组,怎么将伪数组转换为数组

1.什么是伪数组

常见的,function里面的arguments,和document.querySelectorAll()获取的元素数组是伪数组。从定义上说:

  • 伪数组是一个对象
  • 支持按索引获值
  • 拥有length属性
  • 支持数组遍历
  • 没有Array相关的方法
function arrayLike() {
    console.log('arguments', arguments); // [Arguments] { '0': 1, '1': 2, '2': 'c' }
    console.log(arguments.__proto__ === Object.prototype); // true
}

arrayLike(1, 2, 'c');

2.将伪数组转换为数组的方法

1. 解构赋值

[...arguments]

2. Array.from

Array.from(arguments)

3.Array.prototype.slice

Array.prototype.slice.call(arguments)