列举常用的ES6特性
1.let/const
2.箭头函数
3.类的支持
4.字符串模块
5.symbols
6.Promises
箭头函数需要注意哪些地方?
当要求动态上下文的时候,就不能够使用箭头函数,也就是this的固定化。
1.在使用=>定义函数的时候,this的指向是定义时所在的对象,而不是使用时所在的对象;
2.不能够用作构造函数,这就是说,不能够使用new命令,否则就会抛出一个错误。
3.不能够使用arguments对象;
4.不能使用yield命令
let/const/var
var:作用域限制在其声明位置的上下文中,而非声明是全局的变量,声明总是在代码执行之前处理的,等效于在代码开头声明
let:非全局变量/块级函数作用域/块级作用域/不能用window.变量名访问/for(let x)循环都为x创建新的绑定/约束了变量提升/不允许重复声明同一个变量
const:定义的常量值/不能够重新赋值/如果值是对象,可以改变里边的属性值/变量声明必须初始化
var方式定义的变量有什么样的bug?
js没有块级作用域,在Js函数中的var声明,其作用域是函数体的全部
// var
for(var i=0;i<10;i++){
var a = 'a';
}
console.log(a,i); //a 10
// let
for(let i=0;i<10;i++){
let a = 'a';
}
console.log(a,i); //a is not defined
Set数据结构
ES6中的Set方法本身是一个构造函数,它类似于数组,但是成员的值都是唯一的
数组去重的方法
// ES6 set方法
var arr = new Set([1,2,2,3,4]);
console.log([...arr]); //(4) [1, 2, 3, 4]
// 以往去重方法
var arr = [1,1,2,2,3,4];
//创建一个空数组用于接收不重复内容的数组
var new_arr = [ ];
for(var i = 0;i<arr.length;i++){
if(new_arr.indexOf(arr[i])==-1){ //判断arr[i]在新数组中是否存在,不存在则push到数组中
new_arr.push(arr[i])
}
}
console.log(new_arr); //(4) [1, 2, 3, 4]
箭头函数this的指向
非箭头函数:this指向调用其所在的函数对象,离谁近就指向谁(此对于常规对象,原型链,getter&setter等都适用)
构造函数:this与被创建的新对象绑定;
DOM事件:this指向触发事件的元素;
内联事件:bind绑定,call&apply等方法改变this指向等。而有时this也会指向window
所以ES6的箭头函数,修复了原有的this指向问题。
手写ES6 class继承
//定义一个类
class Children{
constructor(skin,language){
this.skin = skin;
this.language = language;
}
say(){
console.log("I'm a Person")
}
}
class American extends Children{
constructor(skin,language){
super(skin,language)
}
aboutMe(){
console.log(this.skin+" "+this.language)
}
}
var m = new American("张三","中文");
m.say();
m.aboutMe();
子类没有constructor
子类American继承父类Person,子类没用定义constructor则默认添加一个,
在constructor中调用super函数,相当于调用父类的构造函数。
调用super函数是为了在子类中获取父类的this,
调用之后this指向子类。也就是父类prototype.constructor.call(this)
子类有constructor
子类必须在constructor方法中调用super方法,否则new实例时会报错。
子类没有自己的this对象,而是继承父类的this对象。如果不调用super函数,
子类就得不到this对象。super()作为父类的构造函数,只能出现在子类的constructor()中,
但是super指向父类的原型对象,可以调用父类的属性和方法。
generator生成器函数
Generator(生成器)是ES6标准引入的新数据类型
Generator看上去像是一个函数,但可以返回多次。
Generator的声明方式类似一般的函数声明,只是多了个*号,可以在函数内看到yield关键字。
调用generator对象有两个方法,
// 1.不断的调用generator对象的next()方法,
// 2.直接用for...of循环迭代generator对象。
每调用一次next,则执行一次yield语句,并在该处暂停。
return完成后退出生成器函数,后续如果还有yield操作就不再执行了。
function * showWords(){
yield "one";
yield "two";
return 'three'
}
var show = showWords();
console.log(show.next()); //{value: "one", done: false}
console.log(show.next()); //{value: "two", done: false}
console.log(show.next()); //{value: "three", done: true}
console.log(show.next()); //{value: "undefined", done: true}
// yield 和 yield*
//yield
function * showWords(){
yield "one";
yield showNumber();
return 'three';
}
// yield *
function * showWords2(){
yield "one";
yield* showNumber(2,3);
return 'three';
}
function * showNumber(a,b){
yield a+b;
yield a*b;
}
var show = showWords();
console.log(show.next()); //{value: "one", done: false}
console.log(show.next()); //{value: "showNumber", done: false}
console.log(show.next()); //{value: "three", done: true}
var show2 = showWords2();
console.log(show2.next()); //{value: "one", done: false}
console.log(show2.next()); //{value: 5, done: false}
console.log(show2.next()); //{value: 6, done: true}
console.log(show2.next()); //{value: "three", done: true}
async函数的基本用法
async函数返回一个Promise对象,可以使用then方法添加回调函数。当函数执行的时候,
一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。
函数前面的async关键字,表明该函数内部有异步操作。调用该函数时,会立即返回一个Promise对象。
由于async函数返回的是Promise对象,可以作为await命令的参数。
ES6 async函数有多种使用形式:
//函数声明
async function foo(){}
//函数表达式
const foo = async function(){}
//对象的方法
let obj = {async foo()}
obj.foo().then(...)
//class方法
class Storage{
constructor(){
this.cachePromise = cache.open("avatars")
}
async getAvatar(name){
const cache = await this.cachePromise;
return cache.match(`/avatars/${name}.jpg`)
}
}
async与generator的区别
async函数是Generator函数的语法糖,将Generator函数的星号替换成async,将yield替换成await。
async函数对Generator函数的改进,体现在以下四点:
1、内置执行器
2、语义性:async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果
3、适用性:await命令后面可以是Promise对象和原始类型的值(数字、字符串和布尔值,但这时等同于同步操作)
4、返回值:async函数返回值Promise,Generator函数返回值Iterator,可以用then指定下一步操作。
async函数是多个异步操作包装成的Promise对象,而await命令就是内部then命令的语法糖
简单实现async/await中的async函数
// async函数的实现原理,就是将Generator函数和自动执行器,包装在一个函数里
function spawn(genF) {
return new Promise(function (resolve, reject) {
const gen = genF();
function step(nextF) {
let next;
//try/catch/finally 语句用于处理代码中可能出现的错误信息。
try {
next = nextF()
} catch (e) {
return reject(e);
}
if (next.done) {
return resolve(next.value);
}
Promise.resolve(next.value).then(
function (v) {
step(function () {
return gen.next(v)
});
},
function (v) {
step(function () {
return gen.throw(e)
});
}
)
}
step(function () {
return gen.next(undefined);
});
})
}
有用过promise吗?请写出下列代码的执行结果
setTimeout(() => {
console.log(1);
}, 0);
new Promise((resolve) => {
console.log(2);
for (var i = 1; i < 200; i++) {
i = 198 && resolve();
}
console.log(3);
}).then(() => {
console.log(4);
});
console.log(5);
/*首先要讲一下,js是单线程执行,那么代码的执行就有先后;
有先后,那就要有规则(排队),大体分两种:同步、异步;
同步很好理解,就不用多说了(我就是老大,你们都要给我让路);
异步(定时器[setTimeout/setInterval]、事件、ajax、promise等),
异步又要细分宏任务、微任务两种机制
宏任务:js异步执行过程中遇到宏任务,就先执行宏任务,将宏任务加入执行的队列,然后再去执行微任务;
微任务:js异步执行过程中遇到微任务,也会将任务加入执行的队列,真执行的时候是先微任务里拿对应回调函数,
然后才轮到宏任务的队列回调执行的;
Object.is()与原来的比较操作符===,==的区别?
== 相等运算符,比较时会自动进行数据类型转换
===严格相等运算符,比较时不进行隐式类型转换
Object.is同值相等算法,在===基础上对0和NaN特别处理
+0 === -0 //true
NaN === NaN // false
Object.is(+0, -0) // false
Object.is(NaN, NaN) // true
介绍一下Set、Map、WeakSet和WeakMap的区别?
Set: 成员不能重复,只有键值,没有键名,可以遍历,方法有add,delete,has
WeakSet:成员都是对象,成员都是弱引用,可以用来保存DOM节点,不容易造成内存泄漏
不能遍历,方法有add,delete ,has
Map: 本质上是键值对的集合,类似集合,可以遍历,方法很多,可以跟各种数据格式转换
WeakMap:只接受对象作为键名(null除外),不接受其他类型的值作为键名,键名所指向的对象,
不计入垃圾回收机制,不能遍历,方法有get、set、has、delete
ES5的继承和ES6的继承有什么区别
ES5继承是先创建子类的实例对象, 然后再将父类的方法添加到this上(Parent.apply(this))
ES6继承是先创建父类的实例对象this(必须先调用父类的super方法), 然后用子类的构造函数修改this
ES6通过class关键字定义类里面有构造方法, 类之间通过extends关键字实现继承。
子类必须在constructor中调用super方法,否则新建实例报错。
因为子类没有自己的this对象,而是继承了父类的this对象, 然后对其进行加工。
如果不调用super方法,子类得不到this对象
super关键字指代父类的实例,即父类的this对象。在子类构造函数中,
调用super后,才可使用this关键字,否则报错
ES6新特性详细介绍说明
1.变量声明:const和let
let声明局部变量,比var(无论声明在何处,都会被视为声明在函数的最顶部),而let不会将声明变量提前;
let表示声明变量,而const表示声明常量,两者都为块级作用域;
const声明的变量都会被认为是常量,它的值被设置完成不能再修改。
注意:let关键词声明的变量不具备变量提升的特性
const和let声明只在最近的一个块级作用域中有效
const在声明时必须被赋值
2.箭头函数
箭头函数是函数的一种简写形式,使用括号包裹参数,跟随一个=>,紧接着是函数体
三个特点:
不需要function关键字来创造
省略return关键字
修复了this指向
3.类和继承
class和extend是一种语法糖,也是基于原型继承实现的
class和super calls,实例化,静态方法和构造函数
//class声明类 内部直接是属性和方法 不用,分隔。 constructor
class Person {
constructor(name, age) {
this.name = name; //实例属性
this.age = age;
console.log(name, age)
}
sayhi(name) {
console.log(`this name is ${this.name}`);
//以往的写法
console.log('my age is ' + this.age)
}
}
class Programmer extends Person {
constructor(name, age) {
//直接调用父类结构器进行初始化
super(name, age)
}
program() {
console.log("I'm coding...")
}
}
var anim = new Person("张三", 18);
anim.sayhi();
var wayou = new Programmer("李四", 20);
wayou.sayhi();
wayou.program();
4.字符串模板
// ES6中允许使用反引号` 来创建字符串,此方法创建的字符串里面可以包含由${ }包裹的变量
var num = Math.random();
console.log(`输出随机数${num}`);
5.增强的对象字面量
对象字面量被增强了,写法更加简洁与玲姐,同时在定义对象的时候能够做的事情更多了。
1.可以在对象子里面量里面定义原型
2.定义方法可以不用function关键词
3.直接调用父类方法
var human = {
breathe() {
console.log('breathing...');
}
};
var worker = {
__proto__: human, //设置此对象的原型为human,想党羽继承human
company: 'freelancer',
work() {
console.log("working..")
}
}
human.breathe();
//调用继承来的breathe方法
worker.breathe();
6.解构:
自动解析数组或对象中的值。若一个函数要函数要返回多个值,常规的做法是返回一个对象,
将每个值作为这个对象的属性返回。在ES6中,利用解构这一特性,可以直接返回一个数组,
然后数组中的值会自动被解析到对应接收该值得变量中。
var [x,y] = getVal(), //函数返回值解析
[name, ,age] = ["wayou","male","secrect"]; //数组解析
function getVal(){
return [1,2];
}
console.log(`x:${x},y:${y}`); //x:1,y:2
console.log(`name:${name},age:${age}`); //name:wayou,age:secrect
7.参数默认值,不定参数,拓展参数
默认参数值:可以在定义函数的时候指定参数的默认值,而不用通过逻辑或操作符来达到目的。
//传统方式设置默认方式
function sayHello(name){
var name = name||'dude';
console.log("Hello "+name);
}
sayHello(); //Hello dude
sayHello("wayou"); //Hello wayou
//ES6的默认参数
function sayHello2(name = "dude"){
console.log("Hello "+name)
}
sayHello2(); //Hello dude
sayHello2("wayou"); //Hello wayou
不定参数(拓展符):
不定参数是在函数中使用命名参数同时接收不定数量的未命名参数。以前可以通过arguments变量来达到。
不定参数的格式是三个句点后跟代表所有不定参数的变量名。
比如下面这个例子中,...x代表了所有传入add函数的参数。
//将所有参数想加的函数
function add(...x){
return x.reduce((m,n)=>m+n);
}
//传递任意个数的参数
console.log(add(1,2,3)); //输出:6
console.log(add(1,2,3,4,5)); //输出:15
reduce()方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。
reduce()可以作为一个高阶函数,用于函数的compose
注意:reduce()对于空数组是不会执行回调函数的
拓展符:将一个数组转为用逗号分隔的参数序列。(若数组为空不产生任何效果)
var x = [1,2,3,4,5,6];
console.log(x); //(6) [1, 2, 3, 4, 5, 6]
console.log(...x); //1 2 3 4 5 6
拓展参数:
拓展参数则是另一种形式的语法糖,它允许传递数组或类数组直接作为函数的参数而不用通过apply。
var people = ['wayou','john','sherlock'];
//sayHello函数来接收三个单独的参数
function sayHello(people1,people2,people3){
console.log(`Hello ${people1},${people2},${people3}`)
}
//以前的方式,如果需要传递数组当参数,我们需要使用函数apply方法
sayHello.apply(null,people); //Hello wayou,john,sherlock
sayHello(...people); //Hello wayou,john,sherlock
8.for of值遍历
for in循环用于遍历数组,类数组或对象,ES6中新引入的for of循环功能相似,
不同的是每次循环他提供的不是序号而是值
var someArray = ['a','b','c'];
for(v of someArray){
console.log(v); //a,b,c
}
9.iterator/generator:
iterator:拥有一个next方法的对象,返回一个对象{done,value},包含两个属性,布尔类型的done和任意值的value。
iterable:拥有一个obj[iterator]方法的对象,这个方法返回一个iterator
generator:特殊的iterator,next方法可以接收一个参数,返回值取决于它的构造函数(generator function),同时拥有throw方法。
yield关键字:它可以暂停函数的执行,随后可以再进入函数继续执行
具体详情:https://blog.domenic.me/es6-iterators-generators-and-iterables/
10.模块:
将不同功能的代码分别写在不同文件中,各模块只需导出公共接口部分,然后通过模块的导入方式可以在其他地方使用。
//单独的js文件,如:point.js
module "point" {
export class Point {
constructor (x,y){
publice x = x;
publice y = y;
}
}
}
//在需要引用模块的js文件内
//声明引用的模块
module point from './point.js';
//这里可以看出,尽管声明了引用的模块,还是可以通过指定需要的部分进行导入
import Point from "point"
var origin = new Ponit(0,0);
console.log(origin)
11.Map、Set和WeakMap、WeakSet
这些是新加的集合类型,提供了更加方便的获取属性值的方法,不用像以前一样用hasOwnProperty来检查某个属性是属于原型链上的还是当前对象的。同时,在进行属性值添加与获取时有专门的get、set方法。
//Sets
var s = new Set();
s.add("hello").add("goodbye").add("hello");
s.size === 2;
s.has("hello")===true;
//Maps
var m = new Map();
m.set("hello",42);
m.set(s,34);
m.get(s) === 34;
我们会把对象作为一个对象的键来存放属性值,普通集合类型比如简单对象会阻止垃圾回收器对这些作为属性键存在的对象回收,偶造成内存泄露的危险。而weakMap,weakSet则更加安全些,这些作为属性键的对象如果没有别的变量在引用它们,则会被回收释放掉,具体还看下面的例子。
//weak Maps
var wm = new WeakMap();
wm.set(s,{eatra:42});
wm.size === undefined;
//weak Sets
var ws = new WeakSet();
ws.add({data:42}); //因为添加到ws的这个临时对象没有其他变量引用它,所以ws不会保存它的值,也就是说这次添加其实没有意思
12.Proxies
proxy可以监听对象身上发生了什么事情,并在这些事情发生后执行一些相应的操作。一下子让我们对一个对象有了很强的跟踪能力,同时咋数据绑定方面也很有用处。
//定义被侦听的目标对象
var engineer = {name:"Join",salary:50};
//定义处理程序
var interceptor = {
set:function(receiver,property,value){
console.log(property,"is changed to",value);
receiver[property] = value;
}
}
//创建代理以进行侦听
engineer = new Proxy(engineer,interceptor);
//做一些改动来触发代理
engineer.salary = 60; //控制台输出:salary is changed to 60
上面代码,我们已经添加了注释,这里进一步解释。对于处理程序,是在被侦听的对象身上发生了相应事件之后,
处理程序里面的方法会被调用,上面例子中我们设置了set的处理函数,表明。如果我们侦听对象的属性被更改,
也就是被set了,那这个处理程序就会被调用,同时通过参数能够的值是哪个属性被更改,更改为什么值
13.symbols
symbols是一种基本类型,它不是一个对象。symbols通过调用symbol函数产生,接收一个可选的名字参数,
该函数返回的symbol是唯一的。之后就可以用这个返回值作为对象的键了。symbol还可以用来创建私有属性,
外部无法直接访问偶symbol作为键的属性值。
var MyClass = (function() {
// module scoped symbol
var key = Symbol("key");
function MyClass(privateData) {
this[key] = privateData;
}
MyClass.prototype = {
doStuff: function() {
this[key]
}
};
return MyClass;
})();
var c = new MyClass("hello")
console.log(c["key"] === undefined); //true,无法访问该属性,因为是私有的
14.Math、Number、String、Object的新API
对Math、Number、String还有Object等添加了许多新的API。
15.Promises
Promise是处理异步操作的一种模式,之前在很多三方库中有实现,比如JQuery的deferred对象。
当你发起一个异步请求,并绑定了.when()/.done()等事件处理程序时,其实就是在应用promise模式。
//创建Promise
var promise = new Promise(function(resolve,reject){
//进行一些异步或耗时操作
if(/*如果成功*/){
resolve("stuff worked")
}else{
reject(Error("It broke"));
}
});
//绑定处理程序
promise.then(function(result){
//promise成功的话会执行这里
console.log(result); //"stuff worked"
},function(err){
//promise处理失败会执行这里
console.log(err); //Error:"It broke"
});