一、map和weakMap的区别
(1)Map
map本质上就是键值对的集合,但是普通的Object中的键值对中的键只能是字符串。而ES6提供的Map数据结构类似于对象,但是它的键不限制范围,可以是任意类型,是一种更加完善的Hash结构。如果Map的键是一个原始数据类型,只要两个键严格相同,就视为是同一个键。
实际上Map是一个数组,它的每一个数据也都是一个数组,其形式如下:
const map = [["name","张三"],["age",18],
]
Map数据结构有以下操作方法:
- size: map.size 返回Map结构的成员总数。
- set(key,value):设置键名key对应的键值value,然后返回整个Map结构,如果key已经有值,则键值会被更新,否则就新生成该键。(因为返回的是当前Map对象,所以可以链式调用)
- get(key):该方法读取key对应的键值,如果找不到key,返回undefined。
- has(key):该方法返回一个布尔值,表示某个键是否在当前Map对象中。
- delete(key):该方法删除某个键,返回true,如果删除失败,返回false。
- clear():map.clear()清除所有成员,没有返回值。
Map结构原生提供是三个遍历器生成函数和一个遍历方法:
- keys():返回键名的遍历器。
- values():返回键值的遍历器。
- entries():返回所有成员的遍历器。
- forEach():遍历Map的所有成员。
const map = new Map([["foo",1],["bar",2],
])
for(let key of map.keys()){console.log(key); // foo bar
}
for(let value of map.values()){console.log(value); // 1 2
}
for(let items of map.entries()){console.log(items); // ["foo",1] ["bar",2]
}
map.forEach( (value,key,map) => {console.log(key,value); // foo 1 bar 2
})
(2)WeakMap
WeakMap 对象也是一组键值对的集合,其中的键是弱引用的。其键必须是对象,原始数据类型不能作为key值,而值可以是任意的。
该对象也有以下几种方法:
- set(key,value):设置键名key对应的键值value,然后返回整个Map结构,如果key已经有值,则键值会被更新,否则就新生成该键。(因为返回的是当前Map对象,所以可以链式调用)
- get(key):该方法读取key对应的键值,如果找不到key,返回undefined。
- has(key):该方法返回一个布尔值,表示某个键是否在当前Map对象中。
- delete(key):该方法删除某个键,返回true,如果删除失败,返回false。
其clear()方法已经被弃用,所以可以通过创建一个空的WeakMap并替换原对象来实现清除。
WeakMap的设计目的在于,有时想在某个对象上面存放一些数据,但是这会形成对于这个对象的引用。一旦不再需要这两个对象,就必须手动删除这个引用,否则垃圾回收机制就不会释放对象占用的内存。
而WeakMap的键名所引用的对象都是弱引用,即垃圾回收机制不将该引用考虑在内。因此,只要所引用的对象的其他引用都被清除,垃圾回收机制就会释放该对象所占用的内存。也就是说,一旦不再需要,WeakMap 里面的键名对象和所对应的键值对会自动消失,不用手动删除引用。
总结:
- Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。
- WeakMap 结构与 Map 结构类似,也是用于生成键值对的集合。但是 WeakMap 只接受对象作为键名( null 除外),不接受其他类型的值作为键名。而且 WeakMap 的键名所指向的对象,不计入垃圾回收机制。
二、常用的正则表达式有哪些?
(1)匹配 16 进制颜色值
var regex = /#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})/g;
(2)匹配日期,如 yyyy-mm-dd 格式
var regex = /^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/;
(3)匹配 qq 号
var regex = /^[1-9][0-9]{4,10}$/g;
(4)手机号码正则
var regex = /^1[34578]\d{9}$/g;
(5)用户名正则
var regex = /^[a-zA-Z\$][a-zA-Z0-9_\$]{4,16}$/;
三、对JSON的理解
JSON 是一种基于文本的轻量级的数据交换格式。它可以被任何的编程语言读取和作为数据格式来传递。
在项目开发中,使用 JSON 作为前后端数据交换的方式。在前端通过将一个符合 JSON 格式的数据结构序列化为 JSON 字符串,然后将它传递到后端,后端通过 JSON 格式的字符串解析后生成对应的数据结构,以此来实现前后端数据的一个传递。
因为 JSON 的语法是基于 js 的,因此很容易将 JSON 和 js 中的对象弄混,但是应该注意的是 JSON 和 js 中的对象不是一回事,JSON 中对象格式更加严格,比如说在 JSON 中属性值不能为函数,不能出现 NaN 这样的属性值等,因此大多数的 js 对象是不符合 JSON 对象的格式的。
在 js 中提供了两个函数来实现 js 数据结构和 JSON 格式的转换处理
- JSON.stringify 函数,通过传入一个符合 JSON 格式的数据结构,将其转换为一个 JSON 字符串。如果传入的数据结构不符合 JSON 格式,那么在序列化的时候会对这些值进行对应的特殊处理,使其符合规范。在前端向后端发送数据时,可以调用这个函数将数据对象转化为 JSON 格式的字符串。
- JSON.parse() 函数,这个函数用来将 JSON 格式的字符串转换为一个 js 数据结构,如果传入的字符串不是标准的 JSON 格式的字符串的话,将会抛出错误。当从后端接收到 JSON 格式的字符串时,可以通过这个方法来将其解析为一个 js 数据结构,以此来进行数据的访问。
四、Unicode、UTF-8、UTF-16、UTF-32的区别?
(1)Unicode
在说Unicode之前需要先了解一下ASCII码:ASCII 码(American Standard Code for Information Interchange)称为美国标准信息交换码。
- 它是基于拉丁字母的一套电脑编码系统。
- 它定义了一个用于代表常见字符的字典。
- 它包含了"A-Z"(包含大小写),数据"0-9" 以及一些常见的符号。
- 它是专门为英语而设计的,有128个编码,对其他语言无能为力
ASCII码可以表示的编码有限,要想表示其他语言的编码,还是要使用Unicode来表示,可以说Unicode是ASCII的超集。
Unicode 全称 Unicode Translation Format,又叫做统一码、万国码、单一码。Unicode 是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。
Unicode 的实现方式(也就是编码方式)有很多种,常见的是UTF-8、UTF-16、UTF-32和USC-2。
(2)UTF-8
UTF-8是使用最广泛的Unicode编码方式,它是一种可变长的编码方式,可以是1—4个字节不等,它可以完全兼容ASCII码的128个字符。
注意:UTF-8 是一种编码方式,Unicode是一个字符集合。
UTF-8 的编码规则:
- 对于单字节的符号,字节的第一位为0,后面的7位为这个字符的Unicode编码,因此对于英文字母,它的Unicode编码和ACSII编码一样。
- 对于n字节的符号,第一个字节的前n位都是1,第n+1位设为0,后面字节的前两位一律设为10,剩下的没有提及的二进制位,全部为这个符号的Unicode码 。
来看一下具体的Unicode编号范围与对应的UTF-8二进制格式 :
编码范围(编号对应的十进制数) | 二进制格式 |
---|---|
0x00—0x7F (0-127) | 0xxxxxxx |
0x80—0x7FF (128-2047) | 110xxxxx 10xxxxxx |
0x800—0xFFFF (2048-65535) | 1110xxxx 10xxxxxx 10xxxxxx |
0x10000—0x10FFFF (65536以上) | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
那该如何通过具体的Unicode编码,进行具体的UTF-8编码呢?步骤如下:
- 找到该Unicode编码的所在的编号范围,进而找到与之对应的二进制格式
- 将Unicode编码转换为二进制数(去掉最高位的0)
- 将二进制数从右往左一次填入二进制格式的X中,如果有X未填,就设为0
来看一个实际的例子:
马字的Unicode编码是:0x9A6C,整数编号是39532
(1)首选确定了该字符在第三个范围内,它的格式是 1110xxxx 10xxxxxx 10xxxxxx
(2)39532对应的二进制数为1001 1010 0110 1100
(3)将二进制数填入X中,结果是:11101001 10101001 10101100
(3)UTF-16
1. 平面的概念
在了解UTF-16之前,先看一下平面的概念:
Unicode编码中有很多很多的字符,它并不是一次性定义的,而是分区进行定义的,每个区存放65536(216)个字符,这称为一个平面,目前总共有17 个平面。
最前面的一个平面称为基本平面,它的码点从0 — 2 16 — 1,写成16进制就是U+0000 — U+FFFF,那剩下的16个平面就是辅助平面,码点范围是 U+10000 — U+10FFFF。
2. UTF-16 概念
UTF-16也是Unicode编码集的一种编码形式,把Unicode字符集的抽象码位映射为16位长的整数(即码元)的序列,用于数据存储或传递。Unicode字符的码位需要1个或者2个16位长的码元来表示,因此UTF-16也是用变长字节表示的。
3. UTF-16 编码规则
- 编号在 U+0000—U+FFFF 的字符(常用字符集),直接用两个字节表示。
- 编号在 U+10000—U+10FFFF 之间的字符,需要用四个字节表示。
4. 编码识别
那么问题来了,当遇到两个字节时,怎么知道是把它当做一个字符还是和后面的两个字节一起当做一个字符呢?
UTF-16 编码肯定也考虑到了这个问题,在基本平面内,从 U+D800 — U+DFFF 是一个空段,也就是说这个区间的码点不对应任何的字符,因此这些空段就可以用来映射辅助平面的字符。
辅助平面共有 220个字符位,因此表示这些字符至少需要 20 个二进制位。UTF-16将这 20 个二进制位分成两半,前 10 位映射在 U+D800 — U+DBFF,称为高位(H),后 10 位映射在 U+DC00 — U+DFFF,称为低位(L)。这就相当于,将一个辅助平面的字符拆成了两个基本平面的字符来表示。
因此,当遇到两个字节时,发现它的码点在 U+D800 —U+DBFF之间,就可以知道,它后面的两个字节的码点应该在 U+DC00 — U+DFFF 之间,这四个字节必须放在一起进行解读。
5. 举例说明
以 "𡠀" 字为例,它的 Unicode 码点为 0x21800,该码点超出了基本平面的范围,因此需要用四个字节来表示,步骤如下:
- 首先计算超出部分的结果:0x21800 - 0x10000
- 将上面的计算结果转为20位的二进制数,不足20位就在前面补0,结果为:0001000110 0000000000
- 将得到的两个10位二进制数分别对应到两个区间中
- U+D800 对应的二进制数为 1101100000000000, 将0001000110填充在它的后10 个二进制位,得到1101100001000110,转成 16 进制数为 0xD846。同理,低位为 0xDC00,所以这个字的UTF-16编码为 0xD846 0xDC00
(4) UTF-32
UTF-32 就是字符所对应编号的整数二进制形式,每个字符占四个字节,这个是直接进行转换的。该编码方式占用的储存空间较多,所以使用较少。
比如“马” 字的Unicode编号是:U+9A6C,整数编号是39532,直接转化为二进制:1001 1010 0110 1100,这就是它的UTF-32编码。
(5)总结
Unicode、UTF-8、UTF-16、UTF-32有什么区别?
- Unicode 是编码字符集(字符集),而UTF-8、UTF-16、UTF-32是字符集编码(编码规则);
- UTF-16 使用变长码元序列的编码方式,相较于定长码元序列的UTF-32算法更复杂,甚至比同样是变长码元序列的UTF-8也更为复杂,因为其引入了独特的代理对这样的代理机制;
- UTF-8需要判断每个字节中的开头标志信息,所以如果某个字节在传送过程中出错了,就会导致后面的字节也会解析出错;而UTF-16不会判断开头标志,即使错也只会错一个字符,所以容错能力教强;
- 如果字符内容全部英文或英文与其他文字混合,但英文占绝大部分,那么用UTF-8就比UTF-16节省了很多空间;而如果字符内容全部是中文这样类似的字符或者混合字符中中文占绝大多数,那么UTF-16就占优势了,可以节省很多空间;
五、常见的位运算符有哪些?其计算规则是什么?
现代计算机中数据都是以二进制的形式存储的,即0、1两种状态,计算机对二进制数据进行的运算加减乘除等都是叫位运算,即将符号位共同参与运算的运算。
常见的位运算有以下几种:
运算符 | 描述 | 运算规则 |
---|---|---|
& | 与 | 两个位都为1时,结果才为1 |
| | 或 | 两个位都为0时,结果才为0 |
^ | 异或 | 两个位相同为0,相异为1 |
~ | 取反 | 0变1,1变0 |
<< | 左移 | 各二进制位全部左移若干位,高位丢弃,低位补0 |
>> | 右移 | 各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃 |
六、常见的DOM操作有哪些
DOM 节点的获取
(1)DOM 节点的获取的API及使用:
getElementById // 按照 id 查询
getElementsByTagName // 按照标签名查询
getElementsByClassName // 按照类名查询
querySelectorAll // 按照 css 选择器查询
按照 id 查询
var imooc = document.getElementById('imooc') // 查询到 id 为 imooc 的元素
按照标签名查询
var pList = document.getElementsByTagName('p') // 查询到标签为 p 的集合
console.log(divList.length)
console.log(divList[0])
按照类名查询
var moocList = document.getElementsByClassName('mooc') // 查询到类名为 mooc 的集合
按照 css 选择器查询
var pList = document.querySelectorAll('.mooc') // 查询到类名为 mooc 的集合
(2)DOM 节点的创建
创建一个新节点,并把它添加到指定节点的后面。已知的 HTML 结构如下:
<html><head><title>DEMO</title></head><body><div id="container"><h1 id="title">我是标题</h1></div> </body>
</html>
要求添加一个有内容的 span 节点到 id 为 title 的节点后面,做法就是:
1.首先获取父节点
var container = document.getElementById('container')
2.创建新节点
var targetSpan = document.createElement('span')
3.设置 span 节点的内容
targetSpan.innerHTML = 'hello world'
4.把新创建的元素塞进父节点里去
container.appendChild(targetSpan)
(3)DOM 节点的删除
删除指定的 DOM 节点,已知的 HTML 结构如下:
<html><head><title>DEMO</title></head><body><div id="container"><h1 id="title">我是标题</h1></div> </body>
</html>
需要删除 id 为 title 的元素,做法是:
1.获取目标元素的父元素
var container = document.getElementById('container')
2.获取目标元素
var targetNode = document.getElementById('title')
3.删除目标元素
container.removeChild(targetNode)
或者通过子节点数组来完成删除:
1.获取目标元素的父元素
var container = document.getElementById('container')
2.获取目标元素
var targetNode = container.childNodes[1]
3.删除目标元素
container.removeChild(targetNode)
(4)修改 DOM 元素
修改 DOM 元素这个动作可以分很多维度,比如说移动 DOM 元素的位置,修改 DOM 元素的属性等。
将指定的两个 DOM 元素交换位置,已知的 HTML 结构如下:
<html><head><title>DEMO</title></head><body><div id="container"><h1 id="title">我是标题</h1><p id="content">我是内容</p></div> </body>
</html>
现在需要调换 title 和 content 的位置,可以考虑 insertBefore 或者 appendChild:
1.获取父元素
var container = document.getElementById('container')
2.获取两个需要被交换的元素
var title = document.getElementById('title')
var content = document.getElementById('content')
3.交换两个元素,把 content 置于 title 前面
container.insertBefore(content, title)