前言🐟
在面试中,面试官可能会通过一道看似简单的题目——如何将字符串转换为数组——来考察你的编程基础和对语言特性的理解。然而,为了进一步测试你的知识广度,他们可能会顺势引出另一个相关的话题——类数组对象。这不仅考察你对字符串操作的理解,还考验你对 JavaScript 中更复杂的数据结构的掌握。
面试中的程序逻辑题
在面试场景中,所涵盖的题目类型丰富多样,不仅仅局限于算法题,还会涉及到程序逻辑题。程序逻辑题着重于对某个特定需求的实现与达成。今日,我们就聚焦于这样一道简单却又典型的需求题:将字符串转换为数组。当面对这一问题时,大家脑海中率先浮现的思路会是什么呢?又能够构思出几种不同的解决方案呢?
想必很多人都会先想到,字符串内置的一个API,他就是 split 方法
split 方法
split 是最常用的方法之一,它可以将字符串按照指定的分隔符分割成数组。如果不传入分隔符,split('') 会将字符串按字符分割。
const str = "hello";
const charArray = str.split(''); // ['h', 'e', 'l', 'l', 'o']
这里需要注意对于某些特殊字符(如 Unicode 字符),可能需要额外处理。为了正确处理 Unicode 字符,推荐使用 Array.from。
const str = "😊"; // Unicode 字符
const charArray = str.split(''); // ['\ud83d', '\ude0a'] (不正确)
const charArrayCorrect = Array.from(str); // ['😊'] (正确)
但是他还是不够优雅,简洁,这个时候就轮到ES6引入的强大工具 扩展运算符 登场了
扩展运算符
扩展运算符(spread)是三个点(...)。它能将具有 iterable 属性(即可迭代对象)的对象进行展开,这里我们展示的是 字符串的展开
const str = "hello";
const charArray = [...str]; // ['h', 'e', 'l', 'l', 'o']
Array.from() 方法
Array.from() 方法在 JavaScript 的数组处理工具库中占据着重要的一席之地。它的设计初衷便是为了将那些类数组对象或者可迭代对象(字符串恰好属于可迭代对象的范畴)高效地转换为真正意义上的数组
const str = "hello";
const charArray = Array.from(str);
console.log(charArray);
// 输出 ['h', 'e', 'l', 'l', 'o'],成功将字符串转换为数组
解构赋值
解构赋值是一种更高级的语法,可以直接从字符串中提取字符,并将其赋值给多个变量。虽然这种方法不能直接生成数组,但可以通过结合数组字面量来实现。
const str = "hello";
const [char1, char2, char3, char4, char5] = str;
console.log([char1, char2, char3, char4, char5]); // ['h', 'e', 'l', 'l', 'o']
类数组对象:NodeList 和 HTMLCollection 的 forEach 之谜
NodeList 和 HTMLCollection 是 DOM API 中的类数组对象,它们具有类似数组的特性(如 length 属性和按索引访问元素),但并不是真正的数组。尽管它们的原型链中没有 Array.prototype,但在现代浏览器中,它们却可以使用 forEach 方法。这背后的原因是什么呢?
类数组它是什么?
1. 类数组对象的定义
类数组对象是指那些具有某些数组特征的对象,但它们并不是真正的数组。它们通常具有以下特点:
length属性:表示对象中元素的数量。- 按索引访问:可以通过数字索引访问元素,例如
nodeList[0]。 - 缺少数组方法:类数组对象不继承自
Array.prototype,因此它们没有数组的内置方法(如push、pop、map等)。
NodeList 和 HTMLCollection 是最常见的类数组对象:
NodeList:由document.querySelectorAll()返回,表示一组 DOM 元素。HTMLCollection:由document.getElementsByClassName()或document.getElementsByTagName()返回,也表示一组 DOM 元素。
2. 为什么类数组对象不能直接使用数组方法?
类数组对象的原型链中没有 Array.prototype,因此它们无法直接访问数组的内置方法。例如,NodeList.prototype 和 HTMLCollection.prototype 都不是 Array.prototype 的子类,因此它们没有继承 join、map、filter 等数组方法。
const nodeList = document.querySelectorAll('div');
console.log(nodeList.__proto__); // NodeListPrototype { ... }
console.log(NodeList.prototype.__proto__); // Object.prototype { ... }
const htmlCollection = document.getElementsByTagName('div');
console.log(htmlCollection.__proto__); // HTMLCollectionPrototype { ... }
console.log(HTMLCollection.prototype.__proto__); // Object.prototype { ... }
3. 现代浏览器中的扩展
在现代浏览器中,NodeList 和 HTMLCollection 已经被扩展,支持了一些数组方法(如 forEach)。这是因为在较新的浏览器实现中,NodeList 和 HTMLCollection 被设计为“活的”集合,并且它们的行为更接近于真正的数组。因此,你可以直接在这些对象上调用 forEach,而不需要显式地转换为数组。
const nodeList = document.querySelectorAll('div');
nodeList.forEach((element) => {
console.log(element);
});
然而,这种行为并不是所有浏览器都支持的,尤其是旧版本的浏览器(如 IE)。因此,在编写跨浏览器兼容的代码时,最好还是使用 Array.prototype.forEach.call() 或将类数组对象转换为数组后再使用 forEach。
4. 如何在类数组对象上使用 forEach?
即使在不支持 forEach 的浏览器中,你也可以通过以下几种方式在类数组对象上使用 forEach:
-
借用
Array.prototype.forEach.call():const nodeList = document.querySelectorAll('div'); Array.prototype.forEach.call(nodeList, (element) => { console.log(element); }); -
使用
Array.from()或扩展运算符...:// 使用 Array.from() const nodeList = document.querySelectorAll('div'); Array.from(nodeList).forEach((element) => { console.log(element); }); // 使用扩展运算符 ... const nodeList = document.querySelectorAll('div'); [...nodeList].forEach((element) => { console.log(element); });
END
- 展开运算符 是 ES6 引入的强大工具,使得代码更加优雅和简洁,适用于多种场景。
- 将字符串转字符数组 有多种方法,包括
split、解构赋值、扩展运算符和Array.from,其中扩展运算符是最简洁的方式。 - 类数组对象 如
NodeList和HTMLCollection虽然不是真正的数组,但在现代浏览器中已经被扩展,支持了forEach方法。为了确保跨浏览器兼容性,建议使用Array.prototype.forEach.call()或将类数组对象转换为数组后再使用forEach。
希望这篇文章能帮助你更好地理解展开运算符、类数组对象以及 NodeList 和 HTMLCollection 的 forEach 之谜。如果你有更多关于 JavaScript 或其他编程问题,欢迎在评论区留言讨论!