前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >滴滴前端面试题(边面边更)_2023-02-24

滴滴前端面试题(边面边更)_2023-02-24

原创
作者头像
java_js0166293
发布2023-02-24 09:49:37
1.1K0
发布2023-02-24 09:49:37
举报
文章被收录于专栏:前端面试20

ES6模块与CommonJS模块有什么异同?

ES6 Module和CommonJS模块的区别:

  • CommonJS是对模块的浅拷⻉,ES6 Module是对模块的引⽤,即ES6 Module只存只读,不能改变其值,也就是指针指向不能变,类似const;
  • import的接⼝是read-only(只读状态),不能修改其变量值。 即不能修改其变量的指针指向,但可以改变变量内部指针指向,可以对commonJS对重新赋值(改变指针指向),但是对ES6 Module赋值会编译报错。

ES6 Module和CommonJS模块的共同点:

  • CommonJS和ES6 Module都可以对引⼊的对象进⾏赋值,即对对象内部属性的值进⾏改变。

函数柯里化

柯里化(currying) 指的是将一个多参数的函数拆分成一系列函数,每个拆分后的函数都只接受一个参数。

对于已经柯里化后的函数来说,当接收的参数数量与原函数的形参数量相同时,执行原函数; 当接收的参数数量小于原函数的形参数量时,返回一个函数用于接收剩余的参数,直至接收的参数数量与形参数量一致,执行原函数。

CDN的原理

CDN和DNS有着密不可分的联系,先来看一下DNS的解析域名过程,在浏览器输入的解析过程如下:

(1) 检查浏览器缓存

(2)检查操作系统缓存,常见的如hosts文件

(3)检查路由器缓存

(4)如果前几步都没没找到,会向ISP(网络服务提供商)的LDNS服务器查询

(5)如果LDNS服务器没找到,会向根域名服务器(Root Server)请求解析,分为以下几步:

  • 根服务器返回顶级域名(TLD)服务器如.com.cn.org等的地址,该例子中会返回.com的地址
  • 接着向顶级域名服务器发送请求,然后会返回次级域名(SLD)服务器的地址,本例子会返回.test的地址
  • 接着向次级域名服务器发送请求,然后会返回通过域名查询到的目标IP,本例子会返回www.test.com的地址
  • Local DNS Server会缓存结果,并返回给用户,缓存在系统中

CDN的工作原理: (1)用户未使用CDN缓存资源的过程:

  1. 浏览器通过DNS对域名进行解析(就是上面的DNS解析过程),依次得到此域名对应的IP地址
  2. 浏览器根据得到的IP地址,向域名的服务主机发送数据请求
  3. 服务器向浏览器返回响应数据

(2)用户使用CDN缓存资源的过程:

  1. 对于点击的数据的URL,经过本地DNS系统的解析,发现该URL对应的是一个CDN专用的DNS服务器,DNS系统就会将域名解析权交给CNAME指向的CDN专用的DNS服务器。
  2. CND专用DNS服务器将CND的全局负载均衡设备IP地址返回给用户
  3. 用户向CDN的全局负载均衡设备发起数据请求
  4. CDN的全局负载均衡设备根据用户的IP地址,以及用户请求的内容URL,选择一台用户所属区域的区域负载均衡设备,告诉用户向这台设备发起请求
  5. 区域负载均衡设备选择一台合适的缓存服务器来提供服务,将该缓存服务器的IP地址返回给全局负载均衡设备
  6. 全局负载均衡设备把服务器的IP地址返回给用户
  7. 用户向该缓存服务器发起请求,缓存服务器响应用户的请求,将用户所需内容发送至用户终端。

如果缓存服务器没有用户想要的内容,那么缓存服务器就会向它的上一级缓存服务器请求内容,以此类推,直到获取到需要的资源。最后如果还是没有,就会回到自己的服务器去获取资源。

CNAME(意为:别名):在域名解析中,实际上解析出来的指定域名对应的IP地址,或者该域名的一个CNAME,然后再根据这个CNAME来查找对应的IP地址。

快排--时间复杂度 nlogn~ n^2 之间

题目描述:实现一个快排

实现代码如下:

代码语言:javascript
复制
function quickSort(arr) {
  if (arr.length < 2) {
    return arr;
  }
  const cur = arr[arr.length - 1];
  const left = arr.filter((v, i) => v <= cur && i !== arr.length - 1);
  const right = arr.filter((v) => v > cur);
  return [...quickSort(left), cur, ...quickSort(right)];
}
// console.log(quickSort([3, 6, 2, 4, 1]));

