Javascript 实用小技巧大大大全,你所不知道的 ECMAScript

Map 介绍

语法

array.map(callback[, thisArg])

参数

callback

原数组中的元素经过该方法后返回一个新的元素。

currentValue

callback 的第一个参数,数组中当前被传递的元素。

index

callback 的第二个参数,数组中当前被传递的元素的索引。

array

callback 的第三个参数,调用 map 方法的数组。

thisArg

执行 callback 函数时 this 指向的对象。

返回值

由回调函数的返回值组成的新数组。

例题

https://www.codewars.com/kata/double-char

Given a string, you have to return a string in which each character (case-sensitive) is repeated once.

doubleChar("String") ==> "SSttrriinngg"

doubleChar("Hello World") ==> "HHeelllloo  WWoorrlldd"

doubleChar("1234!_ ") ==> "11223344!!__  "

Good Luck!

答案:

const doubleChar = (str) =>
  str
    .split('')
    .map((i) => i.repeat(2))
    .join('');

Reduce 介绍

语法

arr.reduce(callback, [initialValue]);

参数

callback

执行数组中每个值的函数,包含四个参数:

accumulator

上一次调用回调返回的值,或者是提供的初始值(initialValue)

currentValue

数组中正在处理的元素

currentIndex

数据中正在处理的元素索引,如果没有提供 initialValues,默认从 0 开始

array

调用 reduce 的数组

initialValue

作为第一次调用 callback 的第一个参数。

返回值

函数累计处理的结果。

例题

https://www.codewars.com/kata/beginner-reduce-but-grow

Given and array of integers (x), return the result of multiplying the values together in order. Example:

[1, 2, 3] --> 6

For the beginner, try to use the reduce method - it comes in very handy quite a lot so is a good one to know.

Array will not be empty.

答案:

const grow = (x) => x.reduce((r, i) => r * i, 1);

正则替换

参考:

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/RegExp

工具:

  • RegExp tester (Chrome 插件)

例题

Write a function that accepts an array of 10 integers (between 0 and 9), that returns a string of those numbers in the form of a phone number.

Example:

createPhoneNumber([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]) // => returns "(123) 456-7890"

The returned format must be correct in order to complete this challenge.
Don’t forget the space after the closing parenthese!

题目地址: https://www.codewars.com/kata/create-phone-number

答案:

const createPhoneNumber = (n) => n.join('').replace(/(\d{3})(\d{3})(\d{3})/, '($1) $2-$3');

异常捕获

ES5 中的传统做法

假设代码块执行抛出错误 fail,那么捕获该错误的写法为:

try {
    
    
  // 代码块执行,并抛出 fail 错误
  throw new Error('fail');
} catch (e) {
    
    
  console.log(e);
}

定时器

我们先来针对上面的代码改写一下,加入一个定时器。

try {
    
    
  setTimeout(() => {
    
    
    throw new Error('fail');
    // Uncaught Error: fail
  }, 1000);
} catch (e) {
    
    
  console.log(e);
}

像这样,将 try/catch 扔在定时器的外面,是无法捕获到内部的错误的。

正确的做法应该是:

setTimeout(() => {
    
    
  try {
    
    
    throw new Error('fail');
  } catch (e) {
    
    
    console.log(e);
  }
}, 1000);

Promise

function doSomething() {
    
    
  return new Promise((resolve, reject) => {
    
    
    // 同步代码中的 throw 可以被捕捉到
    throw new Error('fail');
  });
}

doSomething()
  .then((x) => {
    
    
    console.log('success:', x);
  })
  .catch((err) => {
    
    
    console.log('fail:', err);
  });

这样写是没有问题的,错误能够被捕获到。但只要稍微修改一下,可能就出现问题了。比如:

function doSomething() {
    
    
  return new Promise((resolve, reject) => {
    
    
    // 异步代码中的 throw 不能被 Promise 的 catch 捕捉到
    setTimeout(() => {
    
    
      throw new Error('fail');
    }, 1000);
  });
}

doSomething()
  .then((x) => {
    
    
    console.log('success:', x);
  })
  .catch((err) => {
    
    
    console.log('fail:', err);
  });

这里抛出但错误将不能被捕获。所以,在 Promise 中,我们一般通过 reject 来抛出错误。

function doSomething(x) {
    
    
  return new Promise((resolve, reject) => reject(x));
}

doSomething('fail')
  .then((x) => {
    
    
    console.log('success:', x);
  })
  .catch((err) => {
    
    
    console.log('fail:', err);
  });
