超使用的十个JavaScript技巧

news2024/11/25 11:00:10

前端面试题库 (面试必备)            推荐:★★★★★

地址:前端面试题库

JavaScript 作为最流行的语言之一,其语法灵活且每年都在不断吸纳新特性,即使是一个从业多年的老手, 偶尔也会有一些被低估的 JavaScript 功能和技巧,本文将分享这些技巧,一同讨论探究,不用过于较真标题,只是一种写作手法!(逃)

1. 使用 flatMap

有些 JavaScript 方法尽管鲜为人知,但它们解决独特挑战的潜力能够增强编码效率, 比如 flatMap()

数组方法 flatMap() 本质上是 map()和 flat() 的组合,区别在于 flatMap 只能扁平1级,flat 可以指定需要扁平的级数,flatmap 比分别调用这两个方法稍微高效一些。

使用 flat + map

const arr = [1, 2, [4, 5], 6, 7, [8]];

// 使用 map 对每个元素进行操作并用 flat 展平结果
const result = arr.map(element => Array.isArray(element) ? element : [element]).flat();

console.log(result); // output: [1, 2, 4, 5, 6, 7, 8]

使用 flatmap

const arr = [1, 2, [4, 5], 6, 7, [8]] ;

console.log(arr.flatMap((element) => element)); 
// output :[1, 2, 4, 5, 6, 7, 8]

flatmap 尽管是一个方法,但也会有 中间数组 (指中间创建了必须进行垃圾收集的临时数组)的产生,flatMap 非常适合在需要灵活性和可读性的情况下使用。

2. 数组方法的顺序

javascript 有数十种数组方法, 它们可以组合在一起使用,形式类似:

const numbers = [9, 3, 6, 4, 8, 1, 2, 5, 7];

// 仅针对奇数进行排序,并将它们提升为3的幂
numbers
  .sort((a, b) => a - b)
  .filter((n) => n % 2 !== 0)
  .map((n) => n ** 3);

上面的代码看起来不错,但是有个问题 - 这里对数组先进行排序,再进行过滤。 如果先进行过滤,再排序, 我们可以完成更少的任务,从而完成代码的优化。

const numbers = [9, 3, 6, 4, 8, 1, 2, 5, 7];

numbers
  .filter((n) => n % 2 !== 0)
  .sort((a, b) => a - b)
  .map((n) => n ** 3);

3. 充分使用 reduce

在写 javascript 时,有时候需要以键值分组的格式提供数据,大多数开发者会使用 .forEach() 方法或者 map() 方法,类似于这样的方式。

fetch("https://jsonplaceholder.typicode.com/todos/")
  .then(res=>res.json())
  .then(todos=>{
    
    // using forEach() or Map
    const todosForUserMap = {};
    todos.forEach(todo=>{
      if (todosForUserMap[todo.userId]){
        todosForUserMap[todo.userId].push(todo);  
      }else{
        todosForUserMap[todo.userId] = [todo];
      }  
    })

    console.log(todosForUserMap)
  })

这里使用 forEach 而不是 map 方法。是因为使用 map 方法会返回一个新的数组,而数组创建和赋值产生的性能开销较大,尤其是在数据量较大时, 而这不会在 forEach 中发生

还有一种相当干净且可读性强的方法是使用数组的 reduce 方法

fetch("https://jsonplaceholder.typicode.com/todos/")
  .then(res=>res.json())
  .then(todos=>{
    
    // using reduce
    const todosForUserMap = todos.reduce((accumulator, todo)=>{
      if (accumulator[todo.userId]) accumulator[todo.userId].push(todo);
      if (!accumulator[todo.userId]) accumulator[todo.userId] = [todo];
      return accumulator;
    },{})

    console.log(todosForUserMap)
  })

4. 充分使用 generator

生成器 (Generator) 和 迭代器 (iterators) 可能是 JavaScript 开发人员最不常使用的代码,其知识仅限于编码面试。(因为有更好用的语法糖 async/await ?😂)

