likes
comments
collection
share

前端中的那些 This vs That前端知识中有很多相近的概念或 API,相信不少人在开发中有注意到这些相近的概念或

作者站长头像
站长
· 阅读数 64

前端知识中有很多相近的概念或 API,相信不少人在开发中有注意到这些相近的概念或 API,但是有时不会深入去了解异同,只要某个 API 能满足开发需求即可。

本文将介绍一些相近的概念和 API,让你能更清晰地了解它们的异同,在使用时更游刃有余。

1. cookie vs localStorage vs sessionStorage

前端开发中,这三个本地存储方案可以说是很常见的,用一张图说明下它们的区别:

前端中的那些 This vs That前端知识中有很多相近的概念或 API,相信不少人在开发中有注意到这些相近的概念或

图片来源:local-storage-vs-session-storage-vs-cookies

图中从存储大小、是否自动过期、服务端是否可以获取、是否支持 HTTP 请求传输和数据持久性方面进行对比。除了图中几个部分,在作用域方面,cookie 由域名和路径决定,localStorage 和 sessionStorge 都是遵守同源策略。

最后再提几个关于在使用 sessionStorage 的时偶尔会陌生的知识点:

  1. sessionStorage 数据在各个直接打开的浏览器页签中是不会同步的,这意味着你打开了两个同域名的网站,在其中一个设置了 sessionStorage 数据,另一个页面是不会同步这个数据的(而 localStorage 会),也就是说 sessionStorage 除了关闭浏览器时不会保留数据,各个页签数据的同步也和 localStorage 不一样。
  2. 如果你在当前页设置了一些 sessionStorage 数据,然后通过 window.open<a> 标签打开,新页签会同步一份当前页副本,随后两个页签的 sessionStorage 又会是独立的,不过要注意打开新页签的 rel 属性(用于指定当前文档与被链接文档的关系)要设置为 opener
前端中的那些 This vs That前端知识中有很多相近的概念或 API,相信不少人在开发中有注意到这些相近的概念或

2. querySelectorAll vs getElementsByTagName

querySelectorAll 可以根据传入的 CSS 选择器查找 HTML 元素,使用上比 getElementsByTagName 更灵活。

它们之间的不同点在于:querySelectorAll 返回的是一个静态的 NodeList,而 getElementsByTagName 返回的是动态的。

来看下面这个示例:

<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
</ul>

接下来使用两个方法获取 li 元素类数组,然后再动态插入一个 li,最后查看两个类数组的长度。

const listItems = document.querySelectorAll('li');
const listItems2 = document.getElementsByTagName('li');
console.log(listItems.length, listItems2.length);  // 3,3

const list = document.querySelector('ul');
const li = document.createElement('li');
li.innerHTML = '4';
list.appendChild(li);

console.log(listItems.length, listItems2.length);  // 3, 4

可以看到 querySelectorAll 方法获取的类数组长度在动态添加 li 后还是 3,而 getElementsByTagName 的为 4。

常用的获取元素方法中getElementsByClassName 方法、element.childNodeselement.children 返回的也是动态 NodeList。

3. children vs childNodes

childrenchildNodes 都可以用来获取元素的子节点,不同的是 children 只会获取 HTML 元素节点,而 childNodes 会获取到非 HTML 元素节点,包括文本、注释节点等。

<ul>
    <!-- 这里有有些内容 -->
    <li>A</li>
    <li>B</li>
    <li>C</li>
</ul>
const parent = document.querySelector('ul');
// 输出 HTMLCollection(3) [li, li, li]
console.log(parent.children)
// 输出 NodeList(10) [text, comment, text, text, li, text, li, text, li, text]
console.log(parernt.childNodes)

4. microtasks vs macrotasks

宏任务和微任务概念也经常在前端中出现,与之相关的就是事件循环机制。事件循环机制是必须掌握的,宏任务和微任务也可以了解下,实际开发中碰到相关问题能反应过来是宏任务和微任务的不同即可。

宏任务包括:

  • setTimeout and setInterval 的回调
  • DOM 操作
  • I/O 操作 (Node 中读写文件)
  • requestAnimationFrame

微任务包括:

  • Promises 的 resolve 和 reject
  • MutationObserver 回调
  • Node 中的 process.nextTick

事件循环机制如下图:

前端中的那些 This vs That前端知识中有很多相近的概念或 API,相信不少人在开发中有注意到这些相近的概念或

宏任务微任务执行顺序如下图:

前端中的那些 This vs That前端知识中有很多相近的概念或 API,相信不少人在开发中有注意到这些相近的概念或

最后配合一个例子看下效果:

console.log('Script start')

setTimeout(function () {
  console.log('setTimeout')
}, 0)