documentFragment 是什么?用它跟直接操作 DOM 的区别是什么?

MDN中对documentFragment的解释:

DocumentFragment,文档片段接口,一个没有父对象的最小文档对象。它被作为一个轻量版的 Document使用,就像标准的document一样,存储由节点(nodes)组成的文档结构。与document相比,最大的区别是DocumentFragment不是真实 DOM 树的一部分,它的变化不会触发 DOM 树的重新渲染,且不会导致性能等问题。

当我们把一个 DocumentFragment 节点插入文档树时,插入的不是 DocumentFragment 自身,而是它的所有子孙节点。在频繁的DOM操作时,我们就可以将DOM元素插入DocumentFragment,之后一次性的将所有的子孙节点插入文档中。和直接操作DOM相比,将DocumentFragment 节点插入DOM树时,不会触发页面的重绘,这样就大大提高了页面的性能。

如何对项目中的图片进行优化?

  1. 不用图片。很多时候会使用到很多修饰类图片,其实这类修饰图片完全可以用 CSS 去代替。
  2. 对于移动端来说,屏幕宽度就那么点,完全没有必要去加载原图浪费带宽。一般图片都用 CDN 加载,可以计算出适配屏幕的宽度,然后去请求相应裁剪好的图片。
  3. 小图使用 base64 格式
  4. 将多个图标文件整合到一张图片中(雪碧图)
  5. 选择正确的图片格式:
    • 对于能够显示 WebP 格式的浏览器尽量使用 WebP 格式。因为 WebP 格式具有更好的图像数据压缩算法,能带来更小的图片体积,而且拥有肉眼识别无差异的图像质量,缺点就是兼容性并不好
    • 小图使用 PNG,其实对于大部分图标这类图片,完全可以使用 SVG 代替
    • 照片使用 JPEG

深拷贝

实现一:不考虑 Symbol

代码语言:javascript
复制
function deepClone(obj) {
    if(!isObject(obj)) return obj;
    let newObj = Array.isArray(obj) ? [] : {};
    // for...in 只会遍历对象自身的和继承的可枚举的属性(不含 Symbol 属性)
    for(let key in obj) {
        // obj.hasOwnProperty() 方法只考虑对象自身的属性
        if(obj.hasOwnProperty(key)) {
            newObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key];
        }
    }
    return newObj;
}

实现二:考虑 Symbol

代码语言:javascript
复制
// hash 作为一个检查器,避免对象深拷贝中出现环引用,导致爆栈
function deepClone(obj, hash = new WeakMap()) {
    if(!isObject(obj)) return obj;
    // 检查是有存在相同的对象在之前拷贝过,有则返回之前拷贝后存于hash中的对象
    if(hash.has(obj)) return hash.get(obj);
    let newObj = Array.isArray(obj) ? [] : {};
    // 备份存在hash中,newObj目前是空对象、数组。后面会对属性进行追加,这里存的值是对象的栈
    hash.set(obj, newObj);
    // Reflect.ownKeys返回一个数组,包含对象自身的(不含继承的)所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举。
    Reflect.ownKeys(obj).forEach(key => {
        // 属性值如果是对象,则进行递归深拷贝,否则直接拷贝
        newObj[key] = isObject(obj[key]) ? deepClone(obj[key], hash) : obj[key];
    });
    return newObj;
}

代码输出结果

代码语言:javascript
复制
function Person(name) {
    this.name = name
}
var p2 = new Person('king');
console.log(p2.__proto__) //Person.prototype
console.log(p2.__proto__.__proto__) //Object.prototype
console.log(p2.__proto__.__proto__.__proto__) // null
console.log(p2.__proto__.__proto__.__proto__.__proto__)//null后面没有了,报错
console.log(p2.__proto__.__proto__.__proto__.__proto__.__proto__)//null后面没有了,报错
console.log(p2.constructor)//Person
console.log(p2.prototype)//undefined p2是实例,没有prototype属性
console.log(Person.constructor)//Function 一个空函数
console.log(Person.prototype)//打印出Person.prototype这个对象里所有的方法和属性
console.log(Person.prototype.constructor)//Person
console.log(Person.prototype.__proto__)// Object.prototype
console.log(Person.__proto__) //Function.prototype
console.log(Function.prototype.__proto__)//Object.prototype
console.log(Function.__proto__)//Function.prototype
console.log(Object.__proto__)//Function.prototype
console.log(Object.prototype.__proto__)//null

