ES6变量的定义与一些新语法

ES6变量和的定义方式

let变量的定义

在以前ES5里面,我们使用var定义关键字有以下几个特点
1.var定义的变量没有区域性,它必须定义在方法里面才有区域性
2.var定义的变量会有一个建立阶段,在定义之前是可以使用这个变量的,只是没有值,值是undefined

let变量

let定义的变量没有建立阶段同时let定义的变量会有先天的自带闭包特性
let定义虽然没有建立阶段,但是在之前会有一个扫描特性

let的暂时性死区

下面的代码中方法sayHello里面的变量a见不到外边的值,也获取不到里面的值,这个阶段,我们就称之为暂时性的死区

let a = 123;
function sayHello(){
   console.log(a);       //暂时性的死区
   let a = "hello world"
}
sayHello();

[^1]总结let没有声明提前,也就是没有建立阶段,let变量自带区域性,同一个区域里面定义的let变量,变量名不能重复

const常量的定义

常量是与变量相对应的过程,变量是可以变化的量,常量则是不能变化的量
const具备let所有的特点,只是他不能改变而已
[^1]const a;
上面的代码没有任何意义,常量a在不进行初始化的时候,他就是undefined后期不能更改他的值
const锁栈不锁堆

对象的解构取值

let obj = {
    userName: "张三",
    age: 18,
    sex: "女",
    hobby: ["看书", "睡觉", "玩游戏"]
}
let { userName, age } = obj;
console.log(userName, age);
    *解构失败以后得到的值就是一个undefined*
var obj = {
    userName: "张三",
    teacherInfo: {
        teacherName: "标哥哥"
    },
    stuList: [
        {
            stuName: "小夫",
            age: 20
        }
    ]
}

上面的代码解构老师的姓名tname,与第一个学生的姓名stuname

let { teacherInfo: { teacherName }, stuList: [{ stuName }] } = obj;
console.log(teacherName,stuName);

展开运算符

展开运算的写法

var obj = {
    userName:"张三",
    age:18
}
let obj1 = {...obj,sex:"女"};   //把obj这个对象展开,然后再解构赋值
obj1.userName = "李四";
解构与展开运算符结合
let arr1 = ["a", "b", "c", "d"];
let arr2 = [1, 2, 3, 4, 5];
let newArr2 = [...arr1, ...arr2];

字符串拓展

模板字符串不在使用单双引号,转而去使用反引号 ``

let x = 10;
let y = 5;
function abc(s, v1, v2) {
    console.log(s[0]);              //hello
    console.log(s[1]);              //world
    console.log(s[2]);              //""
    console.log(v1);                //15     x+y
    console.log(v2);                //50     x*y
}
abc`hello${x + y}world${x * y}`;

上面的代码就相当于下面的代码

abc(["hello", "world", ""], x + y, x * y);
在以前的ES5里面,我们有很多字符串的方法,其实在ES6里面,也有新的方法产生

1.padStart,padEnd 补元素 第一个参数是用什么补,第二个参数是补多少个
2.trimStart,trimEnd 去掉空格
3.repeat()方法返回一个新字符串,表示将原字符串重复n次。如

'x'.repeat(3) // "xxx"
'hello'.repeat(2) // "hellohello"
'na'.repeat(0) // ""

4.includes方法,是否包含

let site = ['runoob', 'google', 'taobao'];
site.includes('runoob'); 
// true 
site.includes('baidu'); 
// false

数组的扩展

1.Array.of() 使用Array.of跟[]定义效果一样
2.Array.from() 把类数组转换变成数组 ,之前使用的是Array.prototype.slice.call, jQuery里面使用的是$.toArray()或者$.makeArray()来进行
3.Array.prototype.fill() 填充数组

