ES6高频面试题目整理

本篇文章是根据如下内容进行的总结javascript

一、https://segmentfault.com/a/1190000011344301html

二、http://www.bslxx.com/a/mianshiti/tiku/2017/1019/953.htmljava

三、http://www.bslxx.com/a/mianshiti/tiku/javascript/2017/1213/1505.htmlnode

 

前言jquery

自从ES6发布以来,就受到了广大开发者的欢迎。它的新特性解决了不少实际开发中的痛点,而且使得JavaScript逐步成为一门可以开发大型企业应用的编程语言,基于这种技术环境下,不少公司都将ES6视为开发的其中一个标准,所以在招聘人才的时候,也会对其进行ES6知识的考察。下面就来看看哪些ES6知识是咱们须要重点掌握的。es6

 

箭头函数须要注意的地方面试

*当要求动态上下文的时候,就不可以使用箭头函数,也就是this的固定化。ajax

一、在使用=>定义函数的时候,this的指向是定义时所在的对象,而不是使用时所在的对象;
二、不可以用做构造函数,这就是说,不可以使用new命令,不然就会抛出一个错误;
三、不可以使用arguments对象;
四、不能使用yield命令;编程

下面来看一道面试题,重点说明下第一个知识点:json

class Animal {
  constructor() {
    this.type = "animal";
  }
  say(val) {
    setTimeout(function () {
      console.log(this); //window
      console.log(this.type + " says " + val);
    }, 1000)
  }
}
var animal = new Animal();
animal.say("hi"); //undefined says hi
View Code

【拓展】

《JavaScript高级程序设计》第二版中,写到:“超时调用的代码都是在全局做用域中执行的,所以函数中this的值在非严格模式下指向window对象,在严格模式下是undefined”。也就是说在非严格模式下,setTimeout中所执行函数中的this,永远指向window!!

 

咱们再来看看箭头函数(=>)的状况:

class Animal {
  constructor() {
    this.type = "animal";
  }
  say(val) {
    setTimeout(() => {
      console.log(this); //Animal
      console.log(this.type + ' says ' + val);
    }, 1000)
  }
}
var animal = new Animal();
animal.say("hi"); //animal says hi
View Code

【特色】

  • 不须要function关键字来建立函数
  • 省略return关键字
  • 继承当前上下文的 this 关键字

 

 

let和const

 *let是更完美的var,不是全局变量,具备块级函数做用域,大多数状况不会发生变量提高。const定义常量值,不可以从新赋值,若是值是一个对象,能够改变对象里边的属性值。

一、let声明的变量具备块级做用域
二、let声明的变量不能经过window.变量名进行访问
三、形如for(let x..)的循环是每次迭代都为x建立新的绑定

下面是var带来的不合理场景

var arr = [];
for (var i = 0; i < 10; i++) {
  arr[i] = function () {
    console.log(i);
  }
}
arr[5]() //10,a[5]输出f(){console.log(i);},后面加个括号表明执行f()
View Code

在上述代码中,变量i是var声明的,在全局范围类都有效,因此用来计数的循环变量泄露为全局变量。因此每一次循环,新的i值都会覆盖旧值,致使最后输出都是10。

而若是对循环使用let语句的状况,那么每次迭代都是为x建立新的绑定代码以下:

var arr = [];
for (let i = 0; i < 10; i++) {
  arr[i] = function () {
    console.log(i);
  }
}
arr[5]() //5,a[5]输出f(){console.log(i);},后面加个括号表明执行f()
View Code

【拓展】

固然,除了这种方式让数组找中的各个元素分别是不一样的函数,咱们还能够采用ES5中的闭包和当即函数两种方法。

一、采用闭包

function showNum(i) {
  return function () {
    console.log(i)
  }
}
var a = []
for (var i = 0; i < 5; i++) {
  a[i] = showNum(i)(); //循环输出1,2,3,4
}
View Code

二、采用当即执行函数

var a = []
for (var i = 0; i < 5; i++) {
  a[i] = (function (i) {
    return function () {
      console.log(i)
    }
  })(i)
}
a[2](); //2
View Code

【面试】

把如下代码使用两种方法,依次输出0-9

var funcs = []
for (var i = 0; i < 10; i++) {
  funcs.push(function () {
    console.log(i)
  })
}
funcs.forEach(function (func) {
  func(); //输出十个10
})
View Code