// fail: fail

另外,还有一个比较有意思的细节,在 catch 之后继续添加 .then 会被继续执行。

function doSomething(x) {
    
    
  return new Promise((resolve, reject) => reject(x));
}

doSomething('fail')
  .then((x) => {
    
    
    console.log('success:', x);
  })
  .catch((err) => {
    
    
    console.log('fail:', err);
    // 这里可以写 return 给下面的方法继续执行
  })
  .then((x) => {
    
    
    console.log('continue:', x);
  });
// fail: fail
// continue: undefined

Async/Await

本质上来讲, Async/Await 是通过 Promise 实现,所以基本跟上面 Promise 所讲的差不多。

可以在 await 方法外嵌套 try/catch,类似这样:

function doSomething(x) {
    
    
  return new Promise((resolve, reject) => reject(x));
}

(async () => {
    
    
  try {
    
    
    const result = await doSomething('fail');
    console.log('success:', result);
    // return 返回
  } catch (err) {
    
    
    console.log('fail:', err);
    // return 返回
  }
})();
// fail: fail

但这里就有一个问题,比如函数需要有返回,那么返回的语句就需要写两次,正常但时候返回结果,错误的时候,返回一个 throw new Error() 或者其他的。有一个小的窍门,可以这样写:

function doSomething(x) {
    
    
  return new Promise((resolve, reject) => reject(x));
}

(async () => {
    
    
  const result = await doSomething('fail').catch((err) => {
    
    
    console.log('fail:', err);
    return 0; // 默认值
  });
  console.log('success:', result);
})();
// fail: fail
// success: 0

在错误捕获到之后,重新分配一个默认值,让代码继续运行。

事件委派

如对 AJAX 请求返回结果中的按钮进行 click 事件绑定:

var handleClick = function (e) {
    
    
  if (e.target && e.target.nodeName.toLowerCase() === 'button') {
    
    
    // Codes Here
  }
};

parentNode.addEventListener('click', handleClick);

Deep Clone

问题现象:

var obj1 = {
    
    
  key1: 'value1',
  key2: 'value2',
  children: {
    
    
    key3: 'value3',
    key4: 'value4'
  }
};
var obj2 = Object.assign({
    
    }, obj1);
obj2.children.key3 = 'test';
console.log(obj1.children);
// { key3: 'test', key4: 'value4' }

快速解决方案:

const obj2 = JSON.parse(JSON.stringify(obj1));

Scroll Debounce

用于解决前端滚动侦听事件触发频率过高问题。

核心代码:

function debounce(func, wait = 20, immediate = true) {
    
    
  var timeout;
  return function () {
    
    
    var context = this,
      args = arguments;
    var later = function () {
    
    
      timeout = null;
      if (!immediate) func.apply(context, args);
    };
    var callNow = immediate && !timeout;
    clearTimeout(timeout);
    timeout = setTimeout(later, wait);
    if (callNow) func.apply(context, args);
  };
}

示例代码:

function testFunc(e) {
    
    
  console.count(e);
}

window.addEventListener('scroll', () => console.count('bounce'));
window.addEventListener('scroll', debounce(testFunc));

在浏览器中测试。

遍历用 Map 还是 For

同是遍历,但实际有很大不同。

对比

map

改变自身。

[1, 2, 3, 4, 5].map((x) => x + 1);
// [ 2, 3, 4, 5, 6 ]

for

只是循环。

Benchmark 测试

benchmark 脚本:

suite('iterator', function () {
    
    
  bench('for', function () {
    
    
    const a = [1, 2, 3, 4, 5];
    for (let i = 0; i < a.length; i++) {
    
    
      // nothing
    }
  });
  bench('foreach', function () {
    
    
    const a = [1, 2, 3, 4, 5];
    a.forEach(function (d) {
    
    
      // nothing
    });
  });
  bench('for of', function () {
    
    
    const a = [1, 2, 3, 4, 5];
    for (let i of a) {
    
    
      // nothing
    }
  });
  bench('map', function () {
    
    
    const a = [1, 2, 3, 4, 5];
    a.map((x) => x);
  });
});

测试结果:

                      iterator
      50,038,931 op/s » for
       8,980,276 op/s » foreach
       8,990,758 op/s » for of
       1,713,807 op/s » map


  Suites:  1
  Benches: 4
  Elapsed: 5,710.33 ms

结论

单凭循环 for 最可靠。

foreachfor ... of 差不多。