这道义题目考察原型、原型链的基础,记住就可以了。

动态规划求解硬币找零问题

题目描述:给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1

代码语言:javascript
复制
示例1:
输入: coins = [1, 2, 5], amount = 11
输出: 3
解释: 11 = 5 + 5 + 1

示例2:
输入: coins = [2], amount = 3
输出: -1

实现代码如下:

代码语言:javascript
复制
const coinChange = function (coins, amount) {
  // 用于保存每个目标总额对应的最小硬币个数
  const f = [];
  // 提前定义已知情况
  f[0] = 0;
  // 遍历 [1, amount] 这个区间的硬币总额
  for (let i = 1; i <= amount; i++) {
    // 求的是最小值,因此我们预设为无穷大,确保它一定会被更小的数更新
    f[i] = Infinity;
    // 循环遍历每个可用硬币的面额
    for (let j = 0; j < coins.length; j++) {
      // 若硬币面额小于目标总额,则问题成立
      if (i - coins[j] >= 0) {
        // 状态转移方程
        f[i] = Math.min(f[i], f[i - coins[j]] + 1);
      }
    }
  }
  // 若目标总额对应的解为无穷大,则意味着没有一个符合条件的硬币总数来更新它,本题无解,返回-1
  if (f[amount] === Infinity) {
    return -1;
  }
  // 若有解,直接返回解的内容
  return f[amount];
};

如何提⾼webpack的打包速度?

(1)优化 Loader

对于 Loader 来说,影响打包效率首当其冲必属 Babel 了。因为 Babel 会将代码转为字符串生成 AST,然后对 AST 继续进行转变最后再生成新的代码,项目越大,转换代码越多,效率就越低。当然了,这是可以优化的。

首先我们优化 Loader 的文件搜索范围

代码语言:javascript
复制
module.exports = {
  module: {
    rules: [
      {
        // js 文件才使用 babel
        test: /\.js$/,
        loader: 'babel-loader',
        // 只在 src 文件夹下查找
        include: [resolve('src')],
        // 不会去查找的路径
        exclude: /node_modules/
      }
    ]
  }
}

对于 Babel 来说,希望只作用在 JS 代码上的,然后 node_modules 中使用的代码都是编译过的,所以完全没有必要再去处理一遍。

当然这样做还不够,还可以将 Babel 编译过的文件缓存起来,下次只需要编译更改过的代码文件即可,这样可以大幅度加快打包时间

代码语言:javascript
复制
loader: 'babel-loader?cacheDirectory=true'
(2)HappyPack

受限于 Node 是单线程运行的,所以 Webpack 在打包的过程中也是单线程的,特别是在执行 Loader 的时候,长时间编译的任务很多,这样就会导致等待的情况。

HappyPack 可以将 Loader 的同步执行转换为并行的,这样就能充分利用系统资源来加快打包效率了

代码语言:javascript
复制
module: {
  loaders: [
    {
      test: /\.js$/,
      include: [resolve('src')],
      exclude: /node_modules/,
      // id 后面的内容对应下面
      loader: 'happypack/loader?id=happybabel'
    }
  ]
},
plugins: [
  new HappyPack({
    id: 'happybabel',
    loaders: ['babel-loader?cacheDirectory'],
    // 开启 4 个线程
    threads: 4
  })
]
(3)DllPlugin

DllPlugin 可以将特定的类库提前打包然后引入。这种方式可以极大的减少打包类库的次数,只有当类库更新版本才有需要重新打包,并且也实现了将公共代码抽离成单独文件的优化方案。DllPlugin的使用方法如下:

代码语言:javascript
复制
// 单独配置在一个文件中
// webpack.dll.conf.js
const path = require('path')
const webpack = require('webpack')
module.exports = {
  entry: {
    // 想统一打包的类库
    vendor: ['react']
  },
  output: {
    path: path.join(__dirname, 'dist'),
    filename: '[name].dll.js',
    library: '[name]-[hash]'
  },
  plugins: [
    new webpack.DllPlugin({
      // name 必须和 output.library 一致
      name: '[name]-[hash]',
      // 该属性需要与 DllReferencePlugin 中一致
      context: __dirname,
      path: path.join(__dirname, 'dist', '[name]-manifest.json')
    })
  ]
}

