ES6汇总(持续更新中)

1,075

一、概念篇

ES5、ES6是什么,有什么区别?

参考答案 ES5泛指上一代语言标准。ES6泛指下一代JS语言标准,包含ES2015、ES2016、ES2017、ES2018等。

ES5、ES6时间分界线是那一年?

参考答案 2015年

babel是什么,有什么作用,在webpack中怎么使用?

参考答案 Babel是一个JavaScript编译器,可以将ES6代码转为ES5代码,使在目前不支持ES6浏览器上执行ES6代码。

二、变量篇

var、let、const有什么区别?

参考答案
  • var作用域是函数作用域,let、const作用域是块级作用域。
  • var存在变量提升,let、const不存在变量提升,换句话来说let、const必须声明后才能使用。
  • let、const在同个作用域中不能重复定义。
  • const一般来声明常量,声明后不可以更改。
  • const声明后必须马上赋值。

const声明的变量一定不能修改吗?

参考答案

const实际上保证的并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。

对于基本类型的数据(Undefined、Null、Boolean、Number、String和Symbol〉而言,值就保存在变量指向的内存地址中,因 此等同于常量。

但对于引用类型的数据(主要是对象和数组)而言,变量指向的内存地址,其保存的只是个指针,指针指向堆内存中的数据。

const只能保证这个指针是固定的,至于它指向的数据是可以被修改的。

const foo = {};
//为foo加一个属性,可以成功
foo.prop = 123;
//将foo指向另一个对象,就会报错
foo = { prop : 123 }; //TypeError fo is read-only 

a[6]()的结果是什么,为什么?

var a=[];
for(var i= 0; i<10; i++){
    a[i]= () =>{
        console.log(i)
    }
}
参考答案 结果是10。因为上面的代码中,变量`i`是var声明的,在全局范围内都有效,所以全局只有`i`个变量,每一次循环,变量`i`的值都会发生改变,而循环内,被赋给数组`a`的函数内部的`console.log(i)`中的`i`指向全局的`i`。也就是说,所有数组`a`的成员中的`i`指向的都是同一个`i`,导致运行时输出的是最后一轮的`i`值,也就是10。

a[6]()的结果是什么,为什么?

var a=[];
for(let i= 0; i<10; i++){
    a[i]= () =>{
        console.log(i)
    }
}
参考答案 结果是6。因为上面的代码中,变量`i`是let声明的,当前的`i`只在本轮循环有效。所以每一次循环的 其实都是`i`个新的变量,另外JavaScript引擎内部会记住上一轮循环的值,初始化本轮的变量`i`时,就在上一轮循环的基础上进行计算。于是最后输出的是6。

下面代码输出是什么,为什么?

for (let i = O; i < 3; i++){
    let i='abc';
    console.log(i);
}
参考答案

结果是打印三次abc。因为for循环有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。

下面代码各输出什么,为什么?

console.log(a);
console.log(b);
var a = 1;
let b = 1;
参考答案

结果是undefinedVM30:2 Uncaught ReferenceError: b is not defined。因为在以上代码中,变量a使用var命令声明会发生变量提升,即脚本开始运行时,变量a便己经存在,但是没有值,所以会输出undefined。变量b使用let命令令声明则不会发生变量提升。这表示在声明它之前,变量b是不存在的,这时如果用到它,就会抛出一个错误。

什么是块级作用域

参考答案

是一个语句,将多个操作封装在一起,通常是放在一个大括号里,没有返回值。

执行函数f输出什么,为什么?

function f () {
    let n = 5;
    if (true) {
        let n = 10;
    }
    console.log(n);
}
参考答案

输出5,因为if (true) { let n = 10;}中变量n,是处于块级作用域中,影响不到外面的变量n

在ES6环境中执行函数f输出什么,为什么?

function f(x = y,y = 2){
    return {x,y}
}
参考答案

报错。因为let声明的变量有暂时性死区,在参数y没声明前调用,即赋值给x,就会报错。

什么是解构赋值?

参考答案

按照一定模式从数组和对象中提取值,然后对变量进行赋值。

在解构赋值时指定默认值,什么时候才能生效?

参考答案

当等号右边数组中对应的值严格等于undefined

在对数组解构赋值时,等号右边必须满足什么条件?

参考答案

必须满足其值或是转为对象后,要有Iterator接口。

下面代码执行结果是?

let [a, [, , [b]], d] = [1, [2, 3, [34]], 4];
console.log(b)
参考答案 34

对数组的解构赋值和对对象的解构赋值有什么区别?

参考答案 数组的元素是按次序排列的,变量的取值是由它的位置决定的。而对象的属性没有次序,变量必须与属性同名才能取到正确的值。

怎么用解构赋值的方法获取对象obj中的a,然后赋值给变量c,let obj = {a: 'a元素',b: 'b元素'}

参考答案
let obj = { a: 'a元素', b: 'b元素' }
let { a: c } = obj;

对象的解构赋值的内部机制是咋样的?

参考答案
let { a: a, b: b } = { a: 'a元素', b: 'b元素' }

按以上代码,是先找到同名属性a,然后再赋值给对应的变量a。真正被赋值的是后者,而不是前者。

下面代码执行结果是?

let obj = {
    loc: {
        start: {
            line: 1,
            column: 5
        }
    }
}
let {loc: { start: { line } } } = obj;
console.log(loc)
console.log(start)
console.log(line)
参考答案

因为以上只有line是变量,loc、start都是模式。故输出ReferenceError: loc is not definedReferenceError: start is not defined1

如果要将一个已经声明的变量用于解构赋值,要怎么写?

参考答案
let x;
({ x } = { x: 1 })

因为JavaScript引擎会将{x}理解成一个代码块,从而发生语法错误。只有不将大括号写在行首,避免JavaScript将其解释为代码块才能解决这个问题。

字符串可以解构赋值吗?

参考答案

可以

let [a, b, c, d] = 'test';
console.log(a)//t
console.log(b)//e
console.log(c)//s
console.log(d)//t
let { length } = 'test';
console.log(length)//4

解构赋值的规则是什么?

参考答案

只要等号右边的值不是对象或数组,就先将其转为对象。

解构赋值的用途有哪些?

参考答案
  • 交换变量的值
    let x = 1;
    let y = 2;
    [x, y] = [y, x];
    
  • 函数参数的默认值
  • 提取JSON数据

三、字符串篇

字符串str是'XMS00087672542-YD',怎么判断str中有'876'? 怎么判断str头部有'XM'? 怎么判断str倒数第二个字符是'Y'? 怎么判断str中第三个字符是'S'?

参考答案
const str = 'XMS00087672542-YD';
console.log(str.includes('876'))//如果str中有'876',则是true,没有为false
console.log(str.startsWith('XM'))//如果str头部有'XM',则是true,没有为false
console.log(str.endsWith('Y',str.length-1))//如果str倒数第二个字符是'Y',则是true,没有为false
console.log(str.startsWith('S', 2))//如果str中第三个字符是'S',则是true,没有为false

String.includes()、String.startsWith()、String.endsWith(),这三个方法是做什么的,第二参数分别代表什么含义?

参考答案
  • String.includes(str,n)表示是否找到了字符串str,找到返回true,找不到返回false。其中n表示第 n个位置到字符串结束位置之间的字符。
  • String.startsWith(str,n)表示在字符串头部是否找到了str,找到返回true,找不到返回false。其中n表示第 n个位置到字符串结束位置之间的字符串头部
  • String.endsWith(str,n)表示在字符串尾部是否找到了str,找到返回true,找不到返回false。其中n表示字符串开始位置到第n-1个位置之间的字符串尾部

在ES6中怎么补全字符串

参考答案
  • String.padStart(n,str)用于字符串头部补全,其中n表示str补全后的字符串的最大长度,str表示用来补全的字符串。
    • 如果将str填充到字符串头部后,字符串长度还达不到n,则会复制str填充到字符串头部,直到字符串长度达到n。 'x'.padStart(4,'ab')//abax
    • 如果将str填充到字符串头部后,字符串长度超过n,则会先把str从头部开始截取长度为n减去要补全字符串长度,再填充到字符串头部。 '12'.padStart(10,'YYYY-MM-DD')//YYYY-MM-12
    • 如果字符串长度已经大于n,则不补全直接返回字符串。
    • 如果str不传,则会以空格来填充补全。
  • String.padEnd(n,str)用于字符串尾部补全,其中n表示str补全后的字符串的最大长度,str表示用来补全的字符串。
    • 如果将str填充到字符串尾部后,字符串长度还达不到n,则会复制str填充到字符串尾部,直到字符串长度达到n。 'x'.padEnd(4,'ab')//xaba
    • 如果将str填充到字符串尾部后,字符串长度超过n,则会先把str从头部开始截取长度为n减去要补全字符串长度,再填充到字符串尾部。 'x'.padEnd(4,'abcde')//xabc
    • 如果字符串长度已经大于n,则不补全直接返回字符串。
    • 如果str不传,则会以空格来填充补全。

模板字符串是什么,举个例子来说。

参考答案
const a = '我是变量';
let str = `${a},我是常量`;
console.log(str)//我是变量,我是常量

四、数值篇

Number.isFinite()有什么作用?

参考答案

用来检查这个数值是否为有限的,例子:

Number.isFinite(15);//true
Number.isFinite(NaN);//false
Number.isFinite('a');//false
Number.isFinite(true);//false
Number.isFinite(Infinity);//false

Number.isNaN()有什么作用?

参考答案

用来检查一个值是否为NaN,例子:

Number.isNaN(NaN);//true
Number.isNaN(true);//false
Number.isNaN('a');//false
Number.isNaN(58/NaN);//true
Number.isNaN('a'/NaN);//true
Number.isNaN('a'/0);//true

Number.isInteger()有什么作用?

参考答案

用来判断一个值是否为整数,值不会自动转换成Number类型。例子:

Number.isInteger('255')//false
Number.isInteger(3)//true
Number.isInteger(3.0)//true

Number.parseInt()有什么作用?

参考答案

解析一个字符串直到达小数点或者数字的末端为止,并返回一个整数,如果字符串的第一个字符不能被转换为数字,那么会返回 NaN。例子:

Number.parseInt('0225D2.25')//225
Number.parseInt('0225.25')//225
Number.parseInt('D225.25')//NaN

Number.parseFloat()有什么作用?

参考答案

解析一个字符串直到达数字的末端第二个小数点为止,并返回一个浮点数,如果字符串的第一个字符不能被转换为数字,那么会返回 NaN。例子:

Number.parseFloat('0225.25D25')//225.25
Number.parseFloat('0225.2.25')//225.2
Number.parseFloat('D225.25')//NaN

Math.trunc()有什么作用?

参考答案

用于去除一个值的小数部分,返回整数部分,会将不是Number类型的值先转成Number类型,对于空值和无法截取整数的值将返回 NaN。

Math.trunc(4.1);//4
Math.trunc('4.9');//4
Math.trunc(-4.1);//-4
Math.trunc('-4.9');//-4
Math.trunc('-0.9');//-0
Math.trunc('as');//NaN
Math.trunc(true);//1
Math.trunc(false);//0
Math.trunc();//NaN
Math.trunc('');//0

用ES5怎么实现Math.trunc()的功能?

参考答案
Math.trunc = function(x) {
    return x < 0 ? Math.ceil(x) : Math.floor(x);
}

Math.sign()有什么作用?

参考答案

用来判断一个数到底是正数、负数、零和其它,对于非数值,会先将其转换为数值。

若是正数返回+1,若是负数返回-1,若是0返回0,若是-0返回-0,其他返回NaN。

Math.sign('2');//1
Math.sign(-2);//-1
Math.sign(0);//0
Math.sign(-0);//-0
Math.sign('a');//NaN
Math.sign();//NaN
Math.sign(NaN);//NaN

用ES5怎么实现Math.sign()的功能?

参考答案
Math.sign = function(x) {
	x = +x;
	if (x === 0 || isNaN(x)) {
		return x
	}
	return x > 0 ? 1 : -1;
}

五、函数篇

下面代码执行结果是?

function foo(x,y='World'){
    console.log(x,y)
}
foo('Hello');
foo('Hello','');
foo('Hello',null)
foo('Hello','China');
参考答案
'Hello World'
'Hello'
'Hello null'
'Hello China'

下面代码执行结果是?

function foo({x,y = 'World'}) {
    console.log(x, y)
}
foo({x: 'Hello'});
foo({x: 'Hello',y: ''});
foo({x: 'Hello',y: 'China'});
foo()
参考答案
'Hello World'
'Hello'
'Hello China'
'Cannot destructure property `x` of 'undefined' or 'null'.'

上题代码中foo()为什么报错,怎么解决该报错

参考答案 因为`foo()`参数不是对象,所以解构时候,取null的x属性肯定会报错。可以按下面代码改写 ``` function foo({x,y = 'World'}={}) { console.log(x, y) } foo()//undefined "World" ```

下面代码执行结果是?

function foo(x,y){
    console.log(x,y)
}
console.log(foo.length)
参考答案

2,函数的length属性将返回参数个数

下面代码执行结果是?

function foo(x,y=1){
    console.log(x,y)
}
console.log(foo.length)
参考答案

1,函数的length属性将返回没有指定默认值的参数个数

下面代码执行结果是?

let x = 1;
function foo(x, y = x) {
    console.log(y)
}
foo(2)
参考答案

2。

因为函数参数一旦设置默认值,函数进行声明初始化时,参数会形成一个单独的作用域,直到初始化结束后才会消失。且函数调用时,函数体内的局部变量影响不到默认变量。

所以在这个作用域内,默认变量值x指向第一个参数x,而不是指向全局变量x。

下面代码执行结果是?

let x = 1;
function foo(y = x) {
    let x = 2 ;
    console.log(y)
}
foo()
参考答案

1。

因为函数参数一旦设置默认值,函数进行声明初始化时,参数会形成一个单独的作用域,直到初始化结束后才会消失。且函数调用时,函数体内的局部变量影响不到默认变量。

所以在这个作用域内,默认变量值x没有定义,所以指向了全局变量x。

下面代码执行结果是?

function foo(y = x) {
    let x = 2 ;
    console.log(y)
}
foo()
参考答案

x is not defined

因为函数参数一旦设置默认值,函数进行声明初始化时,参数会形成一个单独的作用域,直到初始化结束后才会消失,且函数调用时,函数体内的局部变量影响不到默认变量。

所以在这个作用域内,默认变量值x没有定义,去全局也没找到变量x,所以报错。

下面代码执行结果是?

let x=1;
function foo(y = () => x) {
    let x = 2 ;
    console.log(y())
}
foo()
参考答案

1

因为函数参数一旦设置默认值,函数进行声明初始化时,参数会形成一个单独的作用域,直到初始化结束后才会消失,且函数调用时,函数体内的局部变量影响不到默认变量。

所以即使默认变量是个函数也遵循这个规则。

下面三段代码执行结果是?


var x = 1;
function foo(x,y = () => {x = 2;}) {
    var x = 3;
    y();
    console.log(x)
}
foo()
var x = 1;
function foo(x,y = () => {x = 2;}) {
    let x = 3;
    y();
    console.log(x)
}
foo()
var x = 1;
function foo(x,y = () => {x = 2;}) {
    x = 3;
    y();
    console.log(x)
}
foo()
参考答案

3、'已声明标识符“x”'、2

因为foo函数的参数形成了一个单独的作用域,在这个作用域内先声明了变量x,然后声明了变量y,变量y是个匿名函数,其内部的变量x指向用一个作用域的第一个参数x。在foo函数内部又声明了一个变量x。该变量x与第一参数x不是属于同一作用域,故它们不是同一个变量。在执行y()后,不会对foo函数内部变量x造成影响,所以console.log(x)会打印出来3。

如果把var x = 3改成let x = 3,因为let不能重复声明变量。所有就会报错。

如果把var x = 3改成x = 3,这是foo函数内部变量x也指向第一参数x。所有执行y(),x会被改成2,所以console.log(x)会打印出来2。

函数的rest参数是什么,使用时候要注意什么?

参考答案

可以用获取函数的多余参数,rest 参数搭配的变量是一个数组,该变量将多余的参数放入其中。

function add(x, ...params) {
    let sum = x;
    for (val in params) {
    	sum += params[val];
    }
    return sum
}
console.log(add(1, 2, 3, 4, 5)); //15

使用时,rest参数后不能再有参数了,同时函数的 length 属性不包括 rest 参数。

当函数参数使用默认值或解构赋值或扩展运算符时,函数内部能不能使用严格模式?为什么

参考答案

不能,这样规定的原因是,函数内部的严格模式同时适用于函数体和函数参数。但是,函数执行时,先执行函数参数,然后再执行函数体。这样就有一个不合理的地方:只有从函数体之中才能知道参数是否应该以严格模式执行,但是参数却应该先于函数体执行。

怎么获取一个具名函数的名称

参考答案
function foo(){}
console.log(foo.name)//foo

箭头函数的this是指向哪里

参考答案

函数体内的 this 对象就是定义时所在的对象,而不是使用时所在的对象。

箭头函数的this是指向哪里

参考答案

函数体内的 this 对象就是定义时所在的对象,而不是使用时所在的对象。

箭头函数可以作为构造函数吗

参考答案

不能

箭头函数可以使用arguments 对象吗

参考答案

不能,该对象在函数体内不存在。如果要用,可以用rest参数代替。

箭头函数可不可以使用 yield 命令

参考答案

不可以,箭头函数不能用作 Generator 函数

六、数组篇

怎么用数组扩展运算符合并数组

参考答案
let arr1 = [1,2,3];
let arr2 = [4,5,6];
let arr = [...arr1,...arr2];
console.log(arr)

怎么用数组扩展运算符求数组中最大值

参考答案
console.log(Math.max(...[1,2,3]))

为什么数组扩展运算符能把字符串转成真正的数组

参考答案
console.log([...'hello'])//["h", "e", "l", "l", "o"]

因为任何Iterator 接口的对象都可以用扩展运算符转为真正的数组。

Array.from()有什么作用,和数组扩展运算符有什么区别。

参考答案

Array.from()方法用于将两类对象转为真正的数组,第一类是类数组,第二类是可遍历(Iterator)对象。

而数组扩展运算符只能将可遍历(Iterator)对像,转成真正的数组。

//类数组一般含有length属性
let obj={
    '0':'a',
    '1':'b',
    length:3,
}
console.log(Array.from(obj))//["a", "b", undefined]
console.log(...obj)//Found non-callable @@iterator

Array.of()有什么作用,和Array()有什么区别。

参考答案

Array.of()方法用于将一组值转换为数组。比Array()稳定,不会因参数个数的不同会导致结果有差异。

Array.of(1,2,3); //[1,2,3]
Array(1,2,3); //[1,2,3]
Array.of(3); //[3]
Array(3); //[ , , ]
Array.of(); //[]
Array(); //[]

说说copyWithin()的用法

参考答案

在当前数组内部将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。 copyWithin(target,start,end),有三个参数

  • target(必选):从该位置开始替换数据。
  • start(可选):从该位置开始读取数据,默认为0。如果为负值,表示倒数。
  • end(可选):到该位置前停止读取数据,默认等于数组长度。如果为负值,表示倒数。

说说find()和findIndex的用法

参考答案

数组实例的find方法用于找出第一个符合条件的数组成员。其参数是一个回调函数,所有数组成员依次执行该回调函数。直到找出第一个返回值为 true 的成员,然后返回该成员,如果没有符合条件的成员,则返回 undefined。

回调函数有三个参数,分别是当前值、当前的位置、原数组。

数组实例的findIndex方法用于找出第一个符合条件的数组成员的位置。其参数是一个回调函数,所有数组成员依次执行该回调函数。直到找出第一个返回值为 true 的成员,然后返回该成员的位置,如果没有符合条件的成员,则返回 -1。

回调函数有三个参数,分别是当前值、当前的位置、原数组。