map 性能最低。

触发 react onchange 事件并赋值

var setValue = function (element, value) {
    
    
  element.value = value;
  if ('createEvent' in document) {
    
    
    var event = new Event('input', {
    
     bubbles: true });
    element.dispatchEvent(event);
  } else {
    
    
    element.fireEvent('onchange');
  }
};

Pipeline Operator (Proposal)

目前处于草稿阶段: https://github.com/tc39/proposal-pipeline-operator

function doubleSay(str) {
    
    
  return `${
      
      str}, ${
      
      str}`;
}
function capitalize(str) {
    
    
  return str[0].toUpperCase() + str.substring(1);
}
function exclaim(str) {
    
    
  return `${
      
      str}!`;
}

let result = 'hello' |> doubleSay |> capitalize |> exclaim;

result |> console.log;
//=> "Hello, hello!"

项目模板: https://github.com/willin/esnext-pipeline-biolerplate

Async (ES 7)

async function fn(args) {
    
    
  // ...
}

// 等同于

function fn(args) {
    
    
  return spawn(function* () {
    
    
    // ...
  });
}

多个await命令后面的异步操作,如果不存在继发关系,最好让它们同时触发。

let foo = await getFoo();
let bar = await getBar();

上面代码中,getFoogetBar是两个独立的异步操作(即互不依赖),被写成继发关系。这样比较耗时,因为只有getFoo完成以后,才会执行getBar,完全可以让它们同时触发。

// 写法一
let [foo, bar] = await Promise.all([getFoo(), getBar()]);

// 写法二
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;

上面两种写法,getFoogetBar都是同时触发,这样就会缩短程序的执行时间。

Proxy (ES 6)

经测试 Node v6.1.0 之后版本已集成。

示例代码:

const proxy = new Proxy(
  {
    
    },
  {
    
    
    get: (target, property) => [target, property]
  }
);

console.log(proxy.func); // [ {}, 'func' ]
console.log(proxy.func('123')); // TypeError: proxy.func is not a function
const proxy = new Proxy(
  {
    
    },
  {
    
    
    get: (target, property) => (test) => [target, property, test]
  }
);

console.log(proxy.func); // [Function]
console.log(proxy.func('123')); // [ {}, 'func', '123' ]

ES 6 作用域

以 For 循环为例:

var funcs = [];

for (var i = 0; i < 10; i += 1) {
    
    
  funcs.push(function () {
    
    
    console.log(i);
  });
}

funcs.forEach((func) => func());
// 输出 10 十次

闭包:

var funcs = [];

for (var i = 0; i < 10; i += 1) {
    
    
  funcs.push((function(value){
    
     console.log(value); }(i));
}

funcs.forEach(func => func());
// 0 到 9 依次输出

在 ES6 中可以简化为:

const funcs = [];

for (let i = 0; i < 10; i += 1) {
    
    
  funcs.push(() => console.log(i));
}

funcs.forEach((func) => func());
// 0 到 9 依次输出

Function 创建函数

var add = new Function('first', 'second = first', 'return first + second');
console.log(add(1, 1)); // 2
console.log(add(1)); // 2
var pickFirst = new Function('..args', 'return args[0]');
console.log(pickFirst(1, 2)); // 1

ES6 中互换值

let a = 1;
let b = 2;

[a, b] = [b, a];

console.log(a, b); // 2 1

Fetch API AJAX

浏览器兼容性

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3HRKmB2K-1690025164220)(/basic/js/fetch.png)]

支持检查

if (typeof fetch === 'function' && typeof window.fetch === 'function') {
    
    
  // 支持
}

if (typeof fetch !== 'function' || typeof window.fetch !== 'function') {
    
    
  // 不支持
}

示例代码

var req = new Request('/data.json', {
    
     method: 'POST', cache: 'reload' });
fetch(req)
  .then(function (res) {
    
    
    return res.json();
  })
  .then(function (data) {
    
    
    console.log(data);
  });

跨域 Cookie 设置

credentials 凭证参数

fetch('a.com/api', {
    
     credentials: 'include' }).then(function (res) {
    
    
  // ...
});

var req = new Request('/data.json', {
    
     method: 'POST', cache: 'reload', credentials: 'include' });
fetch(req)
  .then(function (res) {
    
    
    return res.json();
  })
  .then(function (data) {
    
    
    console.log(data);
  });

参考资料

猜你喜欢

转载自blog.csdn.net/jslygwx/article/details/131871715