然后需要执行这个配置文件生成依赖文件,接下来需要使用 DllReferencePlugin 将依赖文件引入项目中

代码语言:javascript
复制
// webpack.conf.js
module.exports = {
  // ...省略其他配置
  plugins: [
    new webpack.DllReferencePlugin({
      context: __dirname,
      // manifest 就是之前打包出来的 json 文件
      manifest: require('./dist/vendor-manifest.json'),
    })
  ]
}
(4)代码压缩

在 Webpack3 中,一般使用 UglifyJS 来压缩代码,但是这个是单线程运行的,为了加快效率,可以使用 webpack-parallel-uglify-plugin 来并行运行 UglifyJS,从而提高效率。

在 Webpack4 中,不需要以上这些操作了,只需要将 mode 设置为 production 就可以默认开启以上功能。代码压缩也是我们必做的性能优化方案,当然我们不止可以压缩 JS 代码,还可以压缩 HTML、CSS 代码,并且在压缩 JS 代码的过程中,我们还可以通过配置实现比如删除 console.log 这类代码的功能。

(5)其他

可以通过一些小的优化点来加快打包速度

  • resolve.extensions:用来表明文件后缀列表,默认查找顺序是 ['.js', '.json'],如果你的导入文件没有添加后缀就会按照这个顺序查找文件。我们应该尽可能减少后缀列表长度,然后将出现频率高的后缀排在前面
  • resolve.alias:可以通过别名的方式来映射一个路径,能让 Webpack 更快找到路径
  • module.noParse:如果你确定一个文件下没有其他依赖,就可以使用该属性让 Webpack 不扫描该文件,这种方式对于大型的类库很有帮助

代码输出结果

代码语言:javascript
复制
Promise.resolve(1)
  .then(2)
  .then(Promise.resolve(3))
  .then(console.log)

输出结果如下:

代码语言:javascript
复制
1

看到这个题目,好多的then,实际上只需要记住一个原则:.then.catch 的参数期望是函数,传入非函数则会发生值透传

第一个then和第二个then中传入的都不是函数,一个是数字,一个是对象,因此发生了透传,将resolve(1) 的值直接传到最后一个then里,直接打印出1。

实现节流函数和防抖函数

函数防抖的实现:

代码语言:javascript
复制
function debounce(fn, wait) {
  var timer = null;

  return function() {
    var context = this,
      args = [...arguments];

    // 如果此时存在定时器的话,则取消之前的定时器重新记时
    if (timer) {
      clearTimeout(timer);
      timer = null;
    }

    // 设置定时器,使事件间隔指定事件后执行
    timer = setTimeout(() => {
      fn.apply(context, args);
    }, wait);
  };
}

函数节流的实现:

代码语言:javascript
复制
// 时间戳版
function throttle(fn, delay) {
  var preTime = Date.now();

  return function() {
    var context = this,
      args = [...arguments],
      nowTime = Date.now();

    // 如果两次时间间隔超过了指定时间,则执行函数。
    if (nowTime - preTime >= delay) {
      preTime = Date.now();
      return fn.apply(context, args);
    }
  };
}

// 定时器版
function throttle (fun, wait){
  let timeout = null
  return function(){
    let context = this
    let args = [...arguments]
    if(!timeout){
      timeout = setTimeout(() => {
        fun.apply(context, args)
        timeout = null 
      }, wait)
    }
  }
}

对事件循环的理解

因为 js 是单线程运行的,在代码执行时,通过将不同函数的执行上下文压入执行栈中来保证代码的有序执行。在执行同步代码时,如果遇到异步事件,js 引擎并不会一直等待其返回结果,而是会将这个事件挂起,继续执行执行栈中的其他任务。当异步事件执行完毕后,再将异步事件对应的回调加入到一个任务队列中等待执行。任务队列可以分为宏任务队列和微任务队列,当当前执行栈中的事件执行完毕后,js 引擎首先会判断微任务队列中是否有任务可以执行,如果有就将微任务队首的事件压入栈中执行。当微任务队列中的任务都执行完成后再去执行宏任务队列中的任务。

Event Loop 执行顺序如下所示:

  • 首先执行同步代码,这属于宏任务
  • 当执行完所有同步代码后,执行栈为空,查询是否有异步代码需要执行
  • 执行所有微任务
  • 当执行完所有微任务后,如有必要会渲染页面
  • 然后开始下一轮 Event Loop,执行宏任务中的异步代码