`javascript
        let arr=new Array(100);
        arr.fill(0,2,5);

fill第一个参数用指定的元素去填充数组,第二个参数起始位置,第三个参数结束位置不包含
4.Array.prototype.flat() 拍平当前数组,后面接参数代表拍平几层,默认拍平一层
5.==Array.prototype.find()==用于数组中查找对象,返回的是一个对象

let arr = [
      { sid: "H20010001", sname: "张三" }
 ]
let obj = arr.find(function(item,index,_arr){
return item.sid=="H20010003";
})

6.Array.prototype.findIndex() 用于在数组中查找指定索引

数组的迭代

1.forEach
2.map
3.filter
4.some
5.every
6.for语句
7.for…in

上面的7个点都可以将数组遍历出来,接下来,我们学习一个新的遍历点(迭代iterable)
现在在上面的代码当中,我们学到了一新的迭代方法,叫for...of,为什么可以使用for...of?是不是所有的东西都可以使用for...of?它与for...in又 有什么区别呢
for...in是用于遍历有序集合,for...of不仅可以遍历有序集合,还可以遍历无序集合
只要能够使用展开运算符…的,就可以使用for... of
默认的五种
1. NodeList

  1. Array
  2. Set
  3. Map
  4. arguments

是因为他们内部都有迭代器interable

Set单值集合

set是一个内部元素不重复的单值集合,它是一个无序集合
可以利用Set来实现快速去重
1.内部元素不重复
2.无序集合

 let s1 = new Set();
    s1.add("张三")   //添加
  1. add()方法,向集合中添加一个元素
  2. delete()方法,删除一个元素
  3. clear()方法,清空这个集合
  4. has()方法,判断集中是否有这个元素,存在则返回true,不存在则返回false
  5. size属性,获取当前集合里面的元素个数
    Set对象的遍历

在这里就要注意了,Set是一个单值集合,它没有索引,所以是不能够通过for...in来进行遍历。因为for...in遍历出来的是索引(属性名)
这个时候,我们就可以使用for...of,这种遍历方式,不依赖于索引,它直接就把里面的值拿出来了,而Set对象是一个单值集合中,它里面是有值的

Map键值对集合

Map比Set多了一个Key,它是键值对集合,也不是单值集合了,但是Map内部的键是不允许重复的,值可以重复

Map的创建

   let m1 = new Map();
   m1.set("userName", "标哥哥");
   m1.set("age", 19);

在上面的代码里面,m1.set就是向map对象当中添加新的元素,它是成对的添加的,第一个参数代表key键,第二个参数代表value值,所以在上面的代码当中它age所表示的结果就是20

Map对象的相关方法

  1. set()方法,向对象里面添加键值对
  2. get()方法,从对象当中根据一个key来取值,如果取不到则返回undefined
  3. delete()方法,根据一个key从这个集合中删除一个键值对
  4. clear()方法,清空这个集合
  5. has方法,判断对象中是否有这个键
  6. keys()方法 ,返回当前对象的键的迭代器(Iterator),这个迭代器可以用于for…of遍历
  7. values()方法,返回当前对象值的迭代器(Iterator),这个迭代器可以用于for…of遍历
  8. size属性,当前集合对象里面的元素个数
    Map对象的遍历

依然不能用for…in遍历,用的是for…of
遍历的结果是键值,成双成对的

单独遍历key跟value

  let m1_keys =  m1.keys();  //它返回的也是一个迭代器,也可以使用for...of去完成
       for(let k of m1_keys){
           console.log(k);
       }
迭代器接口

迭代器接口叫Iterable,它是一个规范,满足了这个规范就可以用for…of遍历或者用…展开运算符、
下面5个对象默认实现了接口
1.NodeList
2.Array
3.Set
4.Map
5.arguments

函数的扩展

无构造函数的函数

在对象里面定义的方法它是不能通过new去取的

sayHello() {
        //通过这种方式定义的方法,它是没有构造函数的
        console.log("我是对象2里面的方法");
    }

函数参数的默认值

 function def(a = 1, b = 2) {
    console.log(a,b);
    }

一般函数的默认值写在最后

箭头函数

 let e = (x, y) => x + y;  这是函数有返回值的,没有返回值是不允许这样写的
   let d = (x, y) => {
    console.log(x, y);
 }

箭头函数与普通函数的区别

  • 关于this指向的问题
    箭头函数会绑定this【箭头函数会跳过当前的环境的this,取上级环境的this】箭头函数最主要的使用声明就是在回调函数里面,因为它可以绑定外部的this,正是因为有一个像这样的特点,箭头函数就不能作为对象里面的方法
  • 箭头函数里面没有arguments

    const add = (x, y) => {
    console.log(arguments);
    }
    add(11,20);
    这个时候的arguments在这里它不是指实集合,它拿不到1120这两个值

  • 箭头函数是不能当成构造函数去执行的

const abc = (userName, sex) => {
console.log(userName, sex);
}
let obj = new abc(“张三”, “男”); // 不能当成构造函数去执行

  • 箭头函数也是有作用域的,这一点与普通函数一样

函数柯里化

函数柯里化是函数的另一种写法,它将函数的执行步骤分成了多个部分来进行

 function add(a, b, c) {
        return a + b + c;
    }
    let result1 = add(11,12,13);
    console.log(result1)
上面的函数就是一个普通的函数,我们将这个函数柯里化一下
    function add1(a) {
        return function (b) {
            return function (c) {
                return a + b + c;
            }
        }
    }
    let result2 = add1(11)(12)(13);
    console.log(result2);

class关键字创建对象

  class Person {
        //使用class的时候一定要注意,如果有参数要接收,要在constructor里面接收,它是真正的构造函数
        //如果我自己创建了constructor ,则系统就不再给我创建了
        constructor(userName, sex, age) {
            this.userName = userName;
            this.sex = sex;
            this.age = age;
        }
    }
    let p1 = new Person("张珊", "女", 19);   //调用了Person里面的constructor

class里面的constructor叫构造器,如果我要接收外部的参数,我就手动的写一个,如果不需要接收外部的参数,可以不用写,系统会自动帮我们创建一个无参的constructor
同时要注意constructor这个方法是在new的一瞬间自动调用的
当一个对象当中既有属性,又有方法的时候,应该怎么办呢?

    class Person {
    constructor(userName, sex, age) {
        this.userName = userName;
        this.sex = sex;
        this.age = age;
    }
    sayHello() {
            console.log(`大家好,我叫${this.userName},我的性别是${this.sex},我的年龄是${this.age}`);
        }
    }
    
    let p1 = new Person("杨标","男",19);
    p1.sayHello();

静态方法

在ES6的class创建的构造函数里面,它有一个新的关键字叫static,通过这个关键字来修饰的方法是不需要new和对象就可以直接调用了

class Person {
        constructor(userName) {
            this.userName = userName;
        }
        sayHello() {
           console.log("我是sayHello方法")
    }
    //静态方法
        static abc(){
            console.log("我是方法abc");
        }
    }
    let p1 = new Person("张三");
    p1.sayHello();		//sayHello是一个普通的方法,所以必须通过new出来的对象来调用
    Person.abc();		//abc是一个通过static修饰的静态方法,可以直接使用Person来调用,无需new

访问器方法
get 取值
set 赋值

 class Person {
        constructor(firstName, lastName) {
            this.firstName = firstName;
            this.lastName = lastName;
        }
        //访问器方法  取值的时候自动调用
        get userName() {
            console.log("我是取值的时候调用");
            return this.firstName + this.lastName;
        }
        set userName(v) {
            //v就是你赋的值
            console.log("我是赋值的时候调用");
            this.firstName = v[0];
            this.lastName = v.substr(1);
        }
    }

在上面的对象里面,我们分别在userName的这个方法的前面添加了getset,然后我们测试一下上面的代码
let p1 = new Person("张", "三丰"); p1.userName = "周玮夫"; //赋值操作 console.log(p1.userName); //取值操作

继承
丢弃apply跟call使用extends

//新建一个Student,让它继承Person
    class Student extends Person {
        constructor(userName, sex, age, sid) {
            super(userName,sex,age);        //指的是父级的constructor
            this.sid = sid;
        }
    }

在上面的代码当中,你会看到一个super的关键字,它有一个注意事项,它必须放在最前面。在使用this关键字之前,一定要先有super(因为必须是先有父级,再有子级)
在无参数的继承的时候,其实我们是可以省略掉构造器constructor,但是如果你手动写的了constructor,那么,你必须主动的调用super

方法的重写

   class Person {
            sayHello() {
                console.log("我是Person里面的sayHello")
            }
        }
        class Student extends Person {
            sayHello() {
                console.log("我是Student的sayHello");
            }
            abc(){
                this.sayHello();
            }
            def(){
                //调父级的sayHello
                // this.__proto__.sayHello.call(this);    以前ES5里面的写法
                super.sayHello();                         //现在新的写法
            }
        }
        let s1 = new Student();
        s1.sayHello();          //调用的是自身的sayHello
        s1.abc();               //内部调用的还是自身的Student的sayHello
        s1.def();   

代码分析:上面的代码当中,父级对象Person里面它有一个sayHello,子级对象Student里面也有一个sayHello,这样当使用s1在调用sayHello的时候默认调用的是自身的方法,但是我们仍然可以使用super关键字主动的调用父级里面的方法

class与extends的特点
1.classextends来实现的对象与继承关系它们有个显著的特点,把所有的属性都放在了子级对象 ,把方法放在了父级对象
2.classextends实现的对象的创建与继承它们没有破坏instanceof的结构
3.通过class关键字所创建的东西本质上面还是一个function,这些方法的内部都有一个new.target的判断,这个判断让当前的function只能够new来调用,这就说明它只是一个构造函数

    function aaa(){
    if(new.target===undefined){
        throw new Error("Class constructor GirlStudent cannot be invoked without 'new'");
       }
    }
    上面的`aaa`的方法就只能是一个构造函数通过`new`来调用,你如果通过其它的方式来调用,这个时候就会报错

正则的扩展

分组捕获

提取字符串matchexec 方法
exec 首先捕获大的组,在进行分组

分组不捕获

?:

分组号
  let reg = /(\w)\1+/g;
    let str = "大家好abcaaabcccdee";
    let result = null;
    while ((result = reg.exec(str)) != null) {
        console.log(result);
    }

上面是通过exec来进行提取使用分组号提取了重复的字符
当然也可以用match来提取

let result = str.match(reg);
    console.log(result);			//[ 'aaa', 'ccc', 'eeee' ]

前瞻A(?=B)

    let str1 = "abc123";
    let reg = /abc(?=123)/g;
    reg.test(str1);     //true

负前瞻A(?!B)

    let reg =/abc(?!123)/g;
    reg.test(str1);     //false

后顾(?<=B)A

  let str3 = "123abc";
    let reg = /(?<=123)abc/g;
    reg.test(str3);     //true

负后顾(?<!B)A

    let reg = /(?<!123)abc/g;
    reg.test(str3);     //false

指数运算

 let a1 = Math.pow(2,3);	ES5写法
   let a2 = 2 ** 3;        	ES6写法  

大整型数据类型BigInt

   let a = 2n ** 513n;
   let b = 2n ** 513n + 1n;
   console.log(typeof a, typeof b);		//bigint类型
   console.log(a == b);		//false

反射Reflect

反射就是操作对象的另一种方式,它是以第三方的角度去操作对象

   Reflect.set(obj,"userName","李四");
        let a  = Reflect.get(obj,"userName");

反射在调用方法的时候是容易出问题的

 let b = Reflect.get(obj,"sayHello");
        b();  //这个时候的this就出问题了,因为它不再指向obj这个对象的,所以反射在调用方法的时候有其它的写法

反射如果要调用方法,应该采取如下的方式来进行

 let b = Reflect.get(obj, "sayHello");       //这一步是获取sayHello的方法名  
        Reflect.apply(b, obj, []);                  //这就是反射调用方法
        //上面的代码等价于
        obj.sayHello.apply(obj,[]);

反射也可以执行构造函数

   //ES5的代码
        function Person(userName) {
            this.userName = userName;
        }
        let p1 = new Person("张三");
        let p2 = Reflect.construct(Person,["李四"]);
        console.log(p1,p2);  
  //ES6的构造函数
        class Student {                       //class相当于new
            constructor(stuName, stuSex) {
                this.stuName = stuName;
                this.stuSex = stuSex;
            }
        }
        let s1 = new Student("王五", "男");
        let s2 = Reflect.construct(Student, ["赵六", "女"]);
        console.log(s1, s2);

不要把Object.keys()Reflect.ownKeys()混淆了,它们是不一样的

  1. Reflect.get获取对象的属性值
  2. Reflect.set设置对象的属性值
  3. Reflect.apply反射调用方法
  4. Reflect.construct执行某一个构造函数
  5. Reflect.defineProperty通过反射定义特殊属性
  6. Reflect.deleteProperty通过反射删除对象的某一个属性
  7. Reflect.has通过反射判断一个属性是否存
  8. Reflect.ownKeys通过反射来获取对象的属性名
  9. Reflect.getOwnPropertyDescriptor通过反射获取某一个对象的某一个属性的描述信息
  10. Reflect.isExtensible通过反射判断某一个对象是否是一个“不可扩展对象”
  11. Reflect.preventExtensions通过反射设置一个对象为“不可扩展对象”
  12. Reflect.getPrototypeOf获取一个对象的prototype
  13. Reflect.setPrototypeOf设置一个对象的prototype

代理Proxy
我们可以把代理认为是一种全方位的getset访问控制

   let obj = function () {
    let temp = {
        userName: "胡歌",
        sex: "男",
        age: 34
    }
    let p1 = new Proxy(temp, {
            get(target, properName, receiver) {
                return Reflect.get(target,properName,receiver);
            },
            set(target, properName, value, receiver) {
            }
        });
        return p1;
    }();

分析:p1就是代理对象,obj就是实实在在的对象,p1代理了obj的所有操作。其中get负责取值,set负责赋值操作

在get方法里面,有三个参数,分别如下
1. target代理的目标对象,这里指obj
2. properName代表当前操作的属性,上面的代码中指userName
3. receiver代表理(经济人),这里指p1

Symbol数据类型
这是ES当中的新数据类型[JavaScript当中的第7种数据类型],它是全局唯一标识,不重复。与其它语言中的GUID/UUID非常类似
let c = Symbol("标哥创建的"); Symbol创建出来的变量是独一无二的

生成器函数
生成器函数在调用以后它不会立即执行,只是这个方法被激活了,处理暂停状态,然后会返回一个迭代器
通过next方法来执行

unction* abc() {
        let arr = ["张三", "李四", "王五", "赵六", "小夫"];
        //我现在想把里面的值一个一个,依次返回出去
        yield arr[0];   //停止来,把刚刚生产出来的值arr[0]返回到外边去
        yield arr[1];
    }
    x.next();	
    只要有了Iterable就可以实现for...of
    for(let item of x){
        console.log(item);
    }

迭代器接口
接口:interface,接口可以理解为规范,当一个对象实现某一个接口就遵守这个接口的规范(我们也称之为具备某一种能力)
Iterable这个接口它主要定义的规范就是可以让元素使用for...of去遍历(能够通过for…of遍历的,就一定可以使用…的展开运算符)
在JavaScritp里面,以下数据对象默认实现了Iterable接口
1. NodeList
2. Array
3. Set
4. Map
5. arguments

所有的迭代器默认都会有一个方法叫next(),这个方法让程序继续向后运行,直到下一个yield再暂停,我们可以让一个对象手动去实现迭代器的接口,这样这个对象就可以被for...of

let obj = {
        userName: "张三",
        age: 18,
        sex: "女",
        address: "湖北省武汉市",
        [Symbol.iterator]: function* () {
        // 获取到自己所有的属性
        yield obj.userName;
        yield obj.age;
        yield obj.sex;
        yield obj.address;
        }
    }

//obj对象实现了Symbol.iterator这个方法,就说明它实现了迭代器的接口

for(let item of obj){
        console.log(item);
    }

ES6的异步解决方案
在之前的ES5里面,我们解决异步编程的方法就是回调
在ES6里面,为了更好的解决异步编程,它使用了一个新的对象叫Promise。在这个对象的内部它有三个状态

  1. pending等待状态
  2. resolve成功状态
    3. reject失败状态

默认情况下,它是等待状态
上面的代码我们可以通过ES7里面的awaitasync来进一步的简化,这是典型的异步转同步的概念

   function abc() {
    //abc现在就是一个异步的方法,它不可能立刻就外边结果
    //默认情况下的Promise是等待状态
    let p = new Promise((resolve, reject) => {
            setTimeout(function () {
                let temp = parseInt(Math.random() * 100);
                if (temp > 50) {
                    //成功
                    let obj = {
                        value: temp, msg: "成功"
                    }
                    resolve(obj);      //等待状态变成了成功状态
                }
                else if (temp <= 50) {
                    //失败
                    let obj = {
                        value: temp, msg: "失败"
                    }
                    reject(obj);       //等待状态变成了失败状态
                }
            }, 3000);
        });
        return p;
    }
    async function ddd() {
        try {
            //let result =  abc();          //返回的是一个Promise
            let result = await abc();       //这个时候返回的就不再是一个Promise了,而是这个承诺的【结果】。这结果可能成功,可能失败
            ***//await等待的只能是成功的结果,永远都是resolve***
            console.log(result);
        } catch (error) {
            ***//失败就在这里,error就是失败的obj   ,这里执行的一定是reject的结果***
            console.log("你等到的是失败的结果");
            console.log(error);
        }
    }
    ddd();
  1. Promise一定是有三个状态的
    2. 成功的状态是resolve,失败的状态是reject
    3. await必须与async同时使用
    4. await等待的只能是Promise,不能是其它东西,必须只能等到resove的结果,失败的结果在catch里面
    5. 一定要熟悉Promise的写法
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

请叫我斌哥哥

给打赏的我会单独一对一讲解

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值