生成器 (Generator) 是控制异步编程、生成可迭代对象和生成多个值的强大方法。生成器与传统函数不同。他们可以多次启动和停止执行。这使它们能够产生大量值并在以后继续执行,从而使它们非常适合管理异步操作、构造迭代器和处理无尽的数据流。

试想一下,假如在一个获取数据的场景下,数据库/ API 的数据量可能是无限的,而你必须将它们传输到前端,你会怎么做呢?

这种情况下, react 中最常用的方案就是无限加载方案, 如果是在 node 中或者原生JS,你该如何实现无限加载之类的功能

async function *fetchProducts(){
  while (true){
    const productUrl = "https://fakestoreapi.com/products?limit=2";
    const res = await fetch(productUrl)
    const data = await res.json()
    yield data;
        // 在这里操作用户界面
        // 或将其保存在数据库或其他地方
        // 将其用作副作用的地方
        // 即使某些条件匹配,也中断流程
  }
}

async function main() {
  const itr = fetchProducts();
        // 这应该根据用户交互来调用
        // 或者其他技巧,因为您不希望出现无限加载。
  console.log( await itr.next() );
}

return main()

这就是 迭代器 (iterators) 真正有用的地方,而不是将请求的大量数据流式传输到本地存储或者某些位置。这是使用 异步生成器(async generators) 执行此操作的这样之一, 这样我们就可以解决JS中的无限加载问题。

5. console 的妙用

console 并不只有 console.log(), 实际生产中都会使用已经封装好的log库,而 控制台对象 console 实际上内置了许多非常有用的方法,帮助您提高调试输出的质量和可读性,掌握它们能使您更轻松地 debug 和修复代码中的问题。

// 1. console.time 和 console.timeEnd
// 测量执行一段代码所需的时间。识别代码中的性能瓶颈并对其进行优化
console.time('开始获取数据');

fetch('https://reqres.in/api/users')
  .then(response => response.json())
  .then(data => {
    console.timeEnd('获取数据花费时间:');
    // ...code
  });
  
// 2. console.dir
// console.dir 方法以分层格式输出对象的属性。方便查看对象的结构以及其所有属性和方法
const promise = new Promise((resolve, reject) => resolve('foo'));
console.dir(promise);

// 3. console.count
// console.count 方法来计算特定日志消息的输出次数。这对于跟踪特定代码路径的执行次数以及识别代码中的热点非常有用
const fun = (x) => console.count(x);

fun('刻晴'); // 1
fun('甘雨'); // 1
fun('刻晴'); // 2

// 4. console.trace
// trace 可以输出堆栈跟踪。对于理解代码中的执行流程以及识别特定日志消息的来源非常有用
const foo = () => console.trace();
const bar = () => foo();
bar();

// 5. console.profile profileEnd
// 测量代码块的性能。这对于识别性能瓶颈以及优化代码以提高速度和效率非常有用。
console.profile('MyProfile');

// 想要测量性能的代码
for (let i = 0; i < 100000; i++) {
  // ...code
}

console.profileEnd('MyProfile');

6. 深拷贝 structuredClone()

此前,如果开发人员想要深拷贝对象,经常需要依赖第三方库来实现或者手动实现一个神拷贝,或者采取 const cloneObj = JSON.parse(JSON.stringify(obj)); 的 hack, 但其在处理包含循环引用不符合 JSON 的数据类型(如 Map 和 Set,Blob 等 ) 的更复杂对象时,是有很多不足之处的

而现在,JavaScript 内置了一个 structuredClone() 的方法, 此方法提供了一种简单有效的方法来深度克隆对象, 且适用于大多数现代浏览器和 Node.js v17 以上

// 将原始对象传递给该函数, 它将返回一个具有不同引用和对象属性引用的深层副本

const obj = { name: 'Mike', friends: [{ name: 'Sam' }] };
const clonedObj = structuredClone(obj);

console.log(obj.name === clonedObj); // false
console.log(obj.friends === clonedObj.friends); // false