方法一:使用当即执行函数

var funcs = []
for (var i = 0; i < 10; i++) {
  funcs.push((function (value) {
    return function () {
      console.log(value)
    }
  }(i)))
}
funcs.forEach(function (func) {
  func(); //依次输出0-9
})
View Code

方法二:使用闭包

function show(i) {
  return function () {
    console.log(i)
  }
}
var funcs = []
for (var i = 0; i < 10; i++) {
  funcs.push(show(i))
}
funcs.forEach(function (func) {
  func(); //0 1 2 3 4 5 6 7 8 9
})
View Code

方法三:使用let

var funcs = []
for (let i = 0; i < 10; i++) {
  funcs.push(function () {
    console.log(i)
  })
}
funcs.forEach(function (func) {
  func(); //依次输出0-9
})
View Code

其余知识点:forEach() 方法用于调用数组的每一个元素,并将元素传递给回调函数。戳这里查看参考文章

 

 

Set数据结构

*es6方法,Set自己是一个构造函数,它相似于数组,可是成员值都是惟一的。

const set = new Set([1,2,3,4,4])
console.log([...set] )// [1,2,3,4]
console.log(Array.from(new Set([2,3,3,5,6]))); //[2,3,5,6]
View Code

 

 

Class的讲解

*class语法相对原型、构造函数、继承更接近传统语法,它的写法可以让对象原型的写法更加清晰、面向对象编程的语法更加通俗
这是class的具体用法。

class Animal {
  constructor() {
    this.type = 'animal'
  }
  says(say) {
    console.log(this.type + 'says' + say)
  }
}
let animal = new Animal()
animal.says('hello') // animal says hello

class Cat extends Animal {
  constructor() {
    super()
    this.type = 'cat'
  }
}
let cat = new Cat()
cat.says('hello') // cat says hell
View Code

能够看出在使用extend的时候结构输出是cat says hello 而不是animal says hello。说明contructor内部定义的方法和属性是实例对象本身的,不能经过extends 进行继承。在class cat中出现了super(),这是什么呢?由于在ES6中,子类的构造函数必须含有super函数,super表示的是调用父类的构造函数,虽然是父类的构造函数,可是this指向的倒是cat。

更详细的参考文章

 

 

模板字符串

*就是这种形式${varible},在以往的时候咱们在链接字符串和变量的时候须要使用这种方式'string' + varible + 'string'可是有了模版语言后咱们可使用string${varible}string这种进行链接。基本用途有以下:

一、基本的字符串格式化,将表达式嵌入字符串中进行拼接,用${}来界定。

//es5 
var name = 'lux';
console.log('hello' + name);
//es6
const name = 'lux';
console.log(`hello ${name}`); //hello lux
View Code

二、在ES5时咱们经过反斜杠(\)来作多行字符串或者字符串一行行拼接,ES6反引号(``)直接搞定。

//ES5
var template = "hello \
world";
console.log(template); //hello world

//ES6
const template = `hello
world`;
console.log(template); //hello 空行 world
View Code

【拓展】

字符串的其余方法

// 1.includes:判断是否包含而后直接返回布尔值
let str = 'hahay'
console.log(str.includes('y')) // true

// 2.repeat: 获取字符串重复n次
let s = 'he'
console.log(s.repeat(3)) // 'hehehe'
View Code

 

 

重点“人物”:Promise!

概念:Promise是异步编程的一种解决方案,比传统的解决方案(回调函数和事件)更合合理、强大。所谓Promise,简单来讲就是一个容器,里面保存着某个将来才会结束的事件(一般是一个异步操做)的结果。从语法上说,Promies是一个对象,从它能够获取异步操做的消息。Promise提供统一的API,各类异步操做均可以用一样的方法进行处理。处理过程流程图:

【面试套路1】

手写一个promise

var promise = new Promise((resolve, reject) => {
  if (操做成功) {
    resolve(value)
  } else {
    reject(error)
  }
})
promise.then(function (value) {
  // success
}, function (value) {
  // failure
})
View Code

【面试套路2】

怎么解决回调函数里面回调另外一个函数,另外一个函数的参数须要依赖这个回调函数。须要被解决的代码以下:

$http.get(url).success(function (res) {
  if (success != undefined) {
    success(res);
  }
}).error(function (res) {
  if (error != undefined) {
    error(res);
  }
});

