摘要:常用知识总结之前总结了中的一些知识点。在年正式发布了,简称,又称为。作为构造函数的语法糖,同时有属性和属性,因此同时存在两条继承链。子类的属性,表示构造函数的继承,总是指向父类。
ES6常用知识总结
之前总结了es5中js的一些知识点。这段时间看了石川blue老师讲解的es6课程,结合阮一峰老师的es6教程,随手做了一些笔记和总结分享给大家。内容还是es6主要的知识点,基本没有什么创新点,主要是石川blue对里面一些难懂的知识点分析的挺好的,对我个人理解es6很有帮助,也希望对大家能有所帮助。
石川blue老师es6讲解视频百度云地址:https://pan.baidu.com/s/1qZpUeni 密码:2m9t
es6 是什么首先弄明白ECMA和js的关系。ECMA是标准,Javascript是ECMA的实现。因为js也是一种语言,但凡语言都有一套标准,而ECMA就是javascript的标准。
在2015年正式发布了ECMAscript6.0,简称ES6,又称为ECMAscript2015。
在es6之前,定义变量都是使用var,但是var存在一些问题,比如可以重复声明,仅支持函数作用域问题。所以es6设计了let和const来弥补不足的地方,下面看一下let和const具备哪些特性
let
不能重复声明
块级作用域
可修改let变量的值
const
不可重复声明
块级作用域
不可修改const变量的值
2. 箭头函数箭头函数在写法上对es5做了一些修整,代码看起来更显得简洁
如果只有一个参数,圆括号"()"可以省略
函数体如果只有一句return语句,花括号也可以省略
// 定义一个箭头函数 let a = (arg)=>{ // 这里=>符号就相当于function关键字 return arg+=1 } // 也可以简写为 let a = arg => arg+=1
箭头函数也对this的指向做了修整
es6之前的函数的this指向调用函数时所在的对象,而箭头函数的this指向函数定义时所在的对象
// 普通函数 var obj = { say: function () { setTimeout(function() { console.log(this) }); } } // 箭头函数 var obj = { say: function () { setTimeout(() => { console.log(this) }); } } obj.say(); // obj3. 解构赋值
允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构。比如:
var [a,b] = [1,2] // a=1 b=2
解构赋值必须符合下面三条规则:
左右结构必须一样
右边必须是一个合法的数据
声明和赋值必须一句话完成,不能把声明与赋值分开
let [a, b] = [1, 2] // 左右都是数组,可以解构赋值 let {a, b} = {a:1, b:2} // 左右都是对象,可以解构赋值 let [obj, arr] = [{a:1}, [1, 2]] // 左右都是对象,可以解构赋值 let [a, b] = {a:1, b:2} // err 左右结构不一样,不可以解构赋值 let {a,b} = {1, 2} // err 右边不是一个合法的数据,不能解构赋值 let [a, b]; [a, b] = [1, 2] // err 声明与赋值分开,不能解构赋值4. 字符串
1.字符转模板
字符转模板使用反引号(``)来定义字符串,字符串模板支持断行,也可以在字符串嵌入变量,非常方便,可谓是前端的福利。
let name = "Jone" `hello ${name}, how are you `
2. startsWith(), endsWith(), includes()
startsWith() 表示参数字符串是否在原字符串的头部,返回布尔值
endsWith() 表示参数字符串是否在原字符串的尾部,返回布尔值
includes() 表示是否在原字符串找到了参数字符串,返回布尔值
这三个方法都支持第二个参数,表示开始搜索的位置。
let s = "Hello world!"; s.startsWith("world", 6) // true s.endsWith("Hello", 5) // true s.includes("Hello", 6) // false5. 数组
石川blue老师讲的是es6对数组扩展了4个方法:map、reduce、filter、forEach。我认为这四个方法好像在es5就有了(个人感觉,有可能不对),不管这四个方法是es5还是es6添加的方法了,大家对这几个函数应该都挺熟悉,就不多做介绍了,下面说一下es6对数组扩展的其他的新特性。
扩展运算符
扩展运算符(spread)是三个点(...),它可以将一个数组拆分为参数序列,也可以收集剩下的所有的参数或者数组元素。
// 将一个数组拆分为参数序列 let arr = [1,2] function add (a,b) { return a+b } add(...arr) // 收集剩下的所有的参数 function f(a, ...arr) { console.log(...arr) // 2 3 } f(1,2,3) // 用于数组复制 let arr1 = [1,2] let arr2 = [...arr1] // 或者let [...arr2] = arr16. 面向对象 class
ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。
先看如何定义一个class类:
class User { constructor(name) { // 构造器,相当于es5中的构造函数 this.name = name // 实例属性 } showName(){ // 定义类的方法,不能使用function关键字,不能使用逗号分隔 console.log(this.name) } } var foo = new User("foo")(1)constructor
es6中class类专用的构造器,相当于之前定义的构造函数,每个类都必须有constructor,如果没有则自动添加一个空的constructor构造器。
创建实例的时候自动执行constructor函数
constructor中的this指向实例,并且默认返回this(实例)
(2)class类的prototype其实class的基本类型就是函数(typeof User = "function"),既然是函数,那么就会有prototype属性。
类的所有方法都是定义在prototype上
class User { constructor() { // ... } toString() { // ... } toValue() { // ... } } User.toValue() // err User.toValue is not a function User.prototype.toValue() // 可以调用toValue方法 // 等同于 User.prototype = { constructor() {}, toString() {}, toValue() {}, };(3)类的实例
类的实例只能通过new来创建
除了静态方法,定义在类上的所有的方法都会被实例继承
除非定义在类的this对象上才是实例属性,否则都是定义在类的原型(prototype)上
//定义类 class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return "(" + this.x + ", " + this.y + ")"; } } var point = new Point(2, 3); point.toString() // (2, 3) point.hasOwnProperty("x") // true point.hasOwnProperty("y") // true point.hasOwnProperty("toString") // false point.__proto__.hasOwnProperty("toString") // true(4)静态方法
如果在类中定义一个方法的前面加上static关键字,就表示定义一个静态方法,静态方法不会被实例继承,但会被子类继承,所以不能通过实例使用静态方法,而是通过类直接调用。
class User { constructor(name){ this.name = name } static show(){ console.log("123") } } class VipUser extends User{} VipUser.show() // 123 User.show() // 123 var foo = new User("foo") foo.show() // foo.show is not a function(5)静态属性
class的静态属性指的是 Class 本身的属性,目前只能通过Class.propName定义静态属性
静态属性可以被子类继承,不会被实例继承
class User{} User.name = "foo" // 为class定义一个静态属性 class VipUser extends User{} console.log(VipUser.name) // foo var foo = new User() console.log(foo.name) // undefined(6)私有属性和私有方法
es6是不支持私有属性和私有方法,但是日常需求可能会用到私有属性和私有方法,所以目前有一些提案,不过只是提案,尚未支持。
7. 类的继承class通过extends关键字实现继承:
class User { constructor(name){ this.name = name } show(){...} } class VipUser extends User{ constructor(vipName){ // 子类的构造器 super(vipName) // 调用父类的constructor。相当于User.prototype.constructor.call(this,vipName) } showVip(){...} } var v = new VipUser("foo") // 创建实例 v instanceof VipUser // v是子类VipUser的实例 v instanceof User // v还是父类User的实例(1)super
super可以当做函数使用,也可以当做对象使用。
当做函数使用
super作为函数调用时,代表父类的构造函数,就是在子类的构造器中执行父类的constructor函数以获取父类的this对象,因为子类没有自己的this对象,所以ES6规定子类必须在constructor中执行一次super函数。super()函数只能在子类的constructor中执行,不能在其他地方执行。
虽然super代表父类的构造器,但是super()在执行时内部的this指向子类,所以super()就相当于User.prototype.constructor.call(this)。
当做对象使用
super可以作为对象调用父类的属性和方法,在子类的普通方法中,指向父类的原型对象(即User.prototype);在子类的静态方法中,指向父类。
class User { constructor(){ this.x = "hello" } show() { return 2; } } class VipUser extends User { constructor() { super(); console.log(super.show()); // 2 此时super指向User.prototype,相当于User.prototype.show() console.log(super.x) // undefined 无法访问实例属性 } } let vip = new VipUser();
由于super对象在普通函数中使用super指向User.prototype,所以super只能访问父类的原型上的方法,没法访问父类的实例属性和实例方法。
ES6规定如果在子类中使用super对象调用父类的方法时,方法内部的this指向子类
class User { constructor() { this.x = 1 } show() { return this.x; } } class VipUser extends User { constructor() { super(); this.x = 2 console.log(super.show()) // 2 此时show()方法内部的this指向子类,所以输出2,而不是1 } } let vip = new VipUser();
上述代码中虽然super.show()调用的是User.prototype.show(),但是由于通过super对象调用父类方法时,方法内部的this指向子类,所以super.show()相当于 super.show().call(this),也就是User.prototype.show().call(this)
在子类的静态方法中super对象指向父类,而不是父类的原型(User.prototype)。
class User { constructor() { this.x = 1 } static fn() { console.log("父类静态方法") } } class VipUser extends User { constructor() { super(); this.x = 2 } static childFn() { super.fn() // 相当于User.fn() } } VipUser.childFn()(2)类的prototype和__proto__属性
在es5中每一个对象都有__proto__属性,指向对应的构造函数的prototype属性。Class 作为构造函数的语法糖,同时有prototype属性和__proto__属性,因此同时存在两条继承链。
子类的__proto__属性,表示构造函数的继承,总是指向父类。
子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性。
class User { } class VipUser extends User { } VipUser.__proto__ === User // true VipUser.prototype.__proto__ === User.prototype // true(3)实例的__proto__属性
子类实例的__proto__属性指向子类的原型(子类的prototype),子类实例的__proto__属性的__proto__属性指向父类的原型(父类的prototype)
class User { } class VipUser extends User { } var vip = new VipUser() console.log(vip.__proto__ === VipUser.prototype) // true console.log(vip.__proto__.__proto__ === User.prototype) // true8. 对象 (1) 对象的扩展运算符 ...
对象的扩展运算符(...)可以把对象可枚举的属性拆分为键值对序列
用于对象拷贝
let obj1 = {a:1,b:2} let obj2 = {...obj1} console.log(obj2) // {a:1,b:2}
用于合并对象
let obj1 = {a:1,b:2} let obj2 = {c:3,d:4} let obj3 = {a:100, ...obj1, ...obj2, e:5, f:6} // {a: 1, b: 2, c: 3, d: 4, e: 5, f: 6} let obj4 = { ...obj1, ...obj2, e:5, f:6, a:100} // {a: 100, b: 2, c: 3, d: 4, e: 5, f: 6}
如果后面的属性和前面的属性key相同,则会覆盖前面的值
(2) Object.assignObject.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,拷贝(浅拷贝)到目标对象。
如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。
const target = { a: 1 }; const source1 = {a: 100, b: 2 }; const source2 = { c: 3 }; Object.assign(target, source1, source2); target // {a:100, b:2, c:3}
Object.assign拷贝的属性是有限制的,只拷贝源对象的自身属性(不拷贝继承属性),也不拷贝不可枚举的属性。
(3) Object.keys(),Object.values(),Object.entries()ES2017 引入了跟Object.keys、Object.values和Object.entries,作为遍历一个对象的补充手段,供for...of循环使用。
Object.keys() 返回一个数组,成员是参数对象所有可枚举的属性的键名
var obj = { foo: "bar", baz: 42 }; Object.keys(obj) // ["foo", "baz"]
Object.values() 返回一个数组,成员是参数对象所有可枚举属性的键值。
const obj = { 100: "a", 2: "b", 7: "c" }; Object.values(obj) // ["b", "c", "a"]
Object.entries() 返回一个数组,成员是参数对象所有可枚举属性的键值对数组。
const obj = { foo: "bar", baz: 42 }; Object.entries(obj) // [ ["foo", "bar"], ["baz", 42] ]9. Generator函数
Generator可以理解为生成器,和普通函数没多大区别,普通函数是只要开始执行,就一直执行到底,而Generator函数是中间可以停,搭配使用next函数继续执行,用石川blue老师的话说就是踹一脚走一步。
(1)定义一个Generator函数function * fn(){ alert("a") yield alert("b") } var f = fn() f.next() // a f.next() // b
直接调用Generator函数,是什么都不执行的,调用第一个next()才开始执行,一直执行到第一个yield停止,第二次调用next(),从第一个yield执行到第二个yield停止,依次类推
(2)yield和nwxtyield代表暂时暂停执行,next代表继续执行。
yield和next可以传参数,也可以有返回值
1 function * fn(arg){ 2 console.log(arg) 3 let x = yield 100 4 console.log(x) 5 return 200 6 } 7 8 var f = fn("hello") 9 let step1 = f.next(1) 10 console.log(step1) 11 let step2 = f.next(2) 12 // 输出结果为 // hello // {value: 100, done: false} // 2 // {value: 200, done: true}
先看第八行 var f = fn("hello"),可以给Generator第一次执行传参数"hello",等第一次调用next的时候就开始Generator第一次执行,这时arg的值就是"hello"
然后第九行let step1 = f.next(1),这里需要注意,第一次调用next(1)时传的参数是作废的,所以1不会被传入到Generator函数中去,因为Generator函数第一次执行传的参数是通过上面传arg的方式传的参数。所以调用next()开始Generator第一次执行(console.log(arg)),此时arg的值是‘hello’,所以首先打印‘hello’
然后第十行打印step1的返回值。其实next是有返回值的,返回值是一个对象,其中value属性的值就是第一个yield返回的值100,done代表Generator时候执行完毕。
然后第十一行let step2 = f.next(2),这里的next(2)的参数可以传到Generator函数中去,但是为什么把2传给了x,估计很多人都想不明白,下面借鉴石川blue老师画的一张图来解释一下:
看到黄色方框圈的let step1 = f.next(1),对应执行黄色曲线圈的那部分代码,红色方框圈的let step12= f.next(2)对应执行红色曲线圈的部分代码,不要管为什么,你只需要这么理解就ok了。所以第二次next(2)传的参数2就传给了x
然后再看第十二行console.log(step2),所以第二个next的返回值就是对象{value: 200, done: true},value表示最后Generator函数的返回值return 200,done表示Generator函数执行完毕。
(3) 适用场景这种Generator函数适用多个异步请求之间有逻辑分析的情况,比如有一个需求,先请求用户数据,根据用户数据的类型判断用户是普通用户还是VIP用户,然后再根据判断结果请求普通商品数据或者VIP商品数据。
// 借助runner脚本,runner脚本规定Generator函数执行完一个next之后自动执行下一个next runner(function() * (){ let userData = yield $.ajax(...) // 请求用户数据 if(userData.type === "vip") { let goods = yield $.ajax(...) // 请求vip商品数据 } else { let goods = yield $.ajax(...) // 请求普通商品数据 } })
使用Generator函数使得代码看起来更像同步代码,其实使用Promise同样可以实现这种效果,只不过得需要在then()函数中嵌套请求。
9. async awaitasync其实就是对Generator的封装,只不过async可以自动执行next()。
async function read () { let data1= await new Promise(resolve => { resolve("100") }) let data2 = await 200 return 300 }(1)async 返回值
async默认返回一个Promise,如果return不是一个Promise对象,就会被转为立即resolve的Promise,可以在then函数中获取返回值。
async必须等到里面所有的await执行完,async才开始return,返回的Promise状态才改变。除非遇到return和错误。
async function fn () { await 100 await 200 return 300 } fn().then(res => { console.log9(res) // 300 })(3)await
await也是默认返回Promise对象,如果await后面不是一个Promise对象,就会转为立即resolve的Promise
如果一个await后面的Promise如果为reject,那么整个async都会中断执行,后面的awiat都不会执行,并且抛出错误,可以在async的catch中捕获错误
async function f() { await Promise.reject("error"); await Promise.resolve("hello world"); // 不会执行 } f().then(res =>{ }).catch(err=>{ console.log(err) // error })
如果希望一个await失败,后面的继续执行,可以使用try...catch或者在await后面的Promise跟一个catch方法:
// try...catch async function f() { try { await Promise.reject("出错了"); } catch(e) { } return await Promise.resolve("hello world"); } f() .then(v => console.log(v)) // hello world // catch async function f() { await Promise.reject("出错了") .catch(e => console.log(e)); // 出错了 return await Promise.resolve("hello world"); } f() .then(v => console.log(v)) // hello world
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/51939.html
摘要:的翻译文档由的维护很多人说,阮老师已经有一本关于的书了入门,觉得看看这本书就足够了。前端的异步解决方案之和异步编程模式在前端开发过程中,显得越来越重要。为了让编程更美好,我们就需要引入来降低异步编程的复杂性。 JavaScript Promise 迷你书(中文版) 超详细介绍promise的gitbook,看完再不会promise...... 本书的目的是以目前还在制定中的ECMASc...
摘要:感觉对我这种没实习没工作的新手,虽然一些高级的功能暂时用不上,但是一些基础的知识还是为平时的开发提供了巨大的便利。学习告一段落,现在结合平时的开发,总结一些常用的知识。日常开发中,块级作用域中使用的变量,尽量使用或者声明。使用时,进行合并。 很早之前就学过TypeScript和ES6,后来做项目的时候零零散散用了些。这几天又系统地把ES6的知识看了一遍。感觉对我这种没实习没工作的新手,...
摘要:常用知识总结之前总结了中的一些知识点。在年正式发布了,简称,又称为。作为构造函数的语法糖,同时有属性和属性,因此同时存在两条继承链。子类的属性,表示构造函数的继承,总是指向父类。 ES6常用知识总结 之前总结了es5中js的一些知识点。这段时间看了石川blue老师讲解的es6课程,结合阮一峰老师的es6教程,随手做了一些笔记和总结分享给大家。内容还是es6主要的知识点,基本没有什么创新...
摘要:常用知识总结之前总结了中的一些知识点。在年正式发布了,简称,又称为。作为构造函数的语法糖,同时有属性和属性,因此同时存在两条继承链。子类的属性,表示构造函数的继承,总是指向父类。 ES6常用知识总结 之前总结了es5中js的一些知识点。这段时间看了石川blue老师讲解的es6课程,结合阮一峰老师的es6教程,随手做了一些笔记和总结分享给大家。内容还是es6主要的知识点,基本没有什么创新...
阅读 1922·2021-09-07 09:59
阅读 2494·2019-08-29 16:33
阅读 3591·2019-08-29 16:18
阅读 2808·2019-08-29 15:30
阅读 1633·2019-08-29 13:52
阅读 1999·2019-08-26 18:36
阅读 481·2019-08-26 12:19
阅读 657·2019-08-23 15:23