分片思想解决大数据量渲染问题

题目描述:渲染百万条结构简单的大数据时 怎么使用分片思想优化渲染

实现代码如下:

代码语言:javascript
复制
let ul = document.getElementById("container");
// 插入十万条数据
let total = 100000;
// 一次插入 20 条
let once = 20;
//总页数
let page = total / once;
//每条记录的索引
let index = 0;
//循环加载数据
function loop(curTotal, curIndex) {
  if (curTotal <= 0) {
    return false;
  }
  //每页多少条
  let pageCount = Math.min(curTotal, once);
  window.requestAnimationFrame(function () {
    for (let i = 0; i < pageCount; i++) {
      let li = document.createElement("li");
      li.innerText = curIndex + i + " : " + ~~(Math.random() * total);
      ul.appendChild(li);
    }
    loop(curTotal - pageCount, curIndex + pageCount);
  });
}
loop(total, index);

扩展思考:对于大数据量的简单 dom 结构渲染可以用分片思想解决 如果是复杂的 dom 结构渲染如何处理?

这时候就需要使用虚拟列表了 大家自行百度哈 虚拟列表和虚拟表格在日常项目使用还是很频繁的

代码输出结果

代码语言:javascript
复制
Promise.resolve('1')
  .then(res => {
    console.log(res)
  })
  .finally(() => {
    console.log('finally')
  })
Promise.resolve('2')
  .finally(() => {
    console.log('finally2')
      return '我是finally2返回的值'
  })
  .then(res => {
    console.log('finally2后面的then函数', res)
  })

输出结果如下:

代码语言:javascript
复制
1
finally2
finally
finally2后面的then函数 2

.finally()一般用的很少,只要记住以下几点就可以了:

  • .finally()方法不管Promise对象最后的状态如何都会执行
  • .finally()方法的回调函数不接受任何的参数,也就是说你在.finally()函数中是无法知道Promise最终的状态是resolved还是rejected
  • 它最终返回的默认会是一个上一次的Promise对象值,不过如果抛出的是一个异常则返回异常的Promise对象。
  • finally本质上是then方法的特例

.finally()的错误捕获:

代码语言:javascript
复制
Promise.resolve('1')
  .finally(() => {
    console.log('finally1')
    throw new Error('我是finally中抛出的异常')
  })
  .then(res => {
    console.log('finally后面的then函数', res)
  })
  .catch(err => {
    console.log('捕获错误', err)
  })

输出结果为:

代码语言:javascript
复制
'finally1'
'捕获错误' Error: 我是finally中抛出的异常

插入排序--时间复杂度 n^2

题目描述:实现一个插入排序

实现代码如下:

代码语言:javascript
复制
function insertSort(arr) {
  for (let i = 1; i < arr.length; i++) {
    let j = i;
    let target = arr[j];
    while (j > 0 && arr[j - 1] > target) {
      arr[j] = arr[j - 1];
      j--;
    }
    arr[j] = target;
  }
  return arr;
}
// console.log(insertSort([3, 6, 2, 4, 1]));

发布订阅模式(事件总线)

描述:实现一个发布订阅模式,拥有 on, emit, once, off 方法

代码语言:javascript
复制
class EventEmitter {
    constructor() {
        // 包含所有监听器函数的容器对象
        // 内部结构: {msg1: [listener1, listener2], msg2: [listener3]}
        this.cache = {};
    }
    // 实现订阅
    on(name, callback) {
        if(this.cache[name]) {
            this.cache[name].push(callback);
        }
        else {
            this.cache[name] = [callback];
        }
    }
    // 删除订阅
    off(name, callback) {
        if(this.cache[name]) {
            this.cache[name] = this.cache[name].filter(item => item !== callback);
        }
        if(this.cache[name].length === 0) delete this.cache[name];
    }
    // 只执行一次订阅事件
    once(name, callback) {
        callback();
        this.off(name, callback);
    }
    // 触发事件
    emit(name, ...data) {
        if(this.cache[name]) {
            // 创建副本,如果回调函数内继续注册相同事件,会造成死循环
            let tasks = this.cache[name].slice();
            for(let fn of tasks) {
                fn(...data);
            }
        }
    }
}