function success(data) {
  if( data.id != 0) {
    var url = "getdata/data?id=" + data.id + "";
    $http.get(url).success(function (res) {
      showData(res);
    }).error(function (res) {
      if (error != undefined) {
        error(res);
      }
    });
  }
}
View Code

【面试套路3】

如下代码依次输出的内容是?

setTimeout(function () {
  console.log(1)
}, 0);
new Promise(function executor(resolve) {
  console.log(2);
  for (var i = 0; i < 10000; i++) {
    i == 9999 && resolve();
  }
  console.log(3);
}).then(function () {
  console.log(4);
});
console.log(5);
View Code

 上述代码解析:

首先先碰到一个 setTimeout,因而会先设置一个定时,在定时结束后将传递这个函数放到任务队列里面,所以开始确定不会输出 1 。 

而后是一个 Promise,里面的函数是直接执行的,所以应该直接输出 2 3 。 

而后,Promise 的 then 应当会放到当前 tick 的最后,可是仍是在当前 tick 中。 

所以,应当先输出 5,而后再输出 4 , 最后在到下一个 tick,就是 1 。
View Code

【面试套路4】

jQuery的ajax返回的是promise对象吗?

jquery的ajax返回的是deferred对象,经过promise的resolve()方法将其转换为promise对象。

var jsPromise = Promise.resolve($.ajax('/whatever.json'));
View Code

【面试套路5】

 promise只有2个状态,成功和失败,怎么让一个函数不管成功仍是失败都能被调用?

使用promise.all()

Promise.all方法用于将多个Promise实例,包装成一个新的Promise实例。

Promise.all方法接受一个数组做为参数,数组里的元素都是Promise对象的实例,若是不是,就会先调用下面讲到的Promise.resolve方法,将参数转为Promise实例,再进一步处理。(Promise.all方法的参数能够不是数组,但必须具备Iterator接口,且返回的每一个成员都是Promise实例。)

示例:
var p =Promise.all([p1,p2,p3]);
p的状态由p一、p二、p3决定,分为两种状况。
当该数组里的全部Promise实例都进入Fulfilled状态:Promise.all**返回的实例才会变成Fulfilled状态。并将Promise实例数组的全部返回值组成一个数组,传递给Promise.all返回实例的回调函数**。

当该数组里的某个Promise实例都进入Rejected状态:Promise.all返回的实例会当即变成Rejected状态。并将第一个rejected的实例返回值传递给Promise.all返回实例的回调函数。
View Code

【面试套路6】

1、分析下列程序代码,得出运行结果,解释其缘由

const promise = new Promise((resolve, reject) => {
  console.log(1)
  resolve()
  console.log(2)
})
promise.then(() => {
  console.log(3)
})
console.log(4)
View Code

运行结果及缘由

运行结果:
1 2 4 3

缘由:
Promise 构造函数是同步执行的,promise.then 中的函数是异步执行的。
View Code

 

2、分析下列程序代码,得出运行结果,解释其缘由

const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('success')
  }, 1000)
})
const promise2 = promise1.then(() => {
  throw new Error('error!!!')
})

console.log('promise1', promise1)
console.log('promise2', promise2)

setTimeout(() => {
  console.log('promise1', promise1)
  console.log('promise2', promise2)
}, 2000)
View Code

运行结果及缘由

运行结果:
promise1 Promise { <pending> }
promise2 Promise { <pending> }
(node:50928) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: error!!!
(node:50928) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
promise1 Promise { 'success' }
promise2 Promise {
  <rejected> Error: error!!!
    at promise.then (...)
    at <anonymous> }


缘由:
promise 有 3 种状态:pending(进行中)、fulfilled(已完成,又称为Resolved) 或 rejected(已失败)。状态改变只能是 pending->fulfilled 或者 pending->rejected,状态一旦改变则不能再变。上面 promise2 并非 promise1,而是返回的一个新的 Promise 实例。
View Code

 

3、分析下列程序代码,得出运行结果,解释其缘由

const promise = new Promise((resolve, reject) => {
  resolve('success1')
  reject('error')
  resolve('success2')
})

promise
  .then((res) => {
    console.log('then: ', res)
  })
  .catch((err) => {
    console.log('catch: ', err)
  })
View Code

运行结果及缘由

运行结果:
then:success1

缘由:
构造函数中的 resolve 或 reject 只有第一次执行有效,屡次调用没有任何做用,呼应代码二结论:promise 状态一旦改变则不能再变。
View Code

 

