5.14面试复盘
5.14上午前端开发面试
笔试题
1.写出一种你最熟悉的排序算法
十大经典算法排序总结对比

讯享网
名词解释:
n: 数据规模
k:“桶”的个数
In-place: 占用常数内存,不占用额外内存
Out-place: 占用额外内存
稳定性:排序后2个相等键值的顺序和排序之前它们的顺序相同
冒泡排序(Bubble Sort)
冒泡排序算法的原理如下:
1.比较相邻的元素。如果第一个比第二个大,就交换他们两个。
2.对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
3.针对所有的元素重复以上的步骤,除了最后一个。
4.持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
冒泡排序动图演示:

冒泡排序JavaScript代码实现:
function bubbleSort(arr){
var len = arr.length; for(var i = 0; i < len; i++){
for(var j = 0;j < len - i -1; j++){
if(arr[j]>arr[j+1]){
var swap = arr[j]; arr[j] = arr[j+1]; arr[j+1] = swap; } } } return arr; }
讯享网
插入排序(Insertion Sort)
插入排序动图演示:

插入排序JavaScript代码实现:
讯享网 function insertSort(arr){
var len = arr.length; var preIndex,current; for(var i = 1; i < len; i++){
preIndex = i - 1; current = arr[i]; while(preIndex>=0&&arr[preIndex]>current){
arr[preIndex+1] = arr[preIndex]; preIndex--; } arr[preIndex+1] = current; } return arr; }
选择排序(Selection Sort)
选择排序思想:“n个记录的文件的直接选择排序可经过n-1趟直接选择排序得到有序结果: ①初始状态:无序区为R[1…n],有序区为空。 ②第1趟排序 在无序区R[1…n]中选出关键字最小的记录R[k],将它与无序区的第1个记录R[1]交换,使R[1…1]和R[2…n]分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区。 …… ③第i趟排序 第i趟排序开始时,当前有序区和无序区分别为R[1…i-1]和R(i…n)。该趟排序从当前无序区中选出关键字最小的记录 R[k],将它与无序区的第1个记录R交换,使R[1…i]和R分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区。”
选择排序动图演示:

选择排序JavaScript代码实现:
function selectionSort(arr) {
var len = arr.length; var minIndex, temp; for (var i = 0; i < len - 1; i++) {
minIndex = i; for (var j = i + 1; j < len; j++) {
if (arr[j] < arr[minIndex]) {
//寻找最小的数 minIndex = j; //将最小数的索引保存 } } temp = arr[i]; arr[i] = arr[minIndex]; arr[minIndex] = temp; } return arr; }
希尔排序(Shell Sort)
希尔排序算法:先取一个小于n的整数d1作为第一个增量,把文件的全部记录分组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量 =1( < …<d2<d1),即所有记录放在同一组中进行直接插入排序为止。
该方法实质上是一种分组插入方法
比较相隔较远距离(称为增量)的数,使得数移动时能跨过多个元素,则进行一次比较就可能消除多个元素交换。D.L.shell于1959年在以他名字命名的排序算法中实现了这一思想。算法先将要排序的一组数按某个增量d分成若干组,每组中记录的下标相差d.对每组中全部元素进行排序,然后再用一个较小的增量对它进行,在每组中再进行排序。当增量减到1时,整个要排序的数被分成一组,排序完成。
一般的初次取序列的一半为增量,以后每次减半,直到增量为1。
给定实例的shell排序的排序过程
假设待排序文件有10个记录,其关键字分别是:
49,38,65,97,76,13,27,49,55,04。
增量序列的取值依次为:
5,2,1


希尔排序JavaScript代码实现:
讯享网function shellSort(arr) {
var len = arr.length, temp, gap = 1; while(gap < len/3) {
//动态定义间隔序列 gap =gap*3+1; } for (gap; gap > 0; gap = Math.floor(gap/3)) {
for (var i = gap; i < len; i++) {
temp = arr[i]; for (var j = i-gap; j >= 0 && arr[j] > temp; j-=gap) {
arr[j+gap] = arr[j]; } arr[j+gap] = temp; } } return arr; }
归并排序(Merge Sort)
作为一种典型的分而治之思想的算法应用,归并排序的实现由两种方法:
自上而下的递归(所有递归的方法都可以用迭代重写,所以就有了第2种方法)
自下而上的迭代
在《数据结构与算法JavaScript描述》中,作者给出了自下而上的迭代方法。但是对于递归法,作者却认为:
However, it is not possible to do so in JavaScript, as the recursion goes too deep
for the language to handle.
然而,在 JavaScript 中这种方式不太可行,因为这个算法的递归深度对它来讲太深了。
归并排序动图演示:

归并排序JavaScript代码实现:
function mergeSort(arr) {
//采用自上而下的递归方法 var len = arr.length; if(len < 2) {
return arr; } var middle = Math.floor(len / 2), left = arr.slice(0, middle), right = arr.slice(middle); return merge(mergeSort(left), mergeSort(right)); } function merge(left, right) {
var result = []; while (left.length && right.length) {
if (left[0] <= right[0]) {
result.push(left.shift()); } else {
result.push(right.shift()); } } while (left.length) result.push(left.shift()); while (right.length) result.push(right.shift()); return result; }
快速排序(Quick Sort)
快速排序动图演示:

快速排序使用分治法来把一个串(list)分为两个子串(sub-lists)。具体算法描述如下:
讯享网var quickSort = function(arr) {
if (arr.length <= 1) {
return arr; } var pivotIndex = Math.floor(arr.length / 2); var pivot = arr.splice(pivotIndex, 1)[0]; var left = []; var right = []; for (var i = 0; i < arr.length; i++){
if (arr[i] < pivot) {
left.push(arr[i]); } else {
right.push(arr[i]); } } return quickSort(left).concat([pivot], quickSort(right)); };
堆排序(Heap Sort)
堆排序的基本思想是:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了
1.大顶堆:每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列
2.小顶堆:每个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列
堆排序JavaScript代码实现:
var len; //因为声明的多个函数都需要数据长度,所以把len设置成为全局变量 function buildMaxHeap(arr) {
//建立大顶堆 len = arr.length; for (var i = Math.floor(len/2); i >= 0; i--) {
heapify(arr, i); } } function heapify(arr, i) {
//堆调整 var left = 2 * i + 1, right = 2 * i + 2, largest = i; if (left < len && arr[left] > arr[largest]) {
largest = left; } if (right < len && arr[right] > arr[largest]) {
largest = right; } if (largest != i) {
swap(arr, i, largest); heapify(arr, largest); } } function swap(arr, i, j) {
var temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } function heapSort(arr) {
buildMaxHeap(arr); for (var i = arr.length-1; i > 0; i--) {
swap(arr, 0, i); len--; heapify(arr, 0); } return arr; }
计数排序(Counting Sort)

讯享网function countingSort(arr, maxValue) {
var bucket = new Array(maxValue+1), sortedIndex = 0; arrLen = arr.length, bucketLen = maxValue + 1; for (var i = 0; i < arrLen; i++) {
if (!bucket[arr[i]]) {
bucket[arr[i]] = 0; } bucket[arr[i]]++; } for (var j = 0; j < bucketLen; j++) {
while(bucket[j] > 0) {
arr[sortedIndex++] = j; bucket[j]--; } } return arr; }
桶排序(Bucket Sort)
桶排序JavaScript代码实现:
function bucketSort(arr, bucketSize) {
if (arr.length === 0) {
return arr; } var i; var minValue = arr[0]; var maxValue = arr[0]; for (i = 1; i < arr.length; i++) {
if (arr[i] < minValue) {
minValue = arr[i]; //输入数据的最小值 } else if (arr[i] > maxValue) {
maxValue = arr[i]; //输入数据的最大值 } } //桶的初始化 var DEFAULT_BUCKET_SIZE = 5; //设置桶的默认数量为5 bucketSize = bucketSize || DEFAULT_BUCKET_SIZE; var bucketCount = Math.floor((maxValue - minValue) / bucketSize) + 1; var buckets = new Array(bucketCount); for (i = 0; i < buckets.length; i++) {
buckets[i] = []; } //利用映射函数将数据分配到各个桶中 for (i = 0; i < arr.length; i++) {
buckets[Math.floor((arr[i] - minValue) / bucketSize)].push(arr[i]); } arr.length = 0; for (i = 0; i < buckets.length; i++) {
insertionSort(buckets[i]); //对每个桶进行排序,这里使用了插入排序 for (var j = 0; j < buckets[i].length; j++) {
arr.push(buckets[i][j]); } } return arr; }
基数排序(Radix Sort)
MSD 从高位开始进行排序
LSD 从低位开始进行排序
LSD基数排序动图演示:

基数排序JavaScript代码实现:
讯享网//LSD Radix Sort var counter = []; function radixSort(arr, maxDigit) {
var mod = 10; var dev = 1; for (var i = 0; i < maxDigit; i++, dev *= 10, mod *= 10) {
for(var j = 0; j < arr.length; j++) {
var bucket = parseInt((arr[j] % mod) / dev); if(counter[bucket]==null) {
counter[bucket] = []; } counter[bucket].push(arr[j]); } var pos = 0; for(var j = 0; j < counter.length; j++) {
var value = null; if(counter[j]!=null) {
while ((value = counter[j].shift()) != null) {
arr[pos++] = value; } } } } return arr; }
2.从ES6以下特性中挑选出一个来谈谈
简介
ES6, 全称 ECMAScript 6.0 ,是 JavaScript 的下一个版本标准,2015.06 发版。
ES6 主要是为了解决 ES5 的先天不足,比如 JavaScript 里并没有类的概念,但是目前浏览器的 JavaScript 是 ES5 版本,大多数高版本的浏览器也支持 ES6,不过只实现了 ES6 的部分特性和功能。
ECMAScript 的背景
JavaScript 是大家所了解的语言名称,但是这个语言名称是商标( Oracle 公司注册的商标)。因此,JavaScript 的正式名称是 ECMAScript 。1996年11月,JavaScript 的创造者网景公司将 JS 提交给国际化标准组织 ECMA(European computer manufactures association,欧洲计算机制造联合会),希望这种语言能够成为国际标准,随后 ECMA 发布了规定浏览器脚本语言的标准,即 ECMAScript。这也有利于这门语言的开放和中立。
ECMAScript 的历史
ES6 是 ECMAScript 标准十余年来变动最大的一个版本,为其添加了许多新的语法特性。
1997 年 ECMAScript 1.0 诞生。
1998 年 6 月 ECMAScript 2.0 诞生,包含一些小的更改,用于同步独立的 ISO 国际标准。
1999 年 12 月 ECMAScript 3.0诞生,它是一个巨大的成功,在业界得到了广泛的支持,它奠定了 JS 的基本语法,被其后版本完全继承。直到今天,我们一开始学习 JS ,其实就是在学 3.0 版的语法。
2000 年的 ECMAScript 4.0 是当下 ES6 的前身,但由于这个版本太过激烈,对 ES 3 做了彻底升级,所以暂时被"和谐"了。
2009 年 12 月,ECMAScript 5.0 版正式发布。ECMA 专家组预计 ECMAScript 的第五个版本会在 2013 年中期到 2018 年作为主流的开发标准。2011年6月,ES 5.1 版发布,并且成为 ISO 国际标准。
2013 年,ES6 草案冻结,不再添加新的功能,新的功能将被放到 ES7 中;2015年6月, ES6 正式通过,成为国际标准。
ES6 的目标与愿景
成为更好编写的开发语言有以下目标。
适应更复杂的应用;实现代码库之间的共享;不断迭代维护新版本。