与众所周知的 JSON.parse(JSON.stringify())” 不同, structuredClone() 允许您克隆循环引用,这是目前在 JavaScript 中使用深拷贝最简单的方法。

7. 带标签的模板

带标签的模板(Tagged_Templates) - 是模板字符串(反引号)的一种更高级的形式,它允许你使用函数解析模板字面量。

这个高级特性我也是在  Next.js 14 发布后人们都在讨论的一张图才去了解的🫡,尽管这个特性是 ES6 就有的,至今已有8年!!!但我敢打赌知道这个并使用过这个特性的人屈指可数。

相信许多人已经见过下图(因为这个知识点请停止嘲笑 😝Next.js 14), 相信许多人的第一反应就是回到二十年前 PHP 时代并且代码容易遭受 sql 注入攻击 , 但实际上是安全的。这得益于模板字符串的高级特性 - ( 带标签的模板 -Tagged_Templates)

sql.jpeg

如果你不理解 Tagged_Templates 如何工作, 那么就让我用一个例子来简单说明下吧:

const checkCurrency = function (currency, amount) {
  const symbol = currency[0] === "USD" ? "$" : "¥";
  console.log(currency[0], "--" ,currency[1])// Outputs: USD -- RMB
  return `${symbol}${amount}`;
};

const amount = 200;
const currency = checkCurrency`USD${amount}RMB`;
console.log(currency); // Outputs: $200

// 1. checkCurrency是一个函数,标签函数的第一个参数currency包含一个字符串值数组
// 2. 字符串数组由标签模板里的字符串组成,在`USD${amount}RMB`里,字符串有USD和RMB
// 3. 因此 currency[0] 为第一个字符串 USD, currency[1] 则是第二个字符串 RMB
// 3. checkCurrency函数的其余参数则根据表达式直接插入到字符串中,如 amount = 200
// 4. 在checkCurrency函数的内部,判断第一个参数数组首项是否是‘USD’,是则选择"$"符号,否则是 "¥"
// 5. 函数内部将symbol和amount结合在一起返回一个新的字符串,symbol代表货币符号,而amount代表传递给函数的金额。
// 6. 返回的字符串赋值给 currency 常量, 因此 log为 $200

可以看到,Tagged Templates 的工作方式是将模板字符串里的所有字符串作为一个数组传递给函数的第一个参数,其余参数则根据相应的表达式直接插入到字符串中,Tagged Templates将 文字字符串 和表达式的结果 传递给函数,然后该函数可以以自定义方式操作并返回它们。这样开发者在构建 SQL 查询时,对输入进行适当的转义和验证,从而避免 SQL 注入攻击

带标签的模板字符串可用于很多用途,例如 安全性i18n和本地化 等。

8. 空合并运算符 ??

空合并运算符 ?? (Nullish coalescing operator) 是一个逻辑运算符,当其左侧操作数为 null 或 undefined 时,它返回其右侧操作数,否则返回其左侧操作数

const foo = null ?? 'default string';
console.log(foo);  //output: "default string"

const bar = 0 ?? 'default string'
console.log(bar);  //output: 0

这有啥值得提的?||不就行了 ?因为很多人在初学JS可能会困扰的一个问题是 假 (false) 和 假值(falsy) 的区别, 而 ?? 和 || 主要区别在于

  • ?? 仅当左操作数为 null 或 undefined 时, ?? 运算符才会将结果作为右操作数。
  • ||运算符会将左操作数的所有假值(falsy) 的结果作为右操作数

举个例子:

// 1. 使用 0 作为输入 
const a = 0;
console.log(`a || 10 = ${a || 10}`); // a || 10 = 10
console.log(`a ?? 10 = ${a ?? 10}`); // a ?? 10 = 0

// 2. 空字符串 '' 作为输入
const a = ''
console.log(`a || "ABC" = ${a || "ABC"}`); // a || "ABC" = ABC
console.log(`a ?? "ABC" = ${a ?? "ABC"}`); // a ?? "ABC" = 