4、分析下列程序代码,得出运行结果,解释其缘由

Promise.resolve(1)
  .then((res) => {
    console.log(res)
    return 2
  })
  .catch((err) => {
    return 3
  })
  .then((res) => {
    console.log(res)
  })
View Code

运行结果及缘由

运行结果:
1  2

缘由:
promise 能够链式调用。提起链式调用咱们一般会想到经过 return this 实现,不过 Promise 并非这样实现的。promise 每次调用 .then 或者 .catch 都会返回一个新的 promise,从而实现了链式调用。
View Code

 

5、分析下列程序代码,得出运行结果,解释其缘由

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log('once')
    resolve('success')
  }, 1000)
})

const start = Date.now()
promise.then((res) => {
  console.log(res, Date.now() - start)
})
promise.then((res) => {
  console.log(res, Date.now() - start)
})
View Code

运行结果及缘由

运行结果:
once
success 1001
success 1001

缘由:
promise 的 .then 或者 .catch 能够被调用屡次,但这里 Promise 构造函数只执行一次。或者说 promise 内部状态一经改变,而且有了一个值,那么后续每次调用 .then 或者 .catch 都会直接拿到该值。
View Code

 

6、分析下列程序代码,得出运行结果,解释其缘由

Promise.resolve()
  .then(() => {
    return new Error('error!!!')
  })
  .then((res) => {
    console.log('then: ', res)
  })
  .catch((err) => {
    console.log('catch: ', err)
  })
View Code

运行结果及缘由

运行结果
then: Error: error!!!
    at Promise.resolve.then (...)
    at ...

缘由
.then 或者 .catchreturn 一个 error 对象并不会抛出错误,因此不会被后续的 .catch 捕获,须要改为其中一种:
return Promise.reject(new Error('error!!!'))
throw new Error('error!!!')

由于返回任意一个非 promise 的值都会被包裹成 promise 对象,即 return new Error('error!!!') 等价于 return Promise.resolve(new Error('error!!!'))。
View Code

 

7、分析下列程序代码,得出运行结果,解释其缘由

const promise = Promise.resolve()
  .then(() => {
    return promise
  })
promise.catch(console.error)
View Code

运行结果及缘由

运行结果
TypeError: Chaining cycle detected for promise #<Promise>
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:188:7)
    at Function.Module.runMain (module.js:667:11)
    at startup (bootstrap_node.js:187:16)
    at bootstrap_node.js:607:3

缘由
.then 或 .catch 返回的值不能是 promise 自己,不然会形成死循环。
View Code

 

8、分析下列程序代码,得出运行结果,解释其缘由

Promise.resolve(1)
  .then(2)
  .then(Promise.resolve(3))
  .then(console.log)
View Code

运行结果及缘由

运行结果
1

缘由
.then 或者 .catch 的参数指望是函数,传入非函数则会发生值穿透。
View Code

 

9、分析下列程序代码,得出运行结果,解释其缘由

Promise.resolve()
  .then(function success (res) {
    throw new Error('error')
  }, function fail1 (e) {
    console.error('fail1: ', e)
  })
  .catch(function fail2 (e) {
    console.error('fail2: ', e)
  })
View Code

运行结果及缘由

运行结果
fail2: Error: error
    at success (...)
    at ...

缘由
.then 能够接收两个参数,第一个是处理成功的函数,第二个是处理错误的函数。.catch 是 .then 第二个参数的简便写法,可是它们用法上有一点须要注意:.then 的第二个处理错误的函数捕获不了第一个处理成功的函数抛出的错误,然后续的 .catch 能够捕获以前的错误。
View Code

 

10、分析下列程序代码,得出运行结果,解释其缘由

process.nextTick(() => {
  console.log('nextTick')
})
Promise.resolve()
  .then(() => {
    console.log('then')
  })
setImmediate(() => {
  console.log('setImmediate')
})
console.log('end')
View Code

运行结果及缘由

运行结果
end
nextTick
then
setImmediate

缘由
process.nextTick 和 promise.then 都属于 microtask,而 setImmediate 属于 macrotask,在事件循环的 check 阶段执行。事件循环的每一个阶段(macrotask)之间都会执行 microtask,事件循环的开始会先执行一次 microtask。
View Code
相关文章
相关标签/搜索