本文是根据知乎 爱前端不爱恋爱提供的目录整理供大家学习,后将持续更新
1、声明 let、const
2、解构赋值
3、声明类与继承:class、extend
4、Promise的使用与实现
5、generator(异步编程、yield、next()、await 、async)
6、箭头函数this指向问题、拓展运算符
7、map和set有没有用过,如何实现一个数组去重,map数据结构有什么优点?
8、ES6怎么编译成ES5,css-loader原理,过程
9、使用es5实现es6的class
一、声明 let、const
let
声明的变量只在 let
命令所在的代码块内有效。
const
声明一个只读的常量,一旦声明,常量的值就不能改变。
var i =10;
for(var i = 0;i<5;i++){
console.log(i);
}
console.log(i);
var i =10;
for(let i = 0;i<5;i++){
console.log(i);
}
console.log(i);
可见let
定义的变量只在for
循环这一块级作用域内起作用。
let
只能声明一次 var
可以声明多次
const R = 3;
R = 4;
const
声明一个只读变量,声明之后不允许改变。意味着,一旦声明必须初始化,否则会报错。
let
、const
不存在变量提升,var
会变量提升:(变量提升是编译器自动将声明变量放到代码开始,也就是可以先使用再声明)
console.log(i);
let i =1;//报错
const i =1;//报错
var i =1;//1
ES6
明确规定,代码块内如果存在 let
或者 const
,代码块会对这些命令声明的变量从块的开始就形成一个封闭作用域。
二、解构赋值
解构赋值
是对赋值运算符
的扩展。
他是一种针对数组或者对象进行模式匹配,然后对其中的变量进行赋值。
在代码书写上简洁且易读,语义更加清晰明了;也方便了复杂对象中数据字段获取。
- 数组模型的解构(Array)
let [a, [[b], c]] = [1, [[2], 3]];
// a = 1
// b = 2
// c = 3
let [a, b, c, d, e] = 'hello';
// a = 'h'
// b = 'e'
// c = 'l'
// d = 'l'
// e = 'o'
- 对象模型的解构(Object)
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'
三、声明类与继承:class、extend
class(类)
作为对象的模板被引入,可以通过 class
关键字定义类。class
的本质是 function
。它可以看作一个语法糖,让对象原型的写法更加清晰、更像面向对象编程的语法。
// 匿名类
let Example = class {
constructor(a) {
this.a = a;
}
}
// 命名类
let Example = class Example {
constructor(a) {
this.a = a;
}
}
类定义不会被提升,这意味着,必须在访问前对类进行定义,否则就会报错。不可重复声明。类中方法不需要 function
关键字。方法间不能加分号。
class Example{
constructor(){
console.log('我是constructor');
}
}
new Example(); // 我是constructor
constructor
方法是类的默认方法,创建类的实例化对象时被调用。
class
的实例化必须通过 new
关键字。
extends
通过 extends
实现类的继承。
class Child extends Father { ... }
子类 constructor
方法中必须有 super
,且必须出现在 this
之前。
四、Promise的使用与实现
Promise
是异步编程的一种解决方案。
从语法上说,Promise
是一个对象,从它可以获取异步操作的消息。
特点:
Promise
异步操作有三种状态:pending
(进行中)、fulfilled
(已成功)和 rejected
(已失败)。除了异步操作的结果,任何其他操作都无法改变这个状态。
Promise 对象只有:从 pending
变为 fulfilled
和从 pending
变为 rejected
的状态改变。只要处于 fulfilled
和 rejected
,状态就不会再变了即 resolved
(已定型)。
const p1 = new Promise(function(resolve,reject){
resolve('success1');
resolve('success2');
});
const p2 = new Promise(function(resolve,reject){
resolve('success3');
reject('reject');
});
p1.then(function(value){
console.log(value); // success1
});
p2.then(function(value){
console.log(value); // success3
});
缺点:
无法取消 Promise
,一旦新建它就会立即执行,无法中途取消。
如果不设置回调函数,Promise
内部抛出的错误,不会反应到外部。
当处于 pending
状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
五、generator(异步编程、yield、next()、await 、async)
Generator
函数,可以通过 yield
关键字,把函数的执行流挂起,为改变执行流程提供了可能,从而为异步编程提供解决方案。
Generator
有两个区分于普通函数的部分:
- 一是在
function
后面,函数名之前有个*
; - 函数内部有
yield
表达式。
其中 *
用来表示函数为 Generator
函数,yield
用来定义函数内部的状态。
function* sendParameter(){
console.log("strat");
var x = yield '2';
console.log("one:" + x);
var y = yield '3';
console.log("two:" + y);
console.log("total:" + (x + y));
}
var sendp1 = sendParameter();
sendp1.next();
// strat
// {value: "2", done: false}
sendp1.next();
// one:undefined
// {value: "3", done: false}
sendp1.next();
// two:undefined
// total:NaN
// {value: undefined, done: true}
var sendp2 = sendParameter();
sendp2.next(10);
// strat
// {value: "2", done: false}
sendp2.next(20);
// one:20
// {value: "3", done: false}
sendp2.next(30);
// two:30
// total:50
// {value: undefined, done: true}
}
一般情况下,next
方法不传入参数的时候,yield 表达式的返回值是 undefined
。当 next
传入参数的时候,该参数会作为上一步yield
的返回值。
yield*
表达式表示 yield
返回一个遍历器对象,用于在 Generator
函数内部,调用另一个 Generator
函数。
async
async
是 ES7
才有的与异步操作有关的关键字,和 Promise
, Generator
有很大关联的。
async
函数返回一个 Promise
对象,可以使用 then
方法添加回调函数。
async function helloAsync(){
return "helloAsync";
}
console.log(helloAsync()) // Promise {<resolved>: "helloAsync"}
helloAsync().then(v=>{
console.log(v); // helloAsync
})
await
await
操作符用于等待一个 Promise
对象, 它只能在异步函数 async function
内部使用。
返回 Promise
对象的处理结果。如果等待的不是 Promise
对象,则返回该值本身。
如果一个 Promise
被传递给一个 await
操作符,await
将等待 Promise
正常处理完成并返回其处理结果。
function testAwait (x) {
return new Promise(resolve => {
setTimeout(() => {
resolve(x);
}, 2000);
});
}
async function helloAsync() {
var x = await testAwait ("hello world");
console.log(x);
}
helloAsync ();
// hello world
正常情况下,await
命令后面是一个 Promise
对象,它也可以跟其他值,如字符串,布尔值,数值以及普通函数。
await
针对所跟不同表达式的处理方式:
Promise
对象:await
会暂停执行,等待Promise
对象resolve
,然后恢复async
函数的执行并返回解析值。- 非
Promise
对象:直接返回对应的值。
六、箭头函数this指向问题、拓展运算符
箭头函数
提供了一种更加简洁的函数书写方式。不可以作为构造函数
,也就是不能使用 new
命令,否则会报错
var f = v => v;
//等价于
var f = function(a){
return a;
}
f(1); //1
当箭头函数
没有参数或者有多个参数,要用 ()
括起来。
当箭头函数
函数体有多行语句,用 {}
包裹起来,表示代码块,当只有一行语句,并且需要返回结果时,可以省略 {}
, 结果会自动返回。
当箭头函数
要返回对象的时候,为了区分于代码块,要用 ()
将对象包裹起来
// 报错
var f = (id,name) => {id: id, name: name};
f(6,2); // SyntaxError: Unexpected token :
// 不报错
var f = (id,name) => ({id: id, name: name});
f(6,2); // {id: 6, name: 2}
this
var func = () => {
// 箭头函数里面没有 this 对象,
// 此时的 this 是外层的 this 对象,即 Window
console.log(this)
}
func(55) // Window
var func = () => {
console.log(arguments)
}
func(55); // ReferenceError: arguments is not defined
箭头函数体中的 this
对象,是定义函数时的对象,而不是使用函数时的对象。
function fn(){
setTimeout(()=>{
// 定义时,this 绑定的是 fn 中的 this 对象
console.log(this.a);
},0)
}
var a = 20;
// fn 的 this 对象为 {a: 19}
fn.call({a: 18}); // 18
// 回调函数
var Person = {
'age': 18,
'sayHello': function () {
setTimeout(function () {
console.log(this.age);
});
}
};
var age = 20;
Person.sayHello(); // 20
var Person1 = {
'age': 18,
'sayHello': function () {
setTimeout(()=>{
console.log(this.age);
});
}
};
var age = 20;
Person1.sayHello(); // 18
拓展运算符
拓展运算符 ...
复制后面对象的参数给当前变量
var arr1 = [1,2];
var arr2 = [...arr1];
console.log(arr2);//[1,2]
七、map和set有没有用过,如何实现一个数组去重,map数据结构有什么优点?
Map 对象
Map
对象保存键值对。任何值(对象或者原始值) 都可以作为一个键或一个值。
Maps
和 Objects
的区别
- 一个
Object
的键只能是字符串或者Symbols
,但一个Map
的键可以是任意值。 Map
中的键值是有序的(FIFO 原则),而添加到对象中的键则不是。Map
的键值对个数可以从size
属性获取,而Object
的键值对个数只能手动计算。Object
都有自己的原型,原型链上的键名有可能和你自己在对象上的设置的键名产生冲突。
for…of
var myMap = new Map();
myMap.set(0, "zero");
myMap.set(1, "one");
// 将会显示两个 log。 一个是 "0 = zero" 另一个是 "1 = one"
for (var [key, value] of myMap) {
console.log(key + " = " + value);
}
for (var [key, value] of myMap.entries()) {
console.log(key + " = " + value);
}
/* 这个 entries 方法返回一个新的 Iterator 对象,它按插入顺序包含了 Map 对象中每个元素的 [key, value] 数组。 */
// 将会显示两个log。 一个是 "0" 另一个是 "1"
for (var key of myMap.keys()) {
console.log(key);
}
/* 这个 keys 方法返回一个新的 Iterator 对象, 它按插入顺序包含了 Map 对象中每个元素的键。 */
// 将会显示两个log。 一个是 "zero" 另一个是 "one"
for (var value of myMap.values()) {
console.log(value);
}
/* 这个 values 方法返回一个新的 Iterator 对象,它按插入顺序包含了 Map 对象中每个元素的值。 */
forEach()
var myMap = new Map();
myMap.set(0, "zero");
myMap.set(1, "one");
// 将会显示两个 logs。 一个是 "0 = zero" 另一个是 "1 = one"
myMap.forEach(function(value, key) {
console.log(key + " = " + value);
}, myMap)
Map 与 Array的转换
var kvArray = [["key1", "value1"], ["key2", "value2"]];
// Map 构造函数可以将一个 二维 键值对数组转换成一个 Map 对象
var myMap = new Map(kvArray);
// 使用 Array.from 函数可以将一个 Map 对象转换成一个二维键值对数组
var outArray = Array.from(myMap);
Set 对象
Set
对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。
Set
中的特殊值
Set
对象存储的值总是唯一的,所以需要判断两个值是否恒等。有几个特殊值需要特殊对待:
- +0 与 -0 在存储判断唯一性的时候是恒等的,所以不重复;
undefined
与undefined
是恒等的,所以不重复;NaN
与NaN
是不恒等的,但是在Set
中只能存一个,不重复。
let mySet = new Set();
mySet.add(1); // Set(1) {1}
mySet.add(5); // Set(2) {1, 5}
mySet.add(5); // Set(2) {1, 5} 这里体现了值的唯一性
mySet.add("some text");
// Set(3) {1, 5, "some text"} 这里体现了类型的多样性
var o = {a: 1, b: 2};
mySet.add(o);
mySet.add({a: 1, b: 2});
// Set(5) {1, 5, "some text", {…}, {…}}
// 这里体现了对象之间引用不同不恒等,即使值相同,Set 也能存储
类型转换
// Array 转 Set
var mySet = new Set([“value1”, “value2”, “value3”]);
// 用…操作符,将 Set 转 Array
var myArray = […mySet];
String
// String 转 Set
var mySet = new Set(‘hello’); // Set(4) {“h”, “e”, “l”, “o”}
// 注:Set 中 toString 方法是不能将 Set 转换成 String
数组去重
var mySet = new Set([1, 2, 3, 4, 4]);
[...mySet]; // [1, 2, 3, 4]
八、ES6怎么编译成ES5,css-loader原理,过程
Babel
是一个 JavaScript
编译器
Babel
是一个工具链,主要用于将 ECMAScript 2015+
版本的代码转换为向后兼容的 JavaScript
语法,以便能够运行在当前和旧版本的浏览器或其他环境中。
css-loader原理,过程
loader
用于对模块的源代码进行转换。loader
可以使你在 import
或"加载"模块时预处理文件。因此,loader
类似于其他构建工具中“任务(task)”,并提供了处理前端构建步骤的强大方法。
css-loader
是分析各个css
文件的关系并合并成一个css
。
Babel使用指南
关于es6及以上的js编译成es5