学习笔记 2020-11-06
JavaScript 高级程序设计(第4版) 阅读记录
客户端检测
软件与硬件检测
浏览器元数据
Battery Status API
浏览器可以访问设备电池及充电状态的信息。
navigator.getBattery()
方法会返回一个期约实例,解决为一个BatteryManager
对象,包含四个只读属性:charging
布尔值,表示设备当前是否正接入电源充电,如果设备没有电池,则返回true
。chargingTime
整数,表示预计离电池充满还有多少秒。如果电池已充满或设备没有电池,则返回 0 。dischargingTime
整数,表示预计离电量耗尽还有多少秒,如果设备没有电池,则返回 Infinity 。level
浮点数,表示电量百分比。电量完全耗尽返回0.0
,电池充满或设备没有电池则返回1.0
。
这个 API 还提供了 4 个事件属性,可用于设置在相应的电池事件发生时调用的回调函数。
- onchargingchange
- onchargingtimechange
- ondischargingtimechange
- onlevelchange
navigator.getBattery().then(battery => { // 添加充电状态变化时的处理程序 const chargingChangeHandler = () => console.log('chargingchange'); battery.onchargingchange = chargingChangeHandler; // 或 battery.addEventListener('chargingchange', chargingChangeHandler); // 添加充电时间变化时的处理程序 const chargingTimeChangeHandler = () => console.log('chargingtimechange'); battery.onchargingtimechange = chargingTimeChangeHandler; // 或 battery.addEventListener('chargingtimechange', chargingTimeChangeHandler); // 添加放电时间变化时的处理程序 const dischargingTimeChangeHandler = () => console.log('dischargingtimechange'); battery.ondischargingtimechange = dischargingTimeChangeHandler; // 或 battery.addEventListener('dischargingtimechange', dischargingTimeChangeHandler); // 添加电量百分比变化时的处理程序 const levelChangeHandler = () => console.log('levelchange'); battery.onlevelchange = levelChangeHandler; // 或 battery.addEventListener('levelchange', levelChangeHandler); });
硬件
处理器核心数
navigator.hardwareConcurrency
属性返回浏览器支持的逻辑处理器核心数量,包含表示核心数的一个整数值。这个值表示浏览器可以并行执行的最大工作线程数量,不一定是实际的 CPU 核心数。设备内存大小、
navigator.deviceMemory
属性返回设备大致的系统内存大小,包含单位为 GB 的浮点数。最大触点数
navigator.maxTouchPoints
属性返回触摸屏支持的最大关联触点数量,包含一个整数值。
DOM
HTMl 中的每段标记都可以表示为一个节点。DOM 中总共有 12 种节点类型,这些类型都继承一种基本类型。
节点层级
Node 类型
DOM Level 1 描述了名为 Node 的接口,这个接口是所有 DOM 节点类型都必须实现的。 Node 接口在 JavaScript 中被实现为 Node 类型,在除 IE 之外的所有浏览器中都可以直接访问这个类型。在 JavaScript 中,所有节点类型都继承 Node 类型,因此所有类型都共享相同的基本属性和方法。
每个节点都有 nodeType 属性,表示该节点的类型。节点类型由定义在 Node 类型上的 12 个数值常量表示:
- Node.ELEMENT_NODE ( 1 )
- Node.ATTRIBUTE_NODE ( 2 )
- Node.TEXT_NODE ( 3 )
- Node.CDATA_SECTION_NODE ( 4 )
- Node.ENTITY_PEFERENCE_NODE ( 5 )
- Node.ENTITY_NODE ( 6 )
- Node.PROCESSING_INSTRUCTION_NODE ( 7 )
- Node.COMMENT_NODE ( 8 )
- Node.DOCUMENT_NODE ( 9 )
- Node.DOCUMENT_TYPE_NODE ( 10 )
- Node.DOCUMENT_FRAGMENT_NODE ( 11 )
- Node.NOTATION_NODE ( 12 )
节点类型可以通过与这些常量比较来确定:
if (someNode.nodeType == Node.ELEMENT_NODE) {
console.log('Node is an element.');
}
浏览器并不支持所有节点类型。
nodeName 与 nodeValue
nodeName 与 nodeValue 保存着有关节点的信息。这两个属性的值完全取决于节点类型。在使用这两个属性前,最好先检测节点类型。
if (someNode.nodeType == 1) { value = someNode.nodeName; // 会显示元素的标签名 }
对元素而言,nodeName 始终等于元素的标签名,而 nodeValue 则始终为 null 。
节点关系
每个节点有一个 childNodes 属性,其中包含一个 NodeList 的实例。NodeList 是一个类数组对象,用于存储可以按位置存取的有序节点。NodeList 对象独特的地方在于,它其实是一个对 DOM 结构的查询,因此 DOM 结构的变化会自动地在 NodeList 中反映出来。NodeList 是实时的活动对象,而不是第一次访问时所获的内容的快照。
let firstChild = someNode.childNodes[0]; let secondChild = someNode.childNodes.item(1); let count = someNode.childNodes.length;
每个节点有一个 parentNode 属性,指向其 DOM 树中的父元素。
childNodes 列表中的每个节点都是同一列表中其他节点的同胞节点,使用 previousSibling 和 nextSibling 可以在这个列表的节点间导航。列表中第一个节点和最后一个节点的同胞属性为 null 。
父节点和它的第一个及最后一个子节点也有专门属性:
- firstChild
- lastChild
还有一个方法是
hasChildNodes()
,可以判断节点是否有一个或多个子节点。所有节点共享一个关系: ownerDocument 属性是一个指向代表整个文档的文档节点的指针。所有节点都被创建它们的文档所拥有。
现代 JavaScript 教程
任务
过滤数组中的唯一元素
定义
arr
为一个数组。创建一个函数
unique(arr)
,该函数返回一个由arr
中所有唯一元素所组成的数组。例如:
function unique(arr) { /* 你的代码 */ } let values = ["Hare", "Krishna", "Hare", "Krishna", "Krishna", "Krishna", "Hare", "Hare", ":-O" ]; alert( unique(values) ); // Hare, Krishna, :-O
P.S. 这里用到了 string 类型,但其实可以是任何类型的值。
P.S. 使用
Set
来存储唯一值。function unique(arr) { return Array.from(new Set(arr)); }
过滤字谜
Anagrams 是具有相同数量相同字母但是顺序不同的单词。
例如:
nap - pan ear - are - era cheaters - hectares - teachers
写一个函数
aclean(arr)
,它返回被清除了字谜(anagrams)的数组。例如:
let arr = ["nap", "teachers", "cheaters", "PAN", "ear", "era", "hectares"]; alert( aclean(arr) ); // "nap,teachers,ear" or "PAN,cheaters,era"
对于所有的字谜(anagram)组,都应该保留其中一个词,但保留的具体是哪一个并不重要。
function aclean(arr) { let set = new Set(); let newArr = []; arr.forEach(item => { let tmp = item.toLowerCase().split('').sort().join(''); if (!set.has(tmp)) { newArr.push(item); set.add(tmp); } }); return newArr; }
// 参考解决方案 function aclean(arr) { let map = new Map(); for (let word of arr) { // 将单词 split 成字母,对字母进行排序,之后再 join 回来 let sorted = word.toLowerCase().split('').sort().join(''); // (*) map.set(sorted, word); } return Array.from(map.values()); }
迭代键
我们期望使用
map.keys()
得到一个数组,然后使用特定的方法例如.push
等,对其进行处理。但是运行不了:
let map = new Map(); map.set("name", "John"); let keys = map.keys(); // Error: keys.push is not a function keys.push("more");
为什么?我们应该如何修改代码让
keys.push
工作?map.keys()
返回值是一个可迭代对象,不是数组。let map = new Map(); map.set('name', 'John'); let keys = Array.from(map.keys()); keys.push('more'); console.log(keys); // ["name", "more"]