文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

前端中的那些 This vs That,你知道吗?

2024-11-29 21:41

关注

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

1. cookie vs localStorage vs sessionStorage

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

comparison-table

图片来源:local-storage-vs-session-storage-vs-cookies[1]

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

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

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

图片

2. querySelectorAll vs getElementsByTagName

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

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

来看下面这个示例:

  • 1
  • 2
  • 3

接下来使用两个方法获取 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.childNodes 和 element.children 返回的也是动态 NodeList。

3. children vs childNodes

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

  • A
  • B
  • C
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

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

宏任务包括:

微任务包括:

事件循环机制如下图:

图片

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

图片

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

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

一个更清晰的图(源[2]):

图片

5. setTimeout(0) vs requestAnimationFrame

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 设置。

图片

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 bar,baz 的事件监听函数不会触发。

8. HTML 字符实体 vs Unicode 字符

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

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

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

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

一些 Unicode 字符示例:

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

9. script async vs script defer

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

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


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

async 和 defer 的效果如上图。

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 对象定义了所有实例都将拥有的共享属性和方法。

图片

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

11.Dependencies vs devDependencies vs peerDependencies

dependencies 代表依赖项是项目中的一部分,最终会被一起打包到生产代码中,当你执行 npm install 时,你依赖的那个包的依赖也会自动安装,比如你项目使用到了 antd, npm install 时 antd 的依赖项 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.isNaN 比 isNaN 更准确。

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 值(undefined、null、NaN、0、""和 false),都会使用代码中默认参数,这个就是和 ES6 默认参数不同的地方。

14. null vs undefined

null 和 undefined 的不同点如下:

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

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

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

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

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

小结

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

参考资料

[1]local-storage-vs-session-storage-vs-cookies: https://www.loginradius.com/blog/engineering/guest-post/local-storage-vs-session-storage-vs-cookies/

[2]图片源: https://medium.com/@francesco_rizzi/javascript-main-thread-dissected-43c85fce7e23

来源:栗子前端内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