new Promise((resolve) => {
  console.log('Promise')
}).then(function () {
  console.log('Promise then')
})

console.log('Script end')

// 输出顺序为: Script start、Promise、Script end、Promise then、setTimeout

一个更清晰的图():

前端中的那些 This vs That前端知识中有很多相近的概念或 API,相信不少人在开发中有注意到这些相近的概念或

5. setTimeout(0) vs requestAnimationFrame

setTimeout(0)requestAnimationFrame 都能把代码延迟到下一个动画帧运行,它们的不同在于:

  • setTimeout(0) 将代码推到事件循环的任务队列中,如果任务队列中有大量任务,setTimeout(0) 就不会立即执行。
  • requestAnimationFrame 会在下一次渲染前执行,而不是在事件循环中执行,它能自动与显示器刷新率同步。不过,它只有在浏览器准备好渲染新帧时才会执行,如果标签页处于非激活状态,它就不会运行。

处理动画时,requestAnimationFrame 更合适, 如果你要延迟执行代码的话,可以直接使用 setTimeout(0)

补充一个小点:setTimeout 的语法是 setTimeout(functionRef, delay, param1, param2, /* … ,*/ paramN),除了回调函数和延迟时间,后续参数都会作为回调函数的参数。

// 1 秒后输出 delay 1s
setTimeout(console.log, 1000, 'delay 1s')

6. naturalWidth vs width

naturalWidth 是元素的自然宽度,它永远不会改变。例如,一张 100px 宽的图片的 naturalWidth 始终是 100px,即使通过 CSS 或 JavaScript 调整图片大小后也不变。

width 是可以改变的,可以通过 CSS 或 JavaScript 设置。

前端中的那些 This vs That前端知识中有很多相近的概念或 API,相信不少人在开发中有注意到这些相近的概念或

7. stopImmediatePropagation vs stopPropagation

stopImmediatePropagation() 方法与 stopPropagation() 方法一样,可阻止事件冒泡。但是,stopImmediatePropagation() 方法会阻止元素同一事件的其他监听器。

button.addEventListener('click', function () {
  console.log('foo')
})

button.addEventListener('click', function (e) {
  console.log('bar')
  e.stopImmediatePropagation()
})

button.addEventListener('click', function () {
  console.log('baz')
})

上面代码中按钮点击后只会输出 foo and barbaz 的事件监听函数不会触发。

8. HTML 字符实体 vs Unicode 字符

HTML 实体是特殊字符序列,用来表示可能被误认为是 HTML 代码的字符,如小于号 (<) 或双引号 (&)。

下面是一些常见的 HTML 实体:

  • &lt; 代表小于号 <
  • &gt; 代表大于号 >
  • &amp; 代表于符号 &
  • &quot; 代表双引号 "
  • &apos;&#39; 代表单引号 '
  • &nbsp; 代表空格

HTML 字符实体相比 Unicode 字符会更好记些,同时浏览器对 HTML 字符实体支持更好。

Unicode 是表示字符或符号的特定代码,它们用于显示标准字符集中可能没有的字符,如非拉丁字母或特殊符号。

一些 Unicode 字符示例:

  • \u00A9 表示版权符号 (©)
  • \u2192 表示右箭头 (→)
  • \u2615 代表咖啡杯 (☕)
  • \u1F60E 代表戴着墨镜的笑脸 (😎)
  • \u2764 表示一颗红心 (❤)

Unicode 可以表示任何语言的任何字符或符号,不过旧版本浏览器的支持性可能没那么好。

9. script async vs script defer

当浏览器碰到 script 标签时,会执行以下步骤:

  • 暂停文档解析
  • 创建一个新请求来下载脚本
  • 下载完成后执行脚本
  • 继续解析文档

script 标签会阻塞整个文档的解析,为了提供更好的体验,HTML5 为 script 标签提供了两个属性,它们是 asyncdefer

<script src="/path/to/script.js" async></script>
<script src="/path/to/script.js" defer></script>

这两个属性让浏览器知道,该脚本与文档解析可以同时进行。

前端中的那些 This vs That前端知识中有很多相近的概念或 API,相信不少人在开发中有注意到这些相近的概念或

asyncdefer 的效果如上图。

async 会在下载完成后立即执行(下载不阻塞 HTML 解析,执行会),所以多个 script 标签都使用 async 属性的话,是不能保证多个 script 的执行顺序,而使用 defer 的话,下载完后会等待 HTML 解析完成再执行,可以保证多个 script 的执行顺序。

所以 async 一般在独立的脚本上使用,如埋点脚本。

还有一点,动态加载的脚本 async 默认为 true,如果你不需要,可以设置为 false