// 3. 使用 null 作为输入
const a = null;
console.log(`a || 10 = ${a || 10}`); // a || 10 = 10v
console.log(`a ?? 10 = ${a ?? 10}`); // a ?? 10 = 10

// 4. 使用 undefined 作为输入
const a = {name: ''}

console.log(`a.name ?? 'varun 1' = ${a.name ?? 'varun 1'}`); 
console.log(`a.name || 'varun 2' = ${a.name || 'varun 2'}`);
// a.name ?? 'varun 1' = 
// a.name || 'varun 2' = varun 2

// 5. 使用 false 作为输入
const a = false;
console.log(`a || 10 = ${a || 10}`); // a || 10 = 10
console.log(`a ?? 10 = ${a ?? 10}`); // a ?? 10 = false

JS的假值判断,可以参考这个表格 JavaScript-Equality-Table/:

null.png

9. 使用Symbols作为WeakMap的键

WeakMap 和 Map 很像,但不同点在于它的键(key) 只能是对象 Objects 和 symbol,这些键被作为弱引用存储(weakly)

为什么?因为 WeakMap 的键必须是可垃圾回收的。大多数原始数据类型可以任意创建并且没有生命周期,因此它们不能用作键, 而 对象Objects 和 non-registered symbols 可以用作键,因为它们是垃圾可收集的 -  MDN- WeakMap。

这个特性意味着除了键之外内存中没有其他对对象的引用,JavaScript 引擎可以在需要时对对象执行垃圾回收

// map
let user = { name: "User" };

let map = new Map();
map.set(user, "刻晴");

user = null; // 置null来覆盖引用,'user'被存在 map 的内部,通过 map.keys() 获取

// WeakMap
let user = { name: "User" };

let weakMap = new WeakMap();
weakMap.set(user, "甘雨");

user = null; // 使用 WeakMap,'user' 已经被从内存中删除 

好了,那 WeakMap 到底有什么作用呢?根据其特点可以联想到 WeakMap 的用途可以是自定义缓存以及检测内存泄漏

通过使用对象作为键,您可以将缓存的值与特定对象相关联。当对象被垃圾收集时,相应的 WeakMap 条目将被自动删除,立即清除缓存。

在 ES14 中, 使用 symbol 作为 WeakMap 的 key 已经成为可能, 这可以使键值对在 WeakMap 中扮演的角色更加清晰。因为唯一能在 WeakMap 中被作为 key 使用的原始类型只有 symbol, symbol 能保证 key 是 唯一的并且无法重新创建。

let mySymbol = Symbol('mySymbol');

let myWeakMap = new WeakMap();

let obj = {
    name: '写前端的刻猫猫'
};

myWeakMap.set(mySymbol, obj);

console.log(myWeakMap.get(mySymbol)); // Output: {name: '写前端的刻猫猫'}

10. 函数式编程

自 2015 年以来,JavaScript 版本都会进行更新,今年(2023 ES14)也不例外。

ES14 最大的更新便是数组更新了许多数组方法或者为原有的数组方法增加不会带来突变(without mutation) 的互补方法。意味着它们会基于原数组创建新的数组,而不是直接修改原数组。

新增的互补方法有

  • Array.sort() -> Array.toSorted()
  • Array.splice() -> Array.toSpliced()
  • Array.reverse() -> Array.toReversed()

新增的新数组方法有:

  • Array with()
  • Array.findLast()
  • Array.findLastIndex()

今年的主题是更简单的函数式编程(fp) 和 不变性(immutability)

// 以 Array.with() 为例, 之前如果想修改数组某个元素的值需要 arr[2] = 3;
// 这会带来突变,这不纯!愤怒💢 而有了无突变的数组方法后,可以这样写
const arr = [5, 4, 7, 2, 1];
const replaced = arr.with(2, 3);

console.log(replaced);  // [5, 4, 3, 2, 1]

 前端面试题库 (面试必备)            推荐:★★★★★

地址:前端面试题库

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1288757.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CentOS7 部署PostgreSQL

参考文档&#xff1a;https://www.postgresql.org/download/linux/redhat/ 1. 配置yum源 yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm2. 安装PostgreSQL13 yum install -y postgresql13-server3…