ES6 let 与 const
ES2015(ES6) 新增加了两个重要的 JavaScript 关键字: let 和 const。
let 声明的变量只在 let 命令所在的代码块内有效。
const 声明一个只读的常量,一旦声明,常量的值就不能改变。
const定义常量与使用let 定义的变量相似:
- 二者都是块级作用域
- 都不能和它所在作用域内的其他变量或函数拥有相同的名称
两者还有以下两点区别:
const声明的常量必须初始化,而let声明的变量不用- const 定义常量的值不能通过再赋值修改,也不能再次声明。而 let 定义的变量值可以修改。
let 命令
基本用法:
{
let a = 0; a // 0 } a // 报错 ReferenceError: a is not defined
代码块内有效
let是在代码块有效,var 是在全局范围有效
讯享网{
let a = 0; var b = 1; } a // ReferenceError: a is not defined b // 1
不能重复声明
let 只能声明一次 var 可以声明多次:
let a = 1; let a = 2; var b = 3; var b = 4; a // Identifier 'a' has already been declared b // 4
for 循环计数器很适合用 let
讯享网for(var i = 0; i < 10; i++){
setTimeout(function(){
console.log(i); }) } //输出十个10 for(let j = 0; j < 10; j++){
setTimeout(function(){
console.log(j); }) } //输出0
变量 i 是用 var 声明的,在全局范围内有效,所以全局中只有一个变量 i, 每次循环时,setTimeout 定时器里面的 i 指的是全局变量 i ,而循环里的十个 setTimeout 是在循环结束后才执行,所以此时的 i 都是 10。
变量 j 是用 let 声明的,当前的 j 只在本轮循环中有效,每次循环的 j 其实都是一个新的变量,所以 setTimeout 定时器里面的 j 其实是不同的变量,即最后输出 12345。(若每次循环的变量 j 都是重新声明的,如何知道前一个循环的值?这是因为 JavaScript 引擎内部会记住前一个循环的值)。
不存在变量提升
let 不存在变量提升,var 会变量提升:
变量提升
JavaScript 中,var 关键字定义的变量可以在使用后声明,也就是变量可以先使用再声明
let 关键字定义的变量则不可以在使用后声明,也就是变量需要先声明再使用。
const 关键字定义的变量则不可以在使用后声明,也就是变量需要先声明再使用.
console.log(a); //ReferenceError: a is not defined let a = "apple"; console.log(b); //undefined var b = "banana";
变量 b 用 var 声明存在变量提升,所以当脚本开始运行的时候,b 已经存在了,但是还没有赋值,所以会输出 undefined。
变量 a 用 let 声明不存在变量提升,在声明变量 a 之前,a 不存在,所以会报错。
const命令
const 声明一个只读变量,声明之后不允许改变。意味着,一旦声明必须初始化,否则会报错。
const 用于声明一个或多个常量,声明时必须进行初始化,且初始化后值不可再修改:
基本用法:
讯享网const PI = "3."; PI // 3. const MY_AGE; // SyntaxError: Missing initializer in const declaration
暂时性死区:
var PI = "a"; if(true){
console.log(PI); // ReferenceError: PI is not defined const PI = "3."; }
ES6 明确规定,代码块内如果存在 let 或者 const,代码块会对这些命令声明的变量从块的开始就形成一个封闭作用域。代码块内,在声明变量 PI 之前使用它会报错。
在 ES6 之前,JavaScript 只有两种作用域: 全局变量 与 函数内的局部变量。
全局变量
在函数外声明的变量作用域是全局的:.
在函数体外或代码块外使用 var 和 let 关键字声明的变量也有点类似。
它们的作用域都是 全局的:
讯享网var carName = "Volvo"; // 这里可以使用 carName 变量 // 使用 let let x = 2; // 全局作用域 function myFunction() {
// 这里也可以使用 carName 变量 }
全局变量在 JavaScript 程序的任何地方都可以访问。
局部变量
在函数内声明的变量作用域是局部的(函数内):
在函数体内使用 var 和 let 关键字声明的变量有点类似。
它们的作用域都是 局部的:
// 这里不能使用 carName 变量 function myFunction() {
var carName = "Volvo"; // 这里可以使用 carName 变量 } // 这里不能使用 carName 变量 // 使用 let function myFunction() {
let carName = "Volvo"; // 局部作用域 }
函数内使用 var 声明的变量只能在函数内容访问,如果不使用 var 则是全局变量。
JavaScript 块级作用域(Block Scope)
使用 var 关键字声明的变量不具备块级作用域的特性,它在 {} 外依然能被访问到。
讯享网{
var x = 2; } // 这里可以使用 x 变量
在 ES6 之前,是没有块级作用域的概念的。
ES6 可以使用 let 关键字来实现块级作用域。
let 声明的变量只在 let 命令所在的代码块 {} 内有效,在 {} 之外不能访问。

{
let x = 2; } // 这里不能使用 x 变量
重新定义变量
使用 var 关键字重新声明变量可能会带来问题。
在块中重新声明变量也会重新声明块外的变量:
讯享网var x = 10; // 这里输出 x 为 10 {
var x = 2; // 这里输出 x 为 2 } // 这里输出 x 为 2
let 关键字就可以解决这个问题,因为它只在 let 命令所在的代码块 {} 内有效。
var x = 10; // 这里输出 x 为 10 {
let x = 2; // 这里输出 x 为 2 } // 这里输出 x 为 10
循环作用域
使用 var 关键字:
讯享网var i = 5; for (var i = 0; i < 10; i++) {
// 一些代码... } // 这里输出 i 为 10
使用 let 关键字:
let i = 5; for (let i = 0; i < 10; i++) {
// 一些代码... } // 这里输出 i 为 5
在第一个实例中,使用了 var 关键字,它声明的变量是全局的,包括循环体内与循环体外。
在第二个实例中,使用 let 关键字, 它声明的变量作用域只在循环体内,循环体外的变量不受影响。
HTML 代码中使用全局变量
在 JavaScript 中, 全局作用域是针对 JavaScript 环境。
在 HTML 中, 全局作用域是针对 window 对象。
使用 var 关键字声明的全局作用域变量属于 window 对象:
讯享网var carName = "Volvo"; // 可以使用 window.carName 访问变量
使用 let 关键字声明的全局作用域变量不属于 window 对象:
let carName = "Volvo"; // 不能使用 window.carName 访问变量
重置变量
使用 var 关键字声明的变量在任何地方都可以修改:
讯享网var x = 2; // x 为 2 var x = 3; // 现在 x 为 3
在相同的作用域或块级作用域中,不能使用 let 关键字来重置 var 关键字声明的变量:
在相同的作用域或块级作用域中,不能使用 const 关键字来重置 var 和 let关键字声明的变量:
var x = 2; // 合法 let x = 3; // 不合法 {
var x = 4; // 合法 let x = 5 // 不合法 } var x = 2; // 合法 const x = 2; // 不合法 {
let x = 2; // 合法 const x = 2; // 不合法 }
在相同的作用域或块级作用域中,不能使用 let 关键字来重置 let 关键字声明的变量:
在相同的作用域或块级作用域中,不能使用 const 关键字来重置 const 关键字声明的变量:
讯享网let x = 2; // 合法 let x = 3; // 不合法 {
let x = 4; // 合法 let x = 5; // 不合法 } const x = 2; // 合法 const x = 3; // 不合法 x = 3; // 不合法 var x = 3; // 不合法 let x = 3; // 不合法 {
const x = 2; // 合法 const x = 3; // 不合法 x = 3; // 不合法 var x = 3; // 不合法 let x = 3; // 不合法 }
在相同的作用域或块级作用域中,不能使用 var 关键字来重置 let 关键字声明的变量:
const 关键字在不同作用域,或不同块级作用域中是可以重新声明赋值的:
let x = 2; // 合法 var x = 3; // 不合法 {
let x = 4; // 合法 var x = 5; // 不合法 }
let 关键字在不同作用域,或不同块级作用域中是可以重新声明赋值的:
const 关键字在不同作用域,或不同块级作用域中是可以重新声明赋值的:
讯享网let x = 2; // 合法 {
let x = 3; // 合法 } {
let x = 4; // 合法 } const x = 2; // 合法 {
const x = 3; // 合法 } {
const x = 4; // 合法 }
注意要点
const 如何做到变量在声明初始化之后不允许改变的?其实 const 其实保证的不是变量的值不变,而是保证变量指向的内存地址所保存的数据不允许改动。此时,你可能已经想到,简单类型和复合类型保存值的方式是不同的。是的,对于简单类型(数值 number、字符串 string 、布尔值 boolean),值就保存在变量指向的那个内存地址,因此 const 声明的简单类型变量等同于常量。而复杂类型(对象 object,数组 array,函数 function),变量指向的内存地址其实是保存了一个指向实际数据的指针,所以 const 只能保证指针是固定的,至于指针指向的数据结构变不变就无法控制了,所以使用 const 声明复杂类型对象时要慎重。
ES6 解构赋值
概述
解构赋值是对赋值运算符的扩展。
他是一种针对数组或者对象进行模式匹配,然后对其中的变量进行赋值。
在代码书写上简洁且易读,语义更加清晰明了;也方便了复杂对象中数据字段获取。
解构模型
在解构中,有下面两部分参与:
解构的源,解构赋值表达式的右边部分。解构的目标,解构赋值表达式的左边部分。
数组模型的解构(Array)
基本
let [a, b, c] = [1, 2, 3]; // a = 1 // b = 2 // c = 3
可嵌套
讯享网let [a, [[b], c]] = [1, [[2], 3]]; // a = 1 // b = 2 // c = 3
可忽略
let [a, , b] = [1, 2, 3]; // a = 1 // b = 3
不完全解构
讯享网let [a = 1, b] = []; // a = 1, b = undefined
剩余运算符
let [a, ...b] = [1, 2, 3]; //a = 1 //b = [2, 3]
字符串等
在数组的解构中,解构的目标若为可遍历对象,皆可进行解构赋值。可遍历对象即实现 Iterator 接口的数据。
讯享网let [a, b, c, d, e] = 'hello'; // a = 'h' // b = 'e' // c = 'l' // d = 'l' // e = 'o'
解构默认值
let [a = 2] = [undefined]; // a = 2 //当解构模式有匹配结果,且匹配结果是 undefined 时,会触发默认值作为返回结果。 let [a = 3, b = a] = []; // a = 3, b = 3 let [a = 3, b = a] = [1]; // a = 1, b = 1 let [a = 3, b = a] = [1, 2]; // a = 1, b = 2
- a 与 b 匹配结果为 undefined ,触发默认值:a = 3; b = a =3
- a 正常解构赋值,匹配结果:a = 1,b 匹配结果 undefined ,触发默认值:b = a =1
- a 与 b 正常解构赋值,匹配结果:a = 1,b = 2
对象模型的解构(Object)
基本
讯享网let {
foo, bar } = {
foo: 'aaa', bar: 'bbb' }; // foo = 'aaa' // bar = 'bbb' let {
baz : foo } = {
baz : 'ddd' }; // foo = 'ddd'
可嵌套可忽略
let obj = {
p: ['hello', {
y: 'world'}] }; let {
p: [x, {
y }] } = obj; // x = 'hello' // y = 'world' let obj = {
p: ['hello', {
y: 'world'}] }; let {
p: [x, {
}] } = obj; // x = 'hello'
不完全解构
讯享网let obj = {
p: [{
y: 'world'}] }; let {
p: [{
y }, x ] } = obj; // x = undefined // y = 'world'
剩余运算符
let {
a, b, ...rest} = {
a: 10, b: 20, c: 30, d: 40}; // a = 10 // b = 20 // rest = {c: 30, d: 40}
解构默认值
讯享网let {
a = 10, b = 5} = {
a: 3}; // a = 3; b = 5; let {
a: aa = 10, b: bb = 5} = {
a: 3}; // aa = 3; bb = 5;
ES6 Symbol
概述
ES6 引入了一种新的原始数据类型 Symbol ,表示独一无二的值,最大的用法是用来定义对象的唯一属性名。
ES6 数据类型除了 Number 、 String 、 Boolean 、 Objec t、 null 和 undefined ,还新增了 Symbol 。
基本用法
Symbol 函数栈不能用 new 命令,因为 Symbol 是原始数据类型,不是对象。可以接受一个字符串作为参数,为新创建的 Symbol 提供描述,用来显示在控制台或者作为字符串的时候使用,便于区分。
let sy = Symbol("KK"); console.log(sy); // Symbol(KK) typeof(sy); // "symbol" // 相同参数 Symbol() 返回的值不相等 let sy1 = Symbol("kk"); sy === sy1; // false
使用场景
作为属性名
用法
由于每一个 Symbol 的值都是不相等的,所以 Symbol 作为对象的属性名,可以保证属性不重名。
讯享网let sy = Symbol("key1"); // 写法1 let syObject = {
}; syObject[sy] = "kk"; console.log(syObject); // {Symbol(key1): "kk"} // 写法2 let syObject = {
[sy]: "kk" }; console.log(syObject); // {Symbol(key1): "kk"} // 写法3 let syObject = {
}; Object.defineProperty(syObject, sy, {
value: "kk"}); console.log(syObject); // {Symbol(key1): "kk"}
Symbol 作为对象属性名时不能用.运算符,要用方括号。因为.运算符后面是字符串,所以取到的是字符串 sy 属性,而不是 Symbol 值 sy 属性。
let syObject = {
}; syObject[sy] = "kk"; syObject[sy]; // "kk" syObject.sy; // undefined
注意点
Symbol 值作为属性名时,该属性是公有属性不是私有属性,可以在类的外部访问。但是不会出现在 for…in 、 for…of 的循环中,也不会被 Object.keys() 、 Object.getOwnPropertyNames() 返回。如果要读取到一个对象的 Symbol 属性,可以通过 Object.getOwnPropertySymbols() 和 Reflect.ownKeys() 取到。
讯享网let syObject = {
}; syObject[sy] = "kk"; console.log(syObject); for (let i in syObject) {
console.log(i); } // 无输出 Object.keys(syObject); // [] Object.getOwnPropertySymbols(syObject); // [Symbol(key1)] Reflect.ownKeys(syObject); // [Symbol(key1)]
定义常量
在 ES5 使用字符串表示常量。例如:
const COLOR_RED = "red"; const COLOR_YELLOW = "yellow"; const COLOR_BLUE = "blue";
但是用字符串不能保证常量是独特的,这样会引起一些问题:
讯享网const COLOR_RED = "red"; const COLOR_YELLOW = "yellow"; const COLOR_BLUE = "blue"; const MY_BLUE = "blue"; function ColorException(message) {
this.message = message;
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/51734.html