• 浏览器的 resize、scroll、keypress、mousemove 等事件在触发时,会不断地调用绑定在事件上的回调函数,极大地浪费资源,降低前端性能。为了优化体验,需要对这类事件进行调用次数的限制

  • 作用是在短时间内多次触发同一个函数,只执行最后一次,或者只在开始时执行

  • 以用户拖拽改变窗口大小,触发 resize 事件为例,在这过程中窗口的大小一直在改变,所以如果我们在 resize 事件中绑定函数,这个函数将会一直触发,而这种情况大多数情况下是无意义的,还会造成资源的大量浪费。

    1. function debounce(method, wait, immediate) {
    2. if (typeof method != "function") {
    3. throw new TypeError("Expected a function");
    4. }
    5. let timeout;
    6. // debounced函数为返回值
    7. // 使用Async/Await处理异步,如果函数异步执行,等待setTimeout执行完,拿到原函数返回值后将其返回
    8. // args为返回函数调用时传入的参数,传给method
    9. let debounced = function(...args) {
    10. return new Promise(resolve => {
    11. // 用于记录原函数执行结果
    12. let result;
    13. // 将method执行时this的指向设为debounce返回的函数被调用时的this指向
    14. let context = this;
    15. // 如果存在定时器则将其清除
    16. if (timeout) {
    17. clearTimeout(timeout);
    18. }
    19. // 立即执行需要两个条件,一是immediate为true,二是timeout未被赋值或被置为null
    20. if (immediate) {
    21. // 如果定时器不存在,则立即执行,并设置一个定时器,wait毫秒后将定时器置为null
    22. // 这样确保立即执行后wait毫秒内不会被再次触发
    23. let callNow = !timeout;
    24. timeout = setTimeout(() => {
    25. timeout = null;
    26. }, wait);
    27. // 如果满足上述两个条件,则立即执行并记录其执行结果
    28. if (callNow) {
    29. result = method.apply(context, args);
    30. resolve(result);
    31. }
    32. } else {
    33. // 如果immediate为false,则等待函数执行并记录其执行结果
    34. // 并将Promise状态置为fullfilled,以使函数继续执行
    35. timeout = setTimeout(() => {
    36. // args是一个数组,所以使用fn.apply
    37. // 也可写作method.call(context, ...args)
    38. result = method.apply(context, args);
    39. resolve(result);
    40. }, wait);
    41. }
    42. });
    43. };
    44. // 在返回的debounced函数上添加取消方法
    45. debounced.cancel = function() {
    46. clearTimeout(timeout);
    47. timeout = null;
    48. };
    49. return debounced;
    50. }
    51. var run = function() {
    52. console.log("i'm run");
    53. };
    54. window.addEventListener("resize", Ddebounce(run, 2000, false));

节流

  • 节流是指在一段时间内只允许函数执行一次
  • 应用场景如: 输入框的联想、可以限定用户在输入时,只在每两秒钟响应一次联想(或是两秒钟发送一次搜索请求)```javascript var throttle = function(func, delay) { var timer = null; return function() {
    1. var context = this;
    2. var args = arguments;
    3. if (!timer) {
    4. timer = setTimeout(function() {
    5. func.apply(context, args);
    6. timer = null;
    7. }, delay);
    8. }
    }; }; ```

柯里化

  • 函数柯里化的主要目的就是为了减少函数传参,同时将一些固定参数私有化```javascript function curry(func) { var l = func.length; return function curried() {
    1. var args = [].slice.call(arguments);
    2. if (args.length < 1) {
    3. return function() {
    4. var argtsInner = [].slice.call(arguments);
    5. return curried.apply(this, arrs.concat(argtsInner));
    6. };
    7. } else {
    8. return func.apply(this, args);
    9. }
    }; } var f = function(a, b, c) { return console.log([a, b, c]); }; var curried = curry(f); curried(1)(2)(3); ```

