笔记 - ES6 - 学前浅析
第01节:初识ES6
● 兼容性:IE10+,Chrome,FireFox,NodeJS,移动端。
● 编译、转换:
1.在线转换:每次打开页面都需要转换;
2.提前编译:例如Babel。
● ES6主要的改进和新特性涵盖:
1.变量;
2.函数;
3.数组;
4.字符串;
5.面向对象;
6.Promise;
7.generator;
8.模块化;
……
第02节:变量——let和const
● 使用var声明变量的弊端:
1.可以重复声明;
2.无法限制修改;
3.没有块级作用域。
● 使用let和const声明的变量都不能重复声明,并且都支持块级作用域。例如:
let a = 10; let a = 100; alert(a);//报错,提示a重复定义;
块作用域示例:
if(true){ let a = 10; } alert(a);//报错,提示a未定义,证明无法访问到if语句中的a变量;
● 使用let和const声明的变量的区别是:let声明和定义的变量可以修改,而const声明的变量其实是常量,一旦定义便不能修改。例如:
const a = 10; a = 100; alert(a);//报错,提示不能修改;
第03节:函数——箭头函数
● 据说修正了this指向……基本用法:
window.onload = function () { alert('test'); }; //使用箭头函数简写后: window.onload = () => { alert('test'); }
● 记住规律就好办:
1.如果只有一个参数(没有不行,多了不行),()可以省。
2.如果只有一个返回值(没有不行,多了不行),{}可以省。
3.箭头函数简化过程示例:
let fn = function (a) { return a*2; }; //基本简写: let fn = (a) => { return a*2; }; //只有一个参数,()可以省: let fn = a => { return a*2; }; //只有一个返回值,{}可以省: let fn = a => a*2;
第04节:函数——参数
● 使用...来进行参数扩展或数组展开:
1.参数扩展可以收集剩余的参数,剩余参数(Rest Parameter)的名称可自定义,但位置必须是形参列表的最后一个。例如:
function test(a, b, ...args){ alert(a); alert(b); alert(args); } test(10, 15, 20, 25, 30, 35, 40);//先弹出对应形参a的10,再弹出对应形参b的15,最后弹出对应形参args的由剩余参数组成的数组。
2.数组展开,展开后的效果就相当于把数组内容填在相应位置。例如:
let arr1 = [1, 2, 3]; let arr2 = [4, 5, 6]; let arr3 = [...arr1, ...arr2];//其实就相当于把arr1和arr2的内容写在对应位置; alert(arr3);//1,2,3,4,5,6; function test(a, b, c) { alert(a + b + c); } test(...arr1);//6,就是把arr1数组的前三个元素分别对应给3个形参,满足3个形参位置外剩余的元素如果没有位置接收就会被舍弃;
3.参数扩展与数组展开结合使用示例:
function foo(...args) {//将所有传入的参数收集为一个数组; bar(...args);//将收集的参数数组展开并传入; } function bar(a, b) { alert(a + b); } foo(10, 15, 20, 25);//25,但已将所有参数收集为数组,只不过能使用的是前两位;
● 默认参数,非常简单,直接将参数默认值赋值给形参即可;当有实参传入时,使用传入值,没有实参传入时,使用默认值。例如:
function test(a, b = 10, c = 15){ console.log(a, b, c); } test(5);//5,10,15,实参只对应到第一个形参a,函数的其他参数便使用了默认值; test(5, 20, 30);//5,20,30,实参全部对应且值与默认值不同,便将参数默认值覆盖使用。
第05节:解构赋值
● 解构就是将一个结构拆开,解构赋值主要使用对象是数组和JSON对象。
● 解构赋值的使用条件:
1.赋值符左右的数据,结构必须相同。例如:
let [a, b, c] = [1, 2, 3];//左右都是数组; let {d, e, f} = {d:4, e:5, f:6};//左右都是JSON对象; console.log(a, b, c, d ,e, f);//1 2 3 4 5 6,正常的解构,并且赋值成功; let [json, [n1, n2, n3], str, num] = [{foo: 'bar', bar: 'foo'}, [1, 2, 3], 'ok', 100]; console.log(json, n1, n2, n3, str, num);//Object(bar: "foo" foo: "bar") 1 2 3 "ok" 100;只要等号两边结构相同并对应即可赋值成功;
错误的示例:
let [a, b] = {a:12, b: 24};//左右结构不同; console.log(a, b);//报错,无法赋值;
2.赋值符右边必须是合法数据。例如:
let{a, b} = {12, 24}//左右结构貌似是一样的,但右边的是JSON吗?并不是,没有键值对,数据非法; console.log(a, b);//报错,无法赋值;
3.声明和赋值必须同时完成。例如:
let [a, b]; [a, b] = [12, 24]; console.log(a, b);//报错,无法完成解构赋值。
第06节:数组
● 新增方法之map:称为映射,通俗来讲就是一个对一个。例如:
let arr1 = [1, 2, 3, 4]; let result1 = arr1.map(item => item * 3);//将数组内每个元素作为参数传入并分别乘以3,然后将对应的结果返回为一个数组; alert(result1);//3,6,9,12; let arr2 = [59, 90, 75, 88, 55]; let result2 = arr2.map(score => score >= 60 ? '及格' : '不及格');//将数组内每个元素作为参数传入并分别进行三目运算,然后将相应的结果返回为一个数组; alert(result2);//不及格,及格,及格,及格,不及格;
● 新增方法之reduce:称为汇总,通俗来讲就是一堆出来一个。
1.方法解析:
arr.reduce(function (temp, item, index){}); //从数组第1个元素开始,temp是上一轮temp与item之间的处理结果,第1轮temp为第1个元素,item表示当前轮次处理的元素,index表示item的下标,也可理解为轮次,最终返回最后一轮的结果。
2.使用reduce方法求数组平均值的示例:
let arr = [45, 235, 3115, 2345, 15]; let result = arr.reduce((temp, item, index) => { if (index !== arr.length - 1) {//如果不是最后一轮就继续累加; return temp + item; } else { return (temp + item) / arr.length;//如果是最后一轮便将最终累加值除以数组长度求出平均数; } }); alert(result);//1151;
● 新增方法之filter:称为过滤器,使用此方法可以筛选满足条件的数组元素并将这些元素返回为一个新数组。
1.使用filter方法过滤5的整数倍:
let arr = [43, 235, 3115, 2346, 15]; let result = arr.filter(item => { if (item % 5 === 0) {//满足被5整除的条件便返回,不满足的默认被丢弃; return true; } }); //可简写为let result = arr.filter(item => item % 5 === 0); alert(result);//235,3115,15;
2.使用filter方法过滤数组内JSON对象的示例:
let goods = [ {name: '手机', price: 3000}, {name: '相机', price: 5500}, {name: '飞机', price: 10000000}, {name: '电机', price: 12000}, {name: '扳机', price: 400} ]; let result = goods.filter(json => json.price > 5000); console.log(result);//Array(3)0: {name: "相机", price: 5500}1: {name: "飞机", price: 10000000}2: {name: "电机", price: 12000};
● 新增方法之forEach:称为迭代(循环)。例如:
let arr = [43, 235, 3115, 2346, 15]; arr.forEach((item, index) => {//参数index可选,就是元素下标,这个方法会遍历每一个数组元素; alert(index + ':' + item);//依次弹出下标和对应元素的值; });
第07节:字符串
● 新增方法之startsWith:此方法返回一个布尔值,如果字符串是以指定字符串开头,便返回true,反之则反。例如:
let str = 'https://www.baidu.com'; if (str.startsWith('http://')) { alert('普通网址'); } else if (str.startsWith('https://')) { alert('加密网址');//结果为加密网址; } else if (str.startsWith('git://')) { alert('git地址'); } else { alert('其他'); }
● 新增方法之endsWith:与startsWith类似,此方法判断的是字符串结尾。例如:
let str = 'abc.jpg'; if (str.endsWith('.txt')) { alert('文本文件'); } else if (str.endsWith('.mp3')) { alert('音乐文件'); } else if (str.endsWith('.jpg')) { alert('图片文件');//结果为图片文件; } else { alert('其他'); }
● 字符串模板,使用一对反单引号`将字符串模板括起来;可以使用${data}直接将所需数据填入字符串中,并且可以方便的换行。例如:
let title = '标题'; let content = '内容'; let str = ` <div> <h1>${title}</h1>//将变量的值直接调用显示到指定位置; <p>${content}</p> </div>`;
第08节:面向对象——基础
● 创建类的新模式:
1.新增class关键字,新增了constructor构造器,类和构造函数分开了;
2.方法直接在class中添加。
● 继承关键字:
1.extends:继承父类方法;
2.super:继承父类(超类)构造器;
3.原先的构造函数和继承的实现方法示例:
//创建People类的构造函数; function People(name, age) { this.name = name; this.age = age; } //创建People类的方法; People.prototype.showName = function () { alert(this.name); }; People.prototype.showAge = function () { alert(this.age); }; //创建Student类的构造函数; function Student(name, age, num) { People.call(this, name, age);//通过call方法继承People类的属性; this.num = num; } //创建Student类的方法; Student.prototype = new People();//通过将原型指向People类的实例继承方法; Student.prototype.constructor = Student; Student.prototype.showNum = function () { alert(this.num); }; //创建Student类的实例并执行其方法; var stu = new Student('lucy', 18, 10086); stu.showName(); stu.showAge(); stu.showNum();
4.在ES6中,使用新的方式实现上面的代码示例:
//创建People类; class People { //构造器; constructor(name, age) { this.name = name; this.age = age; } //添加方法; showName() { alert(this.name); } showAge() { alert(this.age); } } //创建Student类并指定父类为People类,此时已经继承父类拥有的方法; class Student extends People { //构造器; constructor(name, age, num) { super(name, age);//继承父类属性 this.num = num; } //添加方法; showNum() { alert(this.num); } } //创建Student类的实例并执行其方法; let stu = new Student('jim', 19, 10010); stu.showName(); stu.showAge(); stu.showNum();
第09节:面向对象——实例
● 使用React进行练习,了解React的两大特点:
1.组件化(或称模块化),即Class;
2.强依赖于JSX,即Babel(browser.js)。
● 在React中使用组件的基本方法:
class Item extends React.Component {//每个组件都继承自这个内置组件; constructor(...args) {//构造器的所有参数都收集起来; super(...args);//将收集的参数数组展开至父类继承,这样传入的属性只要父类有便会继承; }; render() {//每个组件必须有这个渲染方法,并且这个方法必须有可渲染的返回值; //return <span>test content</span>;//最简单的渲染标签; return <li>{this.props.str}</li>;//使用{}插入代码块,将需要的数据添加于此,例如这行代码中是将li的自定义属性值使用原型调用并添加到标签里; }; } class List extends React.Component {//利用上面的组件叠加实现一些功能; constructor(...args) { super(...args); }; render() { //获取自定义属性中的数组并映射为相应的组件,再添加到ul标签中,最后使用List组件渲染到页面; return <ul>{this.props.arr.map(elem => <Item str={elem}></Item>)}</ul>; }; } window.onload = function () { let oDiv = document.getElementById('div1'); ReactDOM.render( //在React中,使用字符串和表达式都可以传参,但只有使用表达式才能传字符串以外的数据; <List arr={['abc', '123', 'qwe', 'sna', 'jiu']></List>,//在这个方法中,将组件用标签的形式引用至此,便可在目标标签oDiv中显示; oDiv ); }
第10节:JSON对象
● 关于JSON对象转换的几种方法:
1.串行化:
let json = {a: 12, b: 15}; let str = JSON.stringify(json);//使用JSON类下的stringify方法使JSON对象字符串化; alert(str);//{"a":12,"b":15} //顺便记一下将字符串转换为URI组件编码的方法; let url = 'http://www.abc.com/path/user?data=' + encodeURIComponent(str); alert(url);//http://www.abc.com/path/user?data=%7B%22a%22%3A12%2C%22b%22%3A15%7D;
2.将字符串解析为JSON对象:
let str1 = '{a: 5, b: 15, c: "abc"}'; let str2 = '{"a": 5, "b": 15, "c": "abc"}'; //let json1 = JSON.parse(str1); //console.log(json1);//报错了,无法解析,因为判断str1不能解析为JSON对象,这是因为写法不规范; let json2 = JSON.parse(str2); console.log(json2);//正常打印出了JSON对象; //JSON对象的标准写法是:键值对上只能使用双引号,而且所有的键名(key)必须使用双引号括起来。又一个在JS中使用单引号比较好的理由……
● 关于JSON的简写:当键和值与引用数据一样时,可以只写键名;在JSON对象内部,方法可以像class中一样的方式简写。例如:
let a = 10; let b = 20; let json1 = { a: 10, b: 20, c: 30, show: function () { alert(this.c); } }; //上面的写法等价于: let json2 = { a, b, c: 30, show() { alert(this.c); } };
第11节:Promise对象
● 异步与同步的特点:
1.异步:各操作之间没有关系,可同时进行多个操作,但代码更复杂,写起来很麻烦;
2.同步:同时只能进行一个操作,但代码简单,好写。
● 使用Promise就是为了消除异步操作,用写同步的方式来写异步代码;但是它不适合用来处理带逻辑处理的异步请求。
1.基本用法:
let p = new Promise(function (resolve, reject) {//resolve代表请求成功执行的函数,reject表示请求失败执行的函数; //异步代码,这里使用了jQuery封装的AJAX; $.ajax({ url: 'data/json.txt', dataType: 'json', success(arr) { resolve(arr); }, error(err) { reject(err); } }); }); p.then(function (res) {//将返回的数据传入后使用; alert('成功'); console.log(res); }, function (err) {//将返回的错误信息传入后使用; alert('失败'); console.log(err); }); //封装创建Promise对象的函数大概如此: function createPromise({url, dataType}) {//将需要请求的文件地址和数据类型以JSON格式传入参数; return new Promise(function (resolve, reject) {//直接将文件请求实例返回出去; $.ajax({ url,//这里直接调用传入的文件地址即可; dataType,//数据类型也一样,直接调用; success(arr) { resolve(arr); }, error(err) { reject(err); } }); }); }
2.请求多个数据,1.8版本以上的jQuery中已经封装了创建Promise实例的函数,可以拿来创建Promise对象并使用Promise的all方法来进行文件的请求,例如:
Promise.all([//接收一个数组参数,就是文件请求列表,别忘了是一个数组,别把方括号丢了,不然死活出不来; $.ajax({url: 'data/arr.txt', dataType: 'json'}),//利用jQuery的AJAX请求返回的Promise对象进行传参; $.ajax({url: 'data/json.txt', dataType: 'json'}) ]).then(results => {//如果全部请求成功,则返回请求到的结果组成的数组; let [arr, json] = results;//可利用解构赋值将结果数组拆解,并分别存入指定变量以供使用; alert('成功了'); console.log(arr); console.log(json); }, err => {//如果至少有一个请求失败,便会转入此函数内,并将错误信息传入; alert('失败了'); console.log(err); });
3.在Promise中还有一个race方法,这个方法的作用是同时请求不同位置的相同数据,哪个数据最先响应并完成读取,便使用哪个数据,一般用的比较少。
第12节:Generator函数
● 和普通函数不同的是,Generator函数(生成器)可以指定暂停;通俗来讲它可以踹一脚走一步……基本用法:
function* show() {//特点是在function后加*号,也可以贴函数名写; alert('a'); yield;//需要在指定位置添加暂停执行命令; alert('b'); } //show();//生成器不能被执行,需要使用它返回的生成器对象来驱动; let genObj = show();//接收创建的生成器对象以供驱使; genObj.next();//踹一脚,执行第1个alert; genObj.next();//再踹一脚,执行第2个alert;
● 使用Generator函数,可在下步操作所需数据到位前暂停函数,等数据到位后再执行之后的操作,目的也是为了方便异步请求;其本质是将一个大函数分解为若干小函数。
● 在Generator函数中,yield既可以传参,也可以返回数据。例如:
function* test(num) {//为第一个yield之前的代码传参要在默认位置传入; alert(num); let a = yield;//这里的yield将接收执行它之后代码块的next方法传入的参数; alert(a); yield a += 1000;//这里的yield将值返回给它之前代码块对应的next方法; return 'ok';// } let genObj = test(123);//默认传参要在生成器对象之前; genObj.next();//执行第1块代码,弹出结果123; let b = genObj.next(456);//执行第2块代码,参数被第1个yield接收,并弹出结果456,将第2个yield的返回值接收赋值给b变量; console.log(b);//Object{done:false,value:1456},done是函数状态,值为false表示函数未执行完成,这里value的值则是yield发出的返回值; let c = genObj.next();//执行第3块代码,将return发出的返回值接收并赋值给c变量; console.log(c);//Object{done:true,value:'ok'},done是函数状态,值为true表示函数已执行完成,这里value的值则是return发出的返回值;
● 同是解决异步请求,Generator比Promise好的地方是:Generator配合runner组件更适合带逻辑操作的异步请求,而Promise更适合不做逻辑处理而一并读取的多个数据请求。
第13节:展望ES7和ES8
● 数组:
1.includes方法:检索数组内是否包含为指定数据的元素,包含则返回true,反之则反。例如:
let arr = [1, 2, 3, 4, 5, 6]; alert(arr.includes(3));//true; alert(arr.includes(9));//false;
2.for...of/in和keys、values、entries:
for...in循环的是数组的元素下标,JSON对象的key值,而for...of循环的是数组的元素值,但无法用于JSON对象,因为JSON对象不是可迭代对象;
arr.keys对应对象所有的下标;arr.values对应对象所有的元素;arr.entries对应对象的所有的实体(entry),即键值对实体(格式为key, value)。
● 幂:两个*号叠加就是求幂,例如:
//传统方法: alert(Math.pow(3, 8));//求3的8次方,6561; //新方法,源自Python: alert(4**4);//求4的4次方,256;
● 字符串填充方法padStart和padEnd:使用指定字符在字符串之前或之后填充,使字符串满足指定位数,不指定字符串便默认使用空格填充。例如:
let str1 = '1234'; let str2 = '5678'; alert(str1.padStart(10,'0'));//0000001234,从开头填充至10位,空余位使用0填充; alert(str2.padEnd(10,'0'));//5678000000,从末尾填充至10位,空余位使用0填充;
● 用async和await代替generator和yield:不再依赖runner组件(原生自带)就能实现原来generator配合组件的功能,而且能写成箭头函数;标准统一,更易用,性能更高。
完成。