MST

星途 面试题库

面试题:JavaScript数组方法复杂兼容性场景下的解决方案

项目要求支持多端,包括一些老旧的移动设备浏览器,需要对数组的every、some、filter、map、reduce这一系列方法进行兼容性处理。不仅要考虑到polyfill的实现,还要兼顾性能优化以及避免全局污染。请阐述完整的解决方案,包括可能用到的工具、模块设计思路以及如何进行性能测试和对比。
18.8万 热度难度
编程语言JavaScript

知识考点

AI 面试

面试题答案

一键面试

1. Polyfill 实现

  • every
if (!Array.prototype.every) {
    Array.prototype.every = function (callback, thisArg) {
        for (let i = 0; i < this.length; i++) {
            if (!callback.call(thisArg, this[i], i, this)) {
                return false;
            }
        }
        return true;
    };
}
  • some
if (!Array.prototype.some) {
    Array.prototype.some = function (callback, thisArg) {
        for (let i = 0; i < this.length; i++) {
            if (callback.call(thisArg, this[i], i, this)) {
                return true;
            }
        }
        return false;
    };
}
  • filter
if (!Array.prototype.filter) {
    Array.prototype.filter = function (callback, thisArg) {
        let result = [];
        for (let i = 0; i < this.length; i++) {
            if (callback.call(thisArg, this[i], i, this)) {
                result.push(this[i]);
            }
        }
        return result;
    };
}
  • map
if (!Array.prototype.map) {
    Array.prototype.map = function (callback, thisArg) {
        let result = [];
        for (let i = 0; i < this.length; i++) {
            result.push(callback.call(thisArg, this[i], i, this));
        }
        return result;
    };
}
  • reduce
if (!Array.prototype.reduce) {
    Array.prototype.reduce = function (callback, initialValue) {
        let accumulator = initialValue;
        let startIndex = 0;
        if (initialValue === undefined) {
            accumulator = this[0];
            startIndex = 1;
        }
        for (let i = startIndex; i < this.length; i++) {
            accumulator = callback(accumulator, this[i], i, this);
        }
        return accumulator;
    };
}

2. 避免全局污染

  • 模块封装:将这些 polyfill 代码封装到一个模块中。例如,使用 ES6 模块:
// arrayPolyfills.js
export const addArrayPolyfills = () => {
    if (!Array.prototype.every) {
        Array.prototype.every = function (callback, thisArg) {
            for (let i = 0; i < this.length; i++) {
                if (!callback.call(thisArg, this[i], i, this)) {
                    return false;
                }
            }
            return true;
        };
    }
    // 其他方法类似
};

在需要使用的地方引入并调用 addArrayPolyfills 函数:

import {addArrayPolyfills} from './arrayPolyfills.js';
addArrayPolyfills();
  • 使用 IIFE(立即执行函数表达式)
(function () {
    if (!Array.prototype.every) {
        Array.prototype.every = function (callback, thisArg) {
            for (let i = 0; i < this.length; i++) {
                if (!callback.call(thisArg, this[i], i, this)) {
                    return false;
                }
            }
            return true;
        };
    }
    // 其他方法类似
})();

3. 性能优化

  • 减少不必要计算:在 polyfill 实现中,尽量减少重复计算。例如在 reduce 中,合理处理 initialValue 来避免多余的循环。
  • 缓存长度:在循环中缓存数组长度,避免每次循环都读取 this.length,如:
let len = this.length;
for (let i = 0; i < len; i++) {
    // 循环体
}

4. 工具

  • Babel:可以自动为目标环境添加 polyfill。配置 .babelrc 文件,设置 presets@babel/preset - env,并指定目标环境,如:
{
    "presets": [
        [
            "@babel/preset - env",
            {
                "targets": {
                    "browsers": ["ie >= 9"]
                }
            }
        ]
    ]
}
  • Webpack:结合 Babel 插件,用于打包项目,确保 polyfill 正确引入和处理。

5. 性能测试和对比

  • 工具
    • Benchmark.js:是一个简单而强大的 JavaScript 基准测试套件。例如:
import Benchmark from 'benchmark';
const suite = new Benchmark.Suite;
const arr = Array.from({length: 1000}, (_, i) => i + 1);
suite
  .add('native every', function () {
        arr.every((num) => num > 0);
    })
  .add('polyfill every', function () {
        if (!Array.prototype.every) {
            // polyfill code here
        }
        arr.every((num) => num > 0);
    })
  .on('cycle', function (event) {
        console.log(String(event.target));
    })
  .on('complete', function () {
        console.log('Fastest is'+ this.filter('fastest').map('name'));
    })
  .run({ 'async': true });
  • 对比方式
    • 分别对原生方法和 polyfill 方法进行多次测试,记录执行时间。
    • 在不同规模的数组(如长度为 100、1000、10000 等)上进行测试,观察性能变化趋势。
    • 在不同环境(如不同版本的浏览器、Node.js 环境)下测试,确保 polyfill 在老旧环境性能也能接受。