const script = document.createElement('script');
script.src = '/path/to/script.js';
script.async = false;
document.head.appendChild(script);

10. __proto__ vs prototype

__proto__prototype 的区别很简单:

  • __proto__ 是对象实例的属性

  • prototype 是构造函数的属性

当你使用 __proto__ 时,你是正在查找对象原型链上的属性和方法,而 prototype 对象定义了所有实例都将拥有的共享属性和方法。

前端中的那些 This vs That前端知识中有很多相近的概念或 API,相信不少人在开发中有注意到这些相近的概念或

如上图,Letter 函数的 prototype 属性和其三个实例的 __proto__ 属性都是指向 Letter 的原型链对象 Letter.prototype

11. Dependencies vs devDependencies vs peerDependencies

dependencies 代表依赖项是项目中的一部分,最终会被一起打包到生产代码中,当你执行 npm install 时,你依赖的那个包的依赖也会自动安装,比如你项目使用到了 antd, npm installantd 的依赖项 classnames 也会被安装,这就是你有时候没安装一些库,但是也可以使用的原因。

"dependencies": {
    "lodash": "^4.17.21"
}

devDenpendencies 代表依赖项是仅在开发过程中才需要的,代码的最终生产版本并不需要这些依赖项。

"devDependencies": {
    "jest": "^29.6.4"
}

peerDependencies 代表使用这个库时需要的依赖项,和 dependencies 不同的是,它不会在 npm install 时被安装,需要你显式的在自己项目下安装。各个包管理器的各个版本对 peerDependencies 的处理可能都不同,有兴趣的可以继续深入了解。

 "peerDependencies": {
    "react": ">=16.9.0",
    "react-dom": ">=16.9.0"
  },

12. isNaN vs Number.isNaN

isNaN 是一个全局函数,用于判断参数是否为 NaN,不过,在判断参数是否为 NaN 之前,它会尝试先将参数转换为数字。

isNaN('hello');     // true
isNaN(undefined);   // true
isNaN({});          // true
isNaN([]);          // false  +[] === 0
isNaN(42);          // false

在 ES6 中引入了 Number.isNaN 函数,与 isNaN 不同的是,在判断前 Number.isNaN 不会转换参数。

Number.isNaN('hello');      // false
Number.isNaN(undefined);    // false
Number.isNaN({});           // false
Number.isNaN([]);           // false
Number.isNaN(42);           // false
Number.isNaN(NaN);          // true

一般来说,使用 Number.isNaNisNaN 更准确。

13. 默认参数 vs 或操作符

JavaScript 提供了两种为函数参数设置默认值的方法:使用默认参数或 OR (||) 操作符,两者在最终效果上会有一些不同。

先来看默认参数:

const sayHello = (name = 'World') => {
  console.log(`Hello, ${name}!`);
};

sayHello();                 // `Hello, World!`
sayHello(undefined);        // `Hello, World!`
sayHello(null);				// `Hello, null!`
sayHello('');				// `Hello, !`
sayHello("Phuoc Nguyen");   // `Hello, Phuoc Nguyen!`

可以看到默认参数只有为 undefined 的时候,默认参数才会生效。不传和传 undefined 效果一致。

再来看或操作符:

const sayHello2 = (name) => {
    const withDefaultName = name || 'World';
    console.log(`Hello, ${withDefaultName}!`);
};

sayHello2();                 // `Hello, World!`
sayHello2(undefined);        // `Hello, World!`
sayHello2(null);			 // `Hello, World!`
sayHello2('');				 // `Hello, World!`
sayHello2("Phuoc Nguyen");   // `Hello, Phuoc Nguyen!`

可以看到参数只要是 falsy 值(undefinednullNaN0""false),都会使用代码中默认参数,这个就是和 ES6 默认参数不同的地方。

14. null vs undefined

nullundefined 的不同点如下:

  1. undefined 表示变量已经被声明,但未被赋值;null 用来表示变量没有值。
let foo;
console.log(foo); // undefined

let foo = null;
console.log(foo); // null
  1. undefinednull 代表的类型不同。
console.log(typeof undefined); // 'undefined'
console.log(typeof null); // 'object'

除了以上两点不同之外,还有两点值得关注的:

  1. undefinednull 进行比较的结果。
null == undefined; // true
null === undefined; // false
  1. JSON.stringify 会忽略 undefined, 但是会保留 null
JSON.stringify({
    name: 'John',
    address: null,
    age: undefined,
});

// {"name":"John","address":null}

小结

前端中还有很多相近的概念和 API,在业务开发时可能没时间去了解,但是有空的时候还是可以花点时间去掌握其中的异同,扎实自己的前端基础。

转载自:https://juejin.cn/post/7374308419073785865
评论
请登录