备战秋招,复习基础。如有错误,欢迎批评指正,共同进步!
变量声明
let 变量只在声明的块或子块中可用,不在全局对象上创造属性。不提升 → 出现暂存性死区 → 只要进入当前作用域,所要使用的变量就已经存在,但是不可获取,只有等到声明变量的那一行,才可以获取和使用变量
var 全局或整个封闭函数
const 块级作用域,且不能重新赋值 → 只读引用 → 但可以重新定义对象属性。const obj={"a":"b"}; → a只读b可写
do表达式,使获取块级作用域的返回值
let x = do {
let t = f();
t * t + 1;
};
扩展运算符
把数组变为参数序列
箭头函数
[1,2,3].map(function (x){
return x*x;
});
改写为
[1,2,3].map(x => x*x);
注意事项:
- 函数体内this对象使定义时所在对象,而不是使用时所在对象。
- 不可以当作构造函数,即,不可以使用new命令。
- 不可以使用arguments对象,可用rest参数代替。
- 不可以使用yield命令,不能用作Generator函数。
对象的扩展
_proto_属性
读取或设置当前对象的prototype对象 最好使用object.setPrototypeOf()或object.getPrototypeOf()
symbol
第七种数据类型 表示独一无二的值。 [symbol] → s=symbol();
symbol.for('现有参数') → 使用同一个symbol值,登记机制
Set和Map
Set
类似于数组,但所有成员值唯一,没有重复。
-
set转数组:
array = Array.from(set); -
数组转set:
set = new Set(Array)返回set -
去除数组重复成员:
[...new Set(Array)]返回数组
两个NaN是相等的
两个对象总是不相等的
操作方法:
| 方法 | 返回值 | 含义 |
|---|---|---|
| ---------- | 基础操作 | ---------- |
| set.size | 返回成员总数 | |
| add(a) | 返回set | 添加元素 |
| delete(a) | 返回布尔 | 是否删除成功 |
| has(a) | 返回布尔 | 是否存在该值 |
| clear() | ||
| ---------- | 遍历操作 | ---------- |
| keys() | 返回键名 | set没有键名! |
| values() | 返回键值 | 与keys行为一致 |
| entries() | 返回键值对 | 键名键值相同,如:['red','red'] |
| forEach(键值,键名[,集合]) | 没有返回值 | 使用回调函数遍历每个成员 |
| ---------- | 其他操作 | ---------- |
| for (let x of set) | 循环遍历 | |
| new Set[...a,...b] | 并集 | |
| new Set([...a].filter(x=>b.hax(x))) | 交集 | |
| new Set([...a].filter(x=>!b.hax(x))) | 差集 |
遍历器:set.prototype[symbol.iterator] === set.prototype.values
WeakSet
成员只能是对象。
弱引用,不可遍历!
方法:add() delete() has() → 没有size和foeEach!
Map
键值对集合。任何类型的值都可用当作键!
-
map转数组:
[...map]→[[a,1],[b,2]][...map.keys()]→[a,b][...map.values()]→[1,2] -
数组转map:
map = new Map(array) -
map转对象:如果map的所有键都是字符串,则可用转对象
function srMapToObj(strMap){
let obj = Object.create(null);
for (let [k,v] of strMap){
obj[k] = v;
}
return obj;
}
const myMap = new Map().set('yes',true).set('no',false);
strMapToObj(myMap); //{yes:true,no:false}
- 对象转map:
function objToStrMap(obj){
let strMap = new Map();
for (let k of Object.keys(obj)){
strMap.set(k,obj[k]);
}
return strMap;
}
objToStrMap({yes:true,no:false});
- map转json:
1. map键名都是字符串,转为对象json
function strMapToJson(strMap){
return JSON.stringify(strMapToObj(strMap));
}
2. map键名有非字符串,转为数组json
function mapToArrayJson(map){
return JSON.stringify([...map]);
}
let myMap = newMap().set(true,7).set({foo:3},['abc']);
mapToArrayJson(myMap); // '[[true,7],[{"foo":3},["abc"]]]'
- json转map
1. 所有键名都是字符串
function jsonToStrMap(jsonStr){
return objToStrMap(JSON.parse(jsonStr));
}
2. 整个json就是一个数字,且每个数字成员本身是一个具有2个成员的数组。是数组转json的逆操作。
function jsonToMap(jsonStr){
return new Map(JSON.parse(jsonStr));
}
jsonToMap('[[true,7],[{"foo":3},["abc"]]]') // Map {true =>7, Object {foo:3} => ['abc']}
| 方法 | 返回值 | 备注 |
|---|---|---|
| ---------- | 基础操作 | ---------- |
| map.size | 返回成员总数 | |
| map.set(key,value) | 返回map | key实际是与内存地址绑定的 |
| get(key) | 返回value | |
| has(key) | 返回布尔 | 是否存在该值 |
| delete(key) | ||
| clear() | ||
| ---------- | 遍历操作 | ---------- |
| keys() | 返回键名 | |
| values() | 返回键值 | |
| entries() | 返回所有成员 | |
| forEach() | 遍历Map的所有成员 |
Map的遍历顺序就是插入顺序
遍历器:map[symbol.iterator] === set.entries
Map本身没有map和filter操作(set有)
WeakMap
只接收对象作为键名(null除外)
对象不计入垃圾回收机制(用于键所对应的对象可能会消失的场景)
没有key() value() entries() size() clear() forEach()!
应用场景:以DOM节点作为键名
Proxy
修改某些操作的默认行为,即“元编程”。
Reflect
获得语言的内部方法/修改某些Object返回结果
Promise
资料参考:BAT前端经典面试问题:史上最最最详细的手写Promise教程
一个容器,保存着某个未来才会结束的事件。
- 对象状态不受外界影响
Pending进行中Fulfilled已成功Rejected已失败 - 一旦状态改变就不会再变,任何时候都可以得到这个结果。(事件:一旦错过就监听不到了)
var promise = new Promise(function(resolve,reject){
// some code...
if (异步操作成功) {
resolve(value);
}else{
reject(value);
}
});
用then方法指定回调函数,将在当前脚本所有同步任务执行完成后才会执行.then方法返回的是新的Promise实例,因此可以链式!
promise.then(function(value){...},function(value){...});
- 异步加载图片
function loadImageAsync(url){
return new Promise(function(resolve,reject){
var image = new Image();
image.onload = function(){
resolve(image);
};
image.onerror = function(){
reject(new Error('Could not load image at '+url));
};
image.src = url;
});
}
- 用Promise实现AJAX
var getJSON = function(url){
var promise = new Promise(function(resolve, reject){
var client = new XMLHttpRequest();
client.open("GET",url);
client.onreadystatechange = handler;
client.responseType = 'json';
client.setRequestHeader("Accept","application/json");
client.send();
function handler(){
if (this.readyState !==4){
return;
}
if (this.status === 200){
resolve(this.response);
}else{
reject(new Error(this.statusText));
}
};
});
return promise;
};
getJSON("/posts.json").then(function(json){
console.log('Contents:'+json);
}, function(error){
console.error('出错了',error);
});
Promise.all
将多个Promise实例包装成一个新的Promise实例。
var p = Promise.all([p1,p2,p3]);
状态:
- 只有p1 p2 p3的状态都Fulfilled,p才会Fulfilled。此时p1 p2 p3的返回值组成一个数组,传给p的回调函数。
- 只要p1 p2 p3有一个被Rejected,p就会Rejected。此时第一个被Rejected的实例的返回值会传递给p的回调函数。
手写实现
把所有要执行的函数都放进一个数组里面,然后数组以次以同步的形式一个一个执行,执行结束后能够接着执行then里面的东西。
Promise.all = arr => {
let aResult = []; //用于存放每次执行后返回结果
return new _Promise(function (resolve, reject) {
let i = 0;
next(); //开始逐次执行数组中的函数
function next() {
arr[i].then(function (res) {
aResult.push(res); //执行后返回的结果放入数组中
i++;
if (i == arr.length) { //如果函数数组中的函数都执行完,便把结果数组传给then
resolve(aResult);
} else {
next();
}
})
}
})
};
Promise.race()
状态:只要p1 p2 p3中有一个实例改变状态,P就会改变状态。率先改变状态的Promise实例的返回值传给P的回调参数。
Promise.race = function(promises){
return new Promise((resolve,reject)=>{
for(let i=0;i<promises.length;i++){
promises[i].then(resolve,reject)
};
})
}
Iterator遍历器
一种机制/接口。用途:
- 为各种数据结构提供一个统一的、简便的访问接口
- 使数据结构的成员能够按某种次序排列
- 供for...of循环
let iter = arr[Symbol.iterator]();
for...in循环读取键名 for...of循环读取键值
To be continue...
Generator
状态机/遍历对象生成函数
- function命令与函数名之间有一个*
- 函数体内部使用yield语句定义(暂停)状态(调用next方法时才执行)
- 返回一个指向内部状态的指针对象
yield*
后跟一个遍历器对象,调用另一个generator。或跟一个数组,表示遍历数组内部。
Generator函数
var f = yield readFile(); →异步两阶段的分界线
To be continue...
其他方法
Thunk函数:自动执行Generator函数的方法。把参数放到一个临时函数中,再把临时函数传入函数体。
CO模块:用于Generator函数的自动执行。
async函数:是Generator函数的语法糖。将 * 替换为async,yield替换为await。内置执行器 asyncReadFile(),返回Promise对象。
Class
类和模块的内部默认使用严格模式。
constructor方法:创建和初始化由class创建的对象。
创建子类:extends 但不能继承常规(非可构造)对象,应该 用setPrototypeOf();
调用超类(原型):super 子类的构造必须执行一次super();
Mix-ins:以超类作为输入,以继承该类的子类作为输出。
Class只是一个语法糖,让对象原型的写法更清晰。
Class Point {...}
Point === Point.prototype.constructor;//true
typeof(Point);//function
Module模块
CommonJS模块
ES6前只要使用的模块加载方案。是对象,输入时必须查找对象属性。
//CommonJS模块
let { stat, exists, readFile } = require('fs');
// 等价于
let _fs = require('fs');
let stat = _fs.stat;
let exist = _fs.exist;
let readFile = _fs.readFile;
实质是整体加载fs模块,即加载js的所有方法,生成一个对象,然后再从对象上读取方法。
运行时加载:只有运行时才能得到这个对象,无法在编译时静态优化。
ES6模块
不是对象,是通过export命令显式指定输出的代码,在通过import命令输入。
//ES6模块
import {stat, exists, readFile } from 'fs';
实质是从fs模块加载3个方法,而不加载其他方法。
编译时加载:ES6可以在编译时完成加载模块。但ES6模块本身无法被引用,因为不是对象。
自动采用严格模式!
顶层this指向undefined!
export命令和import命令
- export:规定模块的对外接口,使外部能够读取到模块内部的某个变量。
//写法1
export var m = 1;
//写法2
var m = 1;
export {m};
//写法3
var n = 1;
export {n as m};
//function 写法1
export function f(){};
//function 写法2
function f(){};
export {f};
export输出的接口与值是动态绑定的关系,即,通过该接口可以取到模块内部实时的值。
export命令可以出现在模块任何位置!
- import:其他JS文件通过import命令加载模块。
import {lastName as surname} from './profile';
//整体加载
import * as othername from './profile';
import命令具有提升效果!会提升到整个模块的头部并首先执行。
import会执行所加载的模块。
- 默认输出 export default
export default function test(){// 只能使用一次,一个模块只有一个默认输出
...
}
var a = 1;
export default a;
//以下错误!
export default var a = 1;
import customName from 'test';// 不需要大括号,只可能对应一个方法。名字任意。
ES6模块与CommonJS模块的差异
| 区别 | CommonJS | es6 |
|---|---|---|
| 加载原理 | 第一次加载模块就会执行整个模块,再次用到时,不会执行该模块,而是到缓存中取值。 | 不会缓存运行结果,动态的去被加载的模块中取值,并且变量总是绑定其所在模块。 |
| 输出 | 值的拷贝(模块中值的改变不会影响已经加载的值) | 值的引用(静态分析,动态引用,原来模块值改变会改变加载的值) |
| 加载方式 | 运行时加载(加载整个模块,即模块中的所有接口) | 编译时加载(只加载需要的接口) |
| this指向 | 指向当前模块 | 指向undefined |
| 循环加载 | 只输出已经执行的部分,还未执行的部分不会输出 | 遇到模块加载命令import时不会去执行模块,而是生成一个动态的只读引用,等到真正用到时再去模块中取值。只要引用存在,代码就能执行。 |