5.14面试复盘

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)

插入排序的工作方式像许多人排序一手扑克牌。开始时,我们的左手为空并且桌子上的牌面向下。然后,我们每次从桌子上拿走一张牌并将它插入左手中正确的位置。为了找到一张牌的正确位置,我们从右到左将它与已在手中的每张牌进行比较。拿在左手上的牌总是排序好的,原来这些牌是桌子上牌堆中顶部的牌 。
插入排序是指在待排序的元素中,假设前面n-1(其中n>=2)个数已经是排好顺序的,现将第n个数插到前面已经排好的序列中,然后找到合适自己的位置,使得插入第n个数的这个序列也是排好顺序的。按照此法对所有元素进行插入,直到整个序列排为有序的过程,称为插入排序

插入排序动图演示:
在这里插入图片描述
插入排序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);
    })
}
//输出0123456789

变量 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.1415926";
PI  // 3.1415926

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.1415926";
}

ES6 明确规定,代码块内如果存在 let 或者 const,代码块会对这些命令声明的变量从块的开始就形成一个封闭作用域。代码块内,在声明变量 PI 之前使用它会报错。

在 ES6 之前,JavaScript 只有两种作用域: 全局变量函数内的局部变量

全局变量

在函数外声明的变量作用域是全局的:.

在函数体外或代码块外使用 varlet 关键字声明的变量也有点类似。

它们的作用域都是 全局的:

var carName = "Volvo";
 
// 这里可以使用 carName 变量
// 使用 let
let x = 2;       // 全局作用域
 
function myFunction() {
   
   
    // 这里也可以使用 carName 变量
}

全局变量在 JavaScript 程序的任何地方都可以访问。

局部变量

在函数内声明的变量作用域是局部的(函数内):

在函数体内使用 varlet 关键字声明的变量有点类似。

它们的作用域都是 局部的:

// 这里不能使用 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 关键字来重置 varlet关键字声明的变量:

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;
   this.name = "ColorException";
}
 
function getConstantName(color) {
   
   
    switch (color) {
   
   
        case 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值