网络安全领域的12个大语言模型用例

网络安全是人工智能最大的细分市场&#xff0c;过去几年网络安全厂商纷纷宣称整合了人工智能技术&#xff08;当然也有很多仅仅是炒作&#xff09;&#xff0c;其中大部分是基于基线和统计异常的机器学习。 网络安全是人工智能最大的细分市场&#xff0c;过去几年网络安全厂商…

synchronized关键字-监视器锁(monitor lock)

这就是我们上一篇中代码提到的加锁的主要方式,本质上是调用系统api进行加锁,系统api本质是靠cpu特定指令加锁. synchronize的特性 互斥性 synchronized会起到互斥效果,某个线程执行到某个对象的synchronized中时,,其它线程如果也执行到同一个对象synchronized就会阻塞等待(锁…

快速安装Axure RP Extension for Chrome插件

打开原型文件的html&#xff0c;会跳转到这个页面&#xff0c;怎么破&#xff1f; 我们点开产品设计的原型图如果没有下载Axure插件是打不开&#xff0c;而我们国内网通常又不能再google商店搜索对应插件&#xff0c;下面教大家如何快速安装 1、打开原型文件->resources-&g…

云服务器哪家便宜?亚马逊云科技价格与性能全面解析

前言 近年来&#xff0c;随着企业数字化转型的推进&#xff0c;云服务器选择成为业界热议的话题。在众多云服务提供商中&#xff0c;亚马逊云科技备受关注&#xff0c;其云服务器在价格与性能方面的表现备受期待。为了帮助您更全面了解&#xff0c;本文将以第三方观点&#xf…

S120和PLC通讯设置

一、DP 总线通讯功能简述 PROFIBUS-DP 来实现 S7-300 与 SINAMICS S120 之间的数据交换&#xff0c;用 S7-300 来控制 S120 的运转&#xff0c;及读写所需的参数。 S7-300 与 SINAMICS S120 之间的 DP 通讯是借助于系统功能块 SFC14/SFC15 进行周期性数据通讯。 周期性数…

C# OpenCvSharp DNN 深度神经网络下的风格迁移模型

目录 介绍 效果 项目 代码 下载 C# OpenCvSharp DNN 深度神经网络下的风格迁移模型 介绍 深度神经网络下的风格迁移模型&#xff0c;适用于OpenCv、EmguCv。 斯坦福大学李飞飞团队的风格迁移模型是一种基于深度学习的图像处理技术&#xff0c;可以将一张图像的风格转移…

使用有道词典复制网页上的字

1. 今天发现一个新大陆&#xff0c;同事教的&#xff0c;有道词典可以复制网页上的字&#xff0c;也可以复制PDF文件等一些限制不可复制的字&#xff0c;原来不可复制的字&#xff0c;现在用有道都可以复制了&#xff0c;不需要用油猴下载脚本了。写给老婆这种纯电脑小白的。其…

paddleocr文字识别变迁

数据挖掘 v3 UIM&#xff1a;无标注数据挖掘方案 UIM&#xff08;Unlabeled Images Mining&#xff09;是一种非常简单的无标注数据挖掘方案。核心思想是利用高精度的文本识别大模型对无标注数据进行预测&#xff0c;获取伪标签&#xff0c;并且选择预测置信度高的样本作为训…

分布式I/O模块,为您的数据收集与控制提供强大支持

分布式I/O模块可用于数据收集和各种控制的应用。分布式I/O模块具有可靠度高、价格优惠、设置容易、网络布线方便等特性&#xff0c;适用于分散地区的应用&#xff0c;可以为您节省系统整合的时间和费用。以往都是采用控制电缆和PLC连接。如果采用了分布式I/O模块&#xff0c;就…

界面控件DevExpress WPF导航组件,助力升级应用程序用户体验!(上)

DevExpress WPF的Side Navigation&#xff08;侧边导航&#xff09;、TreeView、导航面板组件能帮助开发者在WPF项目中添加Windows样式的资源管理器栏或Outlook NavBar&#xff08;导航栏&#xff09;&#xff0c;DevExpress WPF NavBar和Accordion控件包含了许多开发人员友好的…

Win10无法删除文件需要管理员权限的解决方法

在Win10电脑中&#xff0c;用户想要删除不需要的文件&#xff0c;却收到了需要管理员权限才能删除&#xff0c;导致用户自己无法将文件删除掉。下面小编给大家带来Win10系统删除文件需要权限的解决方法&#xff0c;解决后用户在Win10电脑上就能删除任意文件了。 Win10无法删除文…

【数据结构】面试OJ题———栈|队列|互相实现|循环队列|括号匹配

目录 1. 有效的括号 思路&#xff1a; 2.用队列实现栈 思路&#xff1a; 3.用栈实现队列 思路&#xff1a; 4.设计循环队列 思路&#xff1a; 1. 有效的括号 20. 有效的括号 - 力扣&#xff08;LeetCode&#xff09; 给定一个只包括 (&#xff0c;)&#xff0c;{&…

Retrofit嵌套请求与适配器

一、前言&#xff1a; 1. retrofit嵌套请求 在实际开发中&#xff0c;可能会存在&#xff1a;需要先请求A接口&#xff0c;在请求B接口的情况&#xff0c;比如进入“玩android”网页请求获取收藏文章列表&#xff0c;但是需要先登录拿到Cookie才能请求搜藏文章几口&am…

语义分割 LR-ASPP网络学习笔记 (附代码)

论文地址&#xff1a;https://arxiv.org/abs/1905.02244 代码地址&#xff1a;https://github.com/WZMIAOMIAO/deep-learning-for-image-processing/tree/master/pytorch_segmentation/lraspp 1.是什么&#xff1f; LR-ASPP是一个轻量级语义分割网络&#xff0c;它是在Mobil…

【最新版本教程】GPT4暂停升级也可硬升!

本教程亲测整个过程&#xff0c;没有问题。 步骤&#xff1a; 1、有自己的3.5账号&#xff08;没有的可以自己去注册&#xff0c;据说现在不用手机号了&#xff0c;方法自己查去&#xff09;&#xff1b; 2、解决国外银行卡的问题。国外银行卡开通&#xff08;https://bewild…

Kafka集群调优

一、前言 我们需要对4个规格的kafka能力进行探底&#xff0c;即其可以承载的最大吞吐&#xff1b;4个规格对应的单节点的配置如下&#xff1a; 标准版&#xff1a; 2C4G铂金版&#xff1a; 4C8G专业版&#xff1a; 8C16G企业版&#xff1a; 16C32G 另外&#xff0c;一般来讲…

Excel——多列合并成一列的4种方法

Excel怎么将多列内容合并成一列&#xff1f; 怎么将多个单元格的内容连接起来放在一个单元格里&#xff1f; 比如下图&#xff0c;要将B、C、D列的内容&#xff0c;合并成E列那样&#xff0c;该怎么做呢&#xff1f; △图1 本文中&#xff0c;高潜老师将给大家介绍 4种 将多…

制作一个RISC-V的操作系统五-RISC-V汇编语言编程二

文章目录 RISC-V汇编指令操作对象RISC-V汇编指令编码格式小端序的概念RISC-V汇编指令分类RISC-V汇编伪指令 RISC-V汇编指令操作对象 RV32I&#xff1a;RISC-V32位机器整数指令集 指令集分非特权指令集和特权指令集 XLEN&#xff1a;变量代表当前机器的字长&#xff08;32位 64…

「Verilog学习笔记」根据状态转移写状态机-三段式

专栏前言 本专栏的内容主要是记录本人学习Verilog过程中的一些知识点&#xff0c;刷题网站用的是牛客网 状态机可以分为Moore状态机和Mealy状态机。 Moore状态机&#xff1a;输出只由当前状态决定Mealy状态机&#xff1a;输出由当前状态和当前的输入共同决定。 三段式状态机是指…