es6特性-第一部分
let
let关键字主要用来进行变量的声明。有以下注意的点:
-
变量名不能重复声明,防止变量被污染。var关键字可以
let star ='罗志祥'; let star ='小猪'; //执行后报错
-
let声明的变量只能在块级作用域(if、函数、for…)内有效。但不影响作用域链。
{let girl = "周扬青'; } console.log(girl); //报错{let school ='尚硅谷';function fn(){console.log(school);}fn();//这块不会报任何错误 }
-
不存在变量提升。必须使用let先声明变量,然后在使用。
console.log(song); let song ="恋爱达人'; //报错
以后声明变量都使用let关键字。
const
const使用这个关键字用来声明常量。有以下注意事项:
-
const声明的常量声明时必须赋值,否则报错。
-
const声明的常量的值不能修改。
-
一般常量名使用大写(潜规则)
-
块儿级作用域
-
对于数组和对象的元素修改,不算做对常量的修改,不会报错。
因为数组和对象的修改是对数组元素或者对象属性做修改,而数组和对象本身的地址不会发生变化。
//声明常量
const SCHOOL =‘尚硅谷';
const A; //报错:常量声明时必须赋值
const a = 100; //一般常量名大写
SCHOOL = 'ATGUIGU '; //报错:常量的值不能修改
{const PLAYER = ‘UZI';
}
console.log(PLAYER); //报错:块儿级作用域const TEAM = ['UZI','MXLG','Ming','Letme'];
TEAM.push('Meiko');//正常
变量的解构赋值
ES6允许按照一定模式从数组和对象中提取值,对变量进行赋值,这种的被称为解构赋值。
//数组解构赋值
const F4 = ['宋小宝', '小沈阳', '刘能', '赵四'];
let [song, xiao, liu, zhao] = F4; //注意:数组要对应的中括号
console.log(song);
console.log(xiao);
console.log(liu);
console.log(zhao);
//对象解构赋值
let wukong = {name: "悟空",age: 18,jineng: function () {console.log('72变');}
}
let { name, age, jineng } = wukong; //注意:对象要对应的大括号
console.log(name);
console.log(age);
console.log(jineng);
jineng();
模板字符串
之前字符串的声明方式是单引号’'或者双引号""。ES6引入新的声明字符串的方式``(反引号)
//1.声明
let str =`我也是一个字符串哦!`;
console.log(str, typeof str);
//2.内容中可以直接出现换行符.单引号和双引号不允许。
let str = `<ul><li>沈腾</li><li>玛丽</li><li>魏翔</li><li>艾伦</li></ul>`;
//3.变量拼接
let lovest = '沈腾';
let out = `${lovest}是我心目中最搞笑的演员!`; //注意这里必须是$(变量名)这种格式。
console.log(out);
简化对象写法
ES6允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。
let name ="尚硅谷';
let change = function(){console.log('我们可以改变你!!");
}
//完整写法
const school = { name:name,change:change,improve:function(){console.log("我们可以提高你的技能");}
}
//es6中简化写法
const school = { name,change,improve(){console.log("我们可以提高你的技能");}
}
//如果对象中的值对应的是字符串,而不是变量,那必须使用完整写法
const student = { name:"light", //这里如果不加引号,就会找light变量age:18,
}
箭头函数
ES6 允许使用=>
定义函数。省略function关键字。
//声明一个函数
let fn = () => {//...
}
let fn1 = (a, b) => {return a + b;
}
console.log(fn1(1, 2));
箭头函数注意事项
-
this是静态的,this始终指向函数声明时所在作用域下的 this的值。即使使用call方法调用。
function getName(){console.log(this.name); } let getName2 = ()=>{console.1og(this.name); } window.name ='尚硅谷'; const school = {name:"ATGUIGU" } //直接调用 getName(); //尚硅谷 getName2();//尚硅谷 //call方法调用 getName.call(school); //ATGUIGU getName2.call(school);//尚硅谷 this对象始终指向window对象
-
不能作为构造实例化对象,否则会报错
-
不能使用arguments 变量,否则会报错
箭头函数简写
-
当形参有且只有一个的时候,可以省略小括号。
let add = n => {return n + n; } console.log(add(9)); //18
-
当代码体只有一条语句的时候,此时花括号和return必须省略而且语句的执行结果就是函数的返回值。
let pow = n => n*n; console.log(pow(8));
总结
箭头函数适合与this 无关的回调。比如:定时器,数组的方法回调等。
箭头函数不适合与this有关的回调。比如:事件回调,对象的方法等。
函数参数的默认值
ES6允许给函数参数赋值初始值。
-
形参初始值具有默认值的参数,一般位置要靠后(潜规则)。
-
函数参数赋值初始值可以与解构赋值结合使用
//函数参数解构赋值 function connect({ host, username, passwd, other = 'no' }) {console.log(host, username, passwd, other); //baidu.com root 123456 no } connect({host: 'baidu.com',username: 'root',passwd: '123456' });
rest参数
ES6引入rest参数,用于获取函数的实参,用来代替arguments。
//ES5获取实参的方式
function date(){console.log(arguments); //打印出来是个对象
}
date('白芷','阿娇','思慧');
//rest参数
function date(...args){console.log(args); //打印出来是个数组
}
date('阿娇',"柏芝",'思慧");
rest参数是将参数以数组的方式存储,然后传入函数中。这样我们就可以通过数组api对rest参数进行处理。
注意:如果函数中有多个形参,rest参数必须放在最后,否则会报错。
function at(a, b, c, ...args) {console.log(a, b, c, args);
}
at(1, 2, 3, 4, 5, 6, 7, 8); //1 2 3 [4, 5, 6, 7, 8]
扩展运算符
...
扩展运算符能将『数组』转换为逗号分隔的『参数序列』
const tfboys = ['王俊凯', '易烊千玺', '王源'];
function chunwan() {console.log(arguments);
}
chunwan(tfboys); //对象
chunwan(...tfboys); //直接取数组
扩展运算符应用
- 数组的合并
const kuaizi = ['王太利', '肖央'];
const fhcq = ['玲花', '曾毅'];
//es5
var zuixuanxiaopingguo = kuaizi.concat(fhcq);
console.log(zuixuanxiaopingguo);// ['王太利', '肖央', '玲花', '曾毅']
//扩展运算符
//原理: 1.扩展运算符先把数组转化成逗号分隔的参数序列
// ...kuaizi => '王太利', '肖央' ...fhcq => '玲花', '曾毅'
// 2.组合数组
// zuixuanxiaopingguo = [...kuaizi, ...fhcq] => ['王太利', '肖央', '玲花', '曾毅']
zuixuanxiaopingguo = [...kuaizi, ...fhcq];
console.log(zuixuanxiaopingguo);// ['王太利', '肖央', '玲花', '曾毅']
- 数组克隆
const sanzhihua = ['E', 'G', 'M'];
const sanyecao = [...sanzhihua];
console.log(zuixuanxiaopingguo);//['E', 'G', 'M']
- 将伪数组转为真正的数组
const divs = document.querySelectorAll('div');
console.log(divs); //对象
const divArr = [...divs];
console.log(divArr); //转化为数组
Symbol
ES6引入了一种新的原始数据类型 Symbol,表示独一无二的值。它是JavaScript语言的第七种数据类型,是一种类似于字符串的数据类型。Symbol的初衷:解决对象的属性名冲突
Symbol的创建
//创建Symbol
let s = Symbol();
console.log(s, typeof s); //Symbol() "symbol"
let s2 = Symbol("张三"); //这块构造函数里面的字符串只是对Symbol数据的描述
let s3 = Symbol("张三");
//symbol.for创建
let s4 = Symbol.for("张三");
let s5 = Symbol.for("张三');
Symbol值通过Symbol函数生成。这就是说,对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的Symbol类型。凡是属性名属于Symbol类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。
Symbol特点
-
Symbol 的值是唯一的(es内部实现唯一性),用来解决命名冲突的问题。
-
Symbol值不能与其他数据进行运算
-
Symbol定义的对象属性不能使用
for...in
循环遍历,但是可以使Reflect.ownKeys
来获取对象的所有键名
Symbol的使用场景
在企业开发中如果需要对一些第三方的插件、框架进行自定义的时候可能会因为添加了同名的属性或者方法, 将框架中原有的属性或者方法覆盖掉为了避免这种情况的发生, 框架的作者或者我们就可以使用Symbol作为属性或者方法的名称。
//第一种添加方法
let bird = {name: '麻雀',fly: function () {console.log('我要飞得很高!')},eat: function () {console.log('该吃饭了!')}}
//给对象添加扩展的属性或者方法
let addMethod = {fly: Symbol(),eat: Symbol()
}
bird[addMethod.fly] = function () {console.log('请不要飞得太高');
}
bird[addMethod.eat] = function () {console.log('请不要吃的太饱');
}
//调用
bird.eat();
bird.fly();
bird[addMethod.eat]();
bird[addMethod.fly]();
//第二种添加方法
let youxi = {name: "狼人杀",[22]: function () {console.log('sfsdfsdaf');},[Symbol('say')]: function () { //相当于私有方法,不得被调用console.log("我可以发言");},[Symbol('zibao')]: function () { //相当于私有方法,不得被调用console.log("我可以自爆");}
}
console.log(youxi);
Symbol的内置值
除了定义自己使用的 Symbol值以外,ES6还提供了11个内置的Symbol值,指向语言内部使用的方法。
其实这些值都是使用Symbol的属性,而这些Symbol的属性可以作为对象的属性来使用。
class Person {static [Symbol.hasInstance](param) {console.log(param);console.log('我被调用了');return false;}
}
let swk = {name: '孙悟空',
}
console.log(swk instanceof Person);
迭代器
迭代器(lterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署lterator接口,就可以完成遍历操作。
- ES6创造了一种新的遍历命令
for...of
循环,lterator接口(这里的lterator接口,其实是指对象里面的一个属性,例如:Symbol(Symbol.iterator),只要有这个属性,就可以使用for...of
消费)主要供for...of
消费。
const xiyou = ['孙悟空', '猪八戒', '唐僧', '沙僧'];for (const item of xiyou) {console.log(item);}
-
原生具备iterator接口的数据(可用for of遍历)
Array、Arguments、Set、Map、String、TypedArray、NodeList。这些数据都具有
Symbol(Symbol.iterator)
属性
工作原理
-
创建一个指针对象,指向当前数据结构的起始位置
let iterator = xiyou[Symbol.iterator]();
-
第一次调用对象的next方法,指针自动指向数据结构的第一个成员
console.log(iterator.next()); //{value: '孙悟空', done: false} console.log(iterator.next()); //{value: '猪八戒', done: false} ...
-
接下来不断调用next方法,指针一直往后移动,值到指向最后一个成员
console.log(iterator.next()); //{value: '沙僧', done: false}
-
每调用next 方法返回一个包含value和 done属性的对象
{value: '沙僧', done: false}
迭代器主要用来自定义遍历对象
//自定义遍历对象
const ig = {name: 'ig',staff: ['theshy', 'ming', 'rookie', 'jacklove', 'ming'],[Symbol.iterator]() {let index = 0;let _this = this;return {next: function () {if (index < _this.staff.length) {index++;return { value: _this.staff[index], done: false };} else {return { value: undefined, done: true }}}};}
};
for (let v of ig) {console.log(v);
}
生成器
生成器函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同。
我们之前的异步编程是纯回调函数:nodefs、 ajax、mongodb
生成器函数声明时,需要在function 和函数名之间需要加 *
。yield
表示程序执行到这里会交出执行权,等待结果返回。它需要在协程Generator 函数中运行。
function * gen(){yield 'xxxx';//...yield 'xxxx';
}
当执行gen()
的时候,并不执行 generator 函数体,而是返回一个迭代器。迭代器具有next()
方法,每次调用 next() 方法,函数就执行到yield
语句的地方。next() 方法返回一个对象,其中value属性表示 yield 关键词后面表达式的值,done 属性表示是否遍历结束。generator 生成器通过next
和yield
的配合实现流程控制。
function* gen() {console.log(111);yield '一只没有耳朵';console.log(222);yield '一只没有尾部';console.log(333);yield '真奇怪';console.log(444);
}
let iterator = gen();
console.log(iterator.next()); //111 {value: '一只没有耳朵', done: false}
console.log(iterator.next()); //222 {value: '一只没有耳朵', done: false}
console.log(iterator.next()); //333 {value: '一只没有耳朵', done: false}
console.log(iterator.next()); //444 {value: undefined, done: true}for (const item of gen()) {console.log(item); //每次输出返回的{value: '一只没有耳朵', done: false}对象的value值并且执行yield之上的代码
}
生成器参数
function* gen1(arg) {console.log(arg);let one = yield 111;console.log(one);let two = yield 222;console.log(two);let three = yield 333;console.log(three);
}
//执行获取迭代器对象
let itor = gen1('AAA');
console.log(itor.next());//AAA {value: 111, done: false}
// next方法可以传入实参
console.log(itor.next("BBB"));//BBB {value: 222, done: false}
console.log(itor.next("CCC"));//CCC {value: 333, done: false}
console.log(itor.next("DDD"));//DDD {value: undefined, done: true}
第一次执行next函数时,可以根据生成器函数的参数接受数据。以后每次执行next所传递的参数,在函数体中都由yield
语句执行。例如第二次next传递的参数由函数体中第一个yield
语句执行完毕后返回,第三次次next传递的参数由函数体中第二个yield
语句执行完毕后返回,…
生成器实例
要求:1s后控制台输出111, 2s后输出222,3s后输出333
我们一般的做法:
setTimeout(() => {console.log('111');setTimeout(() => {console.log('222');setTimeout(() => {console.log('333');}, 3000);}, 2000);
}, 1000);
上面的编写方式如果需要多个回调,显然这种写法肥肠臃肿,这种无尽的回调方式称之为回调地狱。
可以通过生成器函数方式规避这种写法:
function one() {setTimeout(() => {console.log('111');iterator.next();}, 1000)
}
function two() {setTimeout(() => {console.log('222');iterator.next();}, 2000)
}
function three() {setTimeout(() => {console.log('333');iterator.next();}, 3000)
}
function* gen() {yield one();yield two();yield three();
}
let iterator = gen();
iterator.next();
要求:先获取用户数据,在获取用户订单数据,然后获取商品数据。
function getUserData() {setTimeout(() => {let data = "用户数据";itor.next(data);}, 1000);
}
function getOrderData() {setTimeout(() => {let data = "订单数据";itor.next(data);}, 1000);
}
function getGoodsData() {setTimeout(() => {let data = "商品数据";itor.next(data);}, 1000);
}
function* gen1() {let user = yield getUserData();console.log(user);let order = yield getOrderData();console.log(order);let goods = yield getGoodsData();console.log(goods);
}
let itor = gen1();
itor.next();