es6
class、module、generator/ promise/ async/ await、let/ const、解构赋值、块级作用域、函数默认参数、箭头函数、set数据结构、es6基础
1、class语法相对原型、构造函数、继承更接近传统语法,它的写法能够让对象原型的写法更加清晰、面向对象编程的语法更加通俗
class和普通构造函数有何区别? |
1、js构造函数
function MathHandle(x, y) { // 构造函数
this.x = x;
this.y = y;
}
MathHandle.prototype.add = function() { // 原型的一个扩展
return this.x + this.y;
}
var m = new MathHandle(1, 2); // new一个实例
console.log(m.add())
2、class基本语法
class MathHandler{ // MathHandler是一个构造函数,构造函数都有显式原型:prototype
constructor(x, y) { // 构造器,java,c#的语法,面向对象高级语言的语法
this.x = x;
this.y = y;
}
add() {
return this.x + this.y;
}
}
const m = new MathHandle(1, 2); // 实例,所有实例都会有一个隐式原型:__proto__
console.log(m.add());
typeof MathHandle // 'function'
MathHandle.prototype.constructor === MathHandle; // true
m.__proto__ === MathHandle.prototype; // true
3、语法糖
class本身就是一个语法糖
4、继承
(1)低级构造函数的原型,赋值成高级构造函数的实例
(2)
function Animal(){this.eat = function() {console.log('animal eat')}}
function Dog(){this.bark = function() {console.log('dog dark')}}
Dog.prototype = new Animal(); // 绑定原型,实现继承
var hashiqi = new Dog();
class的继承
class Animal() {
constructor(name) {
this.name = name;
}
eat() {
alert(this.name + 'eat);
}
}
class Dog extends Animal{
constructor(name) {
super(name); // 如果有class,extends,则要用super(),super是被继承的class的constructor
this.name = name;
}
eat() {
alert(this.name + 'eat);
}
}
module
common.js 和 es6 中模块引入的区别?[字节跳动] |
CommonJS 是一种模块规范,最初被应用于 Nodejs,成为 Nodejs 的模块规范。运行在浏览器端的 JavaScript 由于也缺少类似的规范,在 ES6 出来之前,前端也实现了一套相同的模块规范 (例如: AMD),用来对前端模块进行管理。自 ES6 起,引入了一套新的 ES6 Module 规范,在语言标准的层面上实现了模块功能,而且实现得相当简单,有望成为浏览器和服务器通用的模块解决方案。但目前浏览器对 ES6 Module 兼容还不太好,我们平时在 Webpack 中使用的 export 和 import,会经过 Babel 转换为 CommonJS 规范。在使用上的差别主要有:
CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
CommonJs 是单个值导出,ES6 Module可以导出多个
CommonJs 是动态语法可以写在判断里,ES6 Module 静态语法只能写在顶层
CommonJs 的 this 是当前模块,ES6 Module的 this 是 undefined
generator、promise、async/await
- generator
Generator原理? |
待完善
- promise
1、promise 有 3 种状态:pending(进行中)、fulfilled(已完成,又称为Resolved) 或 rejected(已失败)。状态改变只能是 pending->fulfilled 或者 pending->rejected,状态一旦改变则不能再变。
2、new promise实例,要return
3、new promise时要传入函数,函数有resolve、reject两个参数
4、成功时执行resolve(),失败时执行reject()
5、then监听结果
手写一个promise? |
var promise = new Promise((resolve, reject) => {
if (操作成功) {
resolve(value);
} else {
retject(error);
}
});
promise.then(function(value) {
// success
}, function(value) {
// failure
})
jQuery的ajax返回的是promise对象吗? |
jquery的ajax返回的是deferred对象,通过promise的resolve()方法将其转换为promise对象。
var jsPromise = Promise.resolve($.ajax('/whatever.json'));
分析下列程序代码,得出运行结果,解释其原因? |
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)
})
then:success1
构造函数中的 resolve 或 reject 只有第一次执行有效,多次调用没有任何作用。因为promise 状态一旦改变则不能再变。
分析下列程序代码,得出运行结果,解释其原因? |
Promise.resolve(1)
.then(2)
.then(Promise.resolve(3))
.then(console.log)
1
.then
或者.catch
的参数期望是函数,传入非函数则会发生值穿透。
分析下列程序代码,得出运行结果,解释其原因? |
Promise.resolve()
.then(() => {
return new Error('error!!!')
})
.then((res) => {
console.log('then: ', res)
})
.catch((err) => {
console.log('catch: ', err)
})
then: Error: error!!!
1、.then或者.catch中return一个error对象并不会抛出错误,所以不会被后续的.catch捕获,需要改成其中的一种:
return Promise.reject(new Error('error!!!'))
throw new Error('error!!!')
2、因为返回任意一个非promise的值都会被包裹成promise对象,即return new Error('error!!!')
等价于return Promise.resolve(new Error(‘error!!!’))
分析下列程序代码,得出运行结果,解释其原因? |
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)
})
fail2: Error: error
1、.then可以接收两个参数,第一个是处理成功的函数,第二个是处理错误的函数。.catch是.then第二个参数的简便写法。它们在用法上有一点需要注意:.then的第二个处理错误的函数捕获不了第一个处理成功的函数抛出的错误,而后续的.catch可以捕获之前的错误。
分析下列程序代码,得出运行结果,解释其原因? |
process.nextTick(() => {
console.log('nextTick')
})
Promise.resolve()
.then(() => {
console.log('then')
})
setImmediate(() => {
console.log('setImmediate')
})
console.log('end')
end
nextTick
then
setImmediate
1、process.nextTick 和 promise.then 都属于 microtask,而 setImmediate 属于 macrotask,在事件循环的 check 阶段执行。事件循环的每个阶段(macrotask)之间都会执行 microtask,事件循环的开始会先执行一次 microtask。
- async、await
1、then只是将callback拆分了。
2、async/await是最直接的同步写法
import 'babel-polyfill';
function load
const load = async function() {
const result1 = await loadImg(src1);
console.log(result1);
const result2 = swait loadImg(src2);
console.log(result2);
}
load();
①使用await,函数必须用async标识
②await后面跟的是一个promise实例
③需要babel-polyfill
generator, asynce/await的关系? |
待完善
1、es5有两种声明变量的方法:var, function
es6有6种声明变量的方法:var, function, let, const, import, class
let、var的区别? |
1、块级作用域:let定义的变量有块级作用域,var声明的变量只有全局和函数作用域。
2、变量提升:let不存在变量提升,var存在变量提升。
3、重复声明:let不允许重复声明,var可以重复声明。
4、暂时性死区:在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”。
var tmp = 123;
if (true) {
tmp = 'abc'; // 执行报错:Uncaught ReferenceError: tmp is not defined
let tmp;
}
隐蔽的死区
function bar(x = y, y = 2) {
return [x, y];
}
bar(); // 报错
请选择正确的一项? |
// A
let {foo} = {bar: 'bar'};
// B
let {foo: {bar}} = {bar: 'bar'};
// C
let {foo, bar} = {foo: 'aaa', bar: 'bbb'};
// D
let {foo: baz} = {foo: 'aaa', bar: 'bbb'};
B
1、B选项报错:Uncaught TypeError: Cannot destructure property 'bar' of 'undefined' or 'null'.
应改成:let {foo: {bar}} = {foo:{bar: 'bar'}};
;
1、在使用=>定义函数的时候,this的指向是定义时所在的对象,而不是使用时所在的对象;
2、不能够用作构造函数,这就是说,不能够使用new命令,否则会抛出一个错误。
3、不能够使用arguments对象。
4、不能使用yield命令。
5、当要求动态上下文的时候,就不能够使用箭头函数,也就是this的固定化。
写出下列的值? |
class Animal {
constructor() {
this.type = 'Animal';
}
say(val) {
setTimeout(function() {
console.log(this);
console.log(`${this.type} say ${val}`);
}, 200);
}
}
var animal = new Animal();
animal.say('hi');
window
undefined says hi
1、《javaScript高级程序设计》第二版中,写到“超时调用的代码都是在全局作用域中执行的,因此函数中this的值在非严格模式下指向window对象,在严格模式下是undefined。也就是说在非严格模式下,setTimeout中所执行函数中的this,永远指向window”。
2、箭头函数
class Animal {
constructor() {
this.type = 'Animal';
}
say(val){
setTimeout(() => {
console.log(this);
console.log(`${this.type} say ${val}`);
}, 200);
}
}
var animal = new Animal();
animal.say('hi');
(1)特点:
①不需要function 关键字来创建函数。
②省略return 关键字
箭头函数有两种格式
a.只包含一个表达式,可以省略{…} 和 return
const add = (a, b) => a + b;
b.包含多条语句,这时候就不能省略{…}和return
const anotherAdd = (a, b) => {return a + b}
③继承当前上下文的this关键字
1set本身是一个构造函数,它类似于数组,但是成员值都是唯一的。
for…of, for…in的区别? |
1、在循环对象属性的时候,使用for…in,在遍历数组的时候的时候使用for…of。
2、for…in循环出的是key,for…of循环出的是value
3、for…of是ES6新引入的特性。修复了ES5引入的for…in的不足
4、for…of不能循环普通的对象,需要通过和Object.keys()搭配使用
var student = {
name: 'Jack',
age: 27,
sex: 'female'
};
var arr = [1, 4, 'small'];
arr.school = 'zheda';
// 1、for...in 遍历对象,得到key
for (let key in student) {
console.log(key); // name,age,sex
}
//for (let i of student) { // 报错:student is not iterable(可迭代的)
// console.log(i);
//}
// 2、直接使用for...of报错,可以用Object.keys(obj);
for (let key of Object.keys(student)) {
// 用Object.keys()获取对象key的数组
console.log(key); // name,age,sex
}
// 3、for...in遍历数组,得到index + key
for (let i in arr) {
console.log(i); // 0,1,2,school
}
// 4、for...of遍历数组,得到value
for (let key of arr) {
console.log(key); // 1,4,small
}
总结一下es6其他常用功能? |
1、let、const /'kɑnst/
const定义常量,常量不能被重新赋值
2、多行字符串/模板变量
js拼接变量字符串模板
console.log(`输出:${name}`)
3、解构赋值
整体数组或对象中拿到其中一个元素或属性值
// obj
const obj = {a: 10, b: 20, c:30};
const {a, c} = obj;
console.log(a); // 10
console.log(c); // 30
// arr
const arr = ['xxx', 'yyy', 'zzz'];
const [x, y, z] = arr;
console.log(x); // xxx
console.log(z); // zzz
4、块级作用域
for循环
// js
var obj = {a: 100, b: 200};
for (var item in obj) {console.log(item)};
console.log(item); // 'b'
// es6
const obj = {a: 100, b: 200};
for (let item in obj) {console.log(item)};
console.log(item); // undefined
5、函数默认参数
// js
function(a, b) {
if(b == null) {b = 0;}
}
// es6
function (a, b=0) {
}
6、箭头函数
彻底解决了之前函数this指向全局window对象的问题
function fn() {
console.log('real', this); // {a: 100}
var arr = [1, 2, 3];
// 普通js
arr.map(function(item) {
console.log('js',this); // window
return item + 1;
});
// 箭头函数
arr.map(item => {
console.log('es6', this); // {a: 100}
return item + 1;
});
}
fn.call({a: 100})
babel原理? |
待完善
答:ast转换
[‘1’,‘2’,‘3’].map(parseInt) 输出什么,为什么? |
输出:[1, NaN, NaN]
首先让我们回顾一下,map函数的第一个参数callback:
var new_array = arr.map(function callback(currentValue[, index[, array]]) { // Return element for new_array }[, thisArg])
这个callback一共可以接收三个参数,其中第一个参数代表当前被处理的元素,而第二个参数代表该元素的索引。
而parseInt则是用来解析字符串的,使字符串成为指定基数的整数。
parseInt(string, radix)
接收两个参数,第一个表示被处理的值(字符串),第二个表示为解析时的基数。
了解这两个函数后,我们可以模拟一下运行情况
parseInt(‘1’, 0) //radix为0时,且string参数不以“0x”和“0”开头时,按照10为基数处理。这个时候返回1
parseInt(‘2’, 1) //基数为1(1进制)表示的数中,最大值小于2,所以无法解析,返回NaN
parseInt(‘3’, 2) //基数为2(2进制)表示的数中,最大值小于3,所以无法解析,返回NaN
map函数返回的是一个数组,所以最后结果为[1, NaN, NaN]