数组扁平化

  • 将数组进行扁平化处理,即将多维数组展开为一维数组 ```javascript // es6 const flattenES6 = arr => { let result = []; arr.forEach((item, i, arr) => {
    1. if (Array.isArray(item)) {
    2. result = result.concat(flatten(item));
    3. } else {
    4. result.push(arr[i]);
    5. }
    }); return result; }; console.log(flattenES6([1, [2, [3, [4]], 5]]));

// es5 function flattenES5(arr) { var result = []; for (var i = 0; i < arr.length; i++) { if (Array.isArray(arr[i])) { result = result.concat(flatten(arr[i])); } else { result.push(arr[i]); } } return result; } console.log(flattenES5([1, [2, [3, [4]], 5]]));

  1. -
  2. 数组扁平化 - toString()
  3. -
  4. 该方法是利用 toString 把数组变成以逗号分隔的字符串,然后遍历数组把每一项再变回原来的类型。
  5. ```javascript
  6. [1, [2, [3, [4]], 5]]; // 1,2,3,4,5
  7. // es6
  8. const flattenES6 = arr =>
  9. arr
  10. .toString()
  11. .split(",")
  12. .map(item => +item);
  13. console.log(flattenES6([1, [2, [3, [4]], 5]]));
  14. // es5
  15. function flattenES5(arr) {
  16. return arr
  17. .toString()
  18. .split(",")
  19. .map(function(item) {
  20. return +item;
  21. });
  22. }
  23. console.log(flattenES5([1, [2, [3, [4]], 5]]));

对象拷贝

  • ```javascript function copy(obj) { if (!obj || typeof obj !== “object”) {
    1. return;
    } var newObj = obj.constructor === Array ? [] : {}; for (var key in obj) {
    1. if (obj.hasOwnProperty(key)) {
    2. if (typeof obj[key] === "object" && obj[key]) {
    3. newObj[key] = copy(obj[key]);
    4. } else {
    5. newObj[key] = obj[key];
    6. }
    7. }
    } return newObj; }

var old = { a: “old”, b: { c: “old” } }; var newObj = copy(old); newObj.b.c = “new”; console.log(old); // { a: ‘old’, b: { c: ‘old’ } } console.log(newObj); // { a: ‘old’, b: { c: ‘new’ } }

  1. <a name="66d37585"></a>
  2. # call 的模拟实现
  3. - 介绍:call() 方法在使用一个指定的 this 值和若干个指定的参数值的前提下调用某个函数或方法。
  4. ```javascript
  5. Function.prototype.Dcall = function(context) {
  6. var context = context || window;
  7. context.fn = this;
  8. var args = [];
  9. for (var i = 1; i < arguments.length; i++) {
  10. args.push("arguments[" + i + "]");
  11. }
  12. var result = eval("context.fn(" + args + ")");
  13. delete context.fn;
  14. return result;
  15. };
  16. // 测试一下
  17. var value = 2;
  18. var obj = {
  19. value: 1
  20. };
  21. function bar(name, age) {
  22. console.log(this.value);
  23. return {
  24. value: this.value,
  25. name: name,
  26. age: age
  27. };
  28. }
  29. bar.Dcall(null); // 2
  30. console.log(bar.Dcall(obj, "wcd", 23));
  31. // 1
  32. // Object {
  33. // value: 1,
  34. // name: 'wcd',
  35. // age: 23
  36. // }

apply 的模拟实现

    1. Function.prototype.Dapply = function(context, arr) {
    2. var context = Object(context) || window;
    3. context.fn = this;
    4. var result;
    5. if (!arr) {
    6. result = context.fn();
    7. } else {
    8. var args = [];
    9. for (var i = 0; i < arr.length; i++) {
    10. args.push("arr[" + i + "]");
    11. }
    12. result = eval("context.fn(" + args + ")");
    13. }
    14. delete context.fn;
    15. return result;
    16. };

bind 的模拟实现

  • 介绍:bind() 方法会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数。

  • 特点:返回一个函数 or 可以传入参数

    1. Function.prototype.Dbind = function(context) {
    2. if (typeof this !== "function") {
    3. throw new Error(
    4. "Function.prototype.bind - what is trying to be bound is not callable"
    5. );
    6. }
    7. var that = this;
    8. var args = Array.prototype.slice.call(arguments, 1);
    9. var fNOP = function() {};
    10. var fBound = function() {
    11. var bindArgs = Array.prototype.slice.call(arguments);
    12. return that.apply(
    13. this instanceof fNOP ? this : context,
    14. args.concat(bindArgs)
    15. );
    16. };
    17. fNOP.prototype = this.prototype;
    18. fBound.prototype = new fNOP();
    19. return fBound;
    20. };