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 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 = `
- 沈腾
- 玛丽
- 魏翔
- 艾伦
`;
//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'
});
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); //转化为数组
ES6引入了一种新的原始数据类型 Symbol,表示独一无二的值。它是JavaScript语言的第七种数据类型,是一种类似于字符串的数据类型。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 的值是唯一的(es内部实现唯一性),用来解决命名冲突的问题。
Symbol值不能与其他数据进行运算
Symbol定义的对象属性不能使用for...in
循环遍历,但是可以使Reflect.ownKeys
来获取对象的所有键名
在企业开发中如果需要对一些第三方的插件、框架进行自定义的时候可能会因为添加了同名的属性或者方法, 将框架中原有的属性或者方法覆盖掉为了避免这种情况的发生, 框架的作者或者我们就可以使用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值以外,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接口,就可以完成遍历操作。
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();