哪些操作会造成内存泄漏?

  • 第一种情况是由于使用未声明的变量,而意外的创建了一个全局变量,而使这个变量一直留在内存中无法被回收。
  • 第二种情况是设置了 setInterval 定时器,而忘记取消它,如果循环函数有对外部变量的引用的话,那么这个变量会被一直留在内存中,而无法被回收。
  • 第三种情况是获取一个 DOM 元素的引用,而后面这个元素被删除,由于我们一直保留了对这个元素的引用,所以它也无法被回收。
  • 第四种情况是不合理的使用闭包,从而导致某些变量一直被留在内存当中。

代码输出结果

代码语言:javascript
复制
// a
function Foo () {
 getName = function () {
   console.log(1);
 }
 return this;
}
// b
Foo.getName = function () {
 console.log(2);
}
// c
Foo.prototype.getName = function () {
 console.log(3);
}
// d
var getName = function () {
 console.log(4);
}
// e
function getName () {
 console.log(5);
}

Foo.getName();           // 2
getName();               // 4
Foo().getName();         // 1
getName();               // 1 
new Foo.getName();       // 2
new Foo().getName();     // 3
new new Foo().getName(); // 3

输出结果:2 4 1 1 2 3 3

解析:

  1. Foo.getName(), Foo为一个函数对象,对象都可以有属性,b 处定义Foo的getName属性为函数,输出2;
  2. getName(), 这里看d、e处,d为函数表达式,e为函数声明,两者区别在于变量提升,函数声明的 5 会被后边函数表达式的 4 覆盖;
  3. Foo().getName(), 这里要看a处,在Foo内部将全局的getName重新赋值为 console.log(1) 的函数,执行Foo()返回 this,这个this指向window,Foo().getName() 即为window.getName(),输出 1;
  4. getName(), 上面3中,全局的getName已经被重新赋值,所以这里依然输出 1;
  5. new Foo.getName(), 这里等价于 new (Foo.getName()),先执行 Foo.getName(),输出 2,然后new一个实例;
  6. new Foo().getName(), 这 里等价于 (new Foo()).getName(), 先new一个Foo的实例,再执行这个实例的getName方法,但是这个实例本身没有这个方法,所以去原型链protot上边找,实例.protot === Foo.prototype,所以输出 3;
  7. new new Foo().getName(), 这里等价于new (new Foo().getName()),如上述6,先输出 3,然后new 一个 new Foo().getName() 的实例。

什么是执行栈

可以把执行栈认为是一个存储函数调用的栈结构,遵循先进后出的原则。 当开始执行 JS 代码时,根据先进后出的原则,后执行的函数会先弹出栈,可以看到,foo 函数后执行,当执行完毕后就从栈中弹出了。

平时在开发中,可以在报错中找到执行栈的痕迹:

代码语言:javascript
复制
function foo() {
  throw new Error('error')
}
function bar() {
  foo()
}
bar()

可以看到报错在 foo 函数,foo 函数又是在 bar 函数中调用的。当使用递归时,因为栈可存放的函数是有限制的,一旦存放了过多的函数且没有得到释放的话,就会出现爆栈的问题

代码语言:javascript
复制
function bar() {  bar()}bar()

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • ES6模块与CommonJS模块有什么异同?
  • 函数柯里化
  • CDN的原理
  • 快排--时间复杂度 nlogn~ n^2 之间
  • documentFragment 是什么?用它跟直接操作 DOM 的区别是什么?
  • 如何对项目中的图片进行优化?
  • 深拷贝
  • 代码输出结果
  • 动态规划求解硬币找零问题
  • 如何提⾼webpack的打包速度?
    • (1)优化 Loader
      • (2)HappyPack
        • (3)DllPlugin
          • (4)代码压缩
            • (5)其他
            • 代码输出结果
            • 实现节流函数和防抖函数
            • 对事件循环的理解
            • 分片思想解决大数据量渲染问题
            • 代码输出结果
            • 插入排序--时间复杂度 n^2
            • 发布订阅模式(事件总线)
            • 哪些操作会造成内存泄漏?
            • 代码输出结果
            • 什么是执行栈
            相关产品与服务
            内容分发网络 CDN
            内容分发网络(Content Delivery Network,CDN)通过将站点内容发布至遍布全球的海量加速节点,使其用户可就近获取所需内容,避免因网络拥堵、跨运营商、跨地域、跨境等因素带来的网络不稳定、访问延迟高等问题,有效提升下载速度、降低响应时间,提供流畅的用户体验。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档