浅谈ES6新特性

一、let及const

1、ES6新增的两种新的声明格式

       let 和const 用于补全ES5标准中var声明变量的不足:在JavaScript中用’var’来声明变量会出现变量提升的情况,即通过"var"声明的变量系统都会把声明隐式的升至顶部,这样的特性往往会让刚接触JavaScript及习惯其他语言的开发人员不适应,导致程序出现问题。

2、ES5中定义作用域有两种

       全局作用域和函数作用域,ES6中新增了块级作用域,用"{}"表示。块级作用域用于声明作用域之外无法访问的变量,主要有两种:

(1)函数内部块级作用域

function test() {
  let a = 20
}
test()
console.log(a)

(2)在字符{}之间的区域

{
    let a = 10
}
console.log(a)
for(let i=0;i<100;i++) {
    let sum = 0;
    sum = sum+i
}

3、const命令

       使用const声明的是常量,常量的值不能通过重新赋值来改变,并且不能重新声明,所以每次通过const来声明的常量必须进行初始化。

(1)const声明对象

       与其他语言不同,const在使用过程中如果声明的是对象,需要注意可以修改对象的属性值,但不允许修改已经声明的对象。

const obj = {
    name: '张三',
    age: 20
}
//属性值是可以直接修改的
obj.name = '李四'
console.log(obj)

//对象直接修改会报错
obj = {}

(2)Object.freeze函数

       如果想让对象属性不能修改,可以借助Object.freeze函数来冻结对象。

   const obj = {
        name: '张三',
        age: 20
    }
    Object.freeze(obj); //冻结对象
    obj.name = "李四"; //修改属性
    console.log(obj); //结果任然是{name:'张三',age:20}

       通过Object.freeze冻结对象需要注意的是:不能冻结多层对象.

const obj = {
    name: '张三',
    age: 20,
    family: {
        father: {
            name: '张安',
            age: 48
        }
    }
}
Object.freeze(obj); //冻结对象
obj.family.father.age = 50; //family对象里的属性不能被冻结
console.log(obj); //对象age的值被改变

        解决多层冻结问题可以通过封装一个deepFreeze函数来实现:

function deepFreeze(obj){
    Object.freeze(obj);
    for(let key in obj){
        if(obj.hasOwnProperty(key) && typeof obj[key] === 'object'){
            deepFreeze(obj[key])
        }
    }
}

deepFreeze(obj); //冻结对象
obj.family.father.age = 50; //修改对象属性的值
console.log(obj); //对象age的值未被改变

4、临时死区

        let和const都是块级标识符,所以let和const都是在当前代码块内有效,常量不存在变量提升的情况。但是通过let和const声明的常量,会放在临时死区(temporal dead zone),通过下面代码可以看出:

{
    console.log(typeof a);
    let a = 10
}

       即使通过安全的typeof操作符也会报错,原因是JavaScript引擎在扫描代码变量时,要么会把变量提升至顶部,要么会把变量放在临时死区。这里通过let声明的’a’变量会被放在临时死区,所以在声明之前打印就会报错。

5、循环中let和const的使用

       (1)在ES5标准中,for循环中的循环变量都是通过var来声明的,由于var没有独立的作用域,导致在循环中创建函数时会出现结果和思路不一致的情况,如下:

let funArr = []; //该数组中存放的是函数
for(var i=0;i<5;i++) {
    funArr.push(function(){
        console.log(i)
    })
}
funArr.forEach(item=> {
    item()
})

       (2)循环结果不是预想的0,1,2,3,4,而是5个5,这是因为var声明在循环中作用域共用,并且会把i保存在全局作用域中。要解决循环中保存函数的问题,可以利用闭包创建独立的作用域,代码如下:

   let funArr = [];
        for(var i=0;i<5;i++) {
            (
                function(i) {
                    funArr.push(function(){
                        console.log(i)
                    })
                }
            )(i) 
        }
        funArr.forEach(item=> {
            item()
        })

        (3)这样通过自执行函数就可以解决循环中创建函数的问题。但利用ES6中let和const提供的快级作用域可以让代码更简洁:

let funArr = [];
for(let i=0;i<5;i++) {
    funArr.push(function(){
        console.log(i)
    })
}
funArr.forEach(item=> {
    item()
})

        (4)在for-in或for-of循环中使用const时,方法与let一致:

   let obj = {
        name: '张三',
        age: 20
    }
    for(const i in obj){
        console.log(i)  //输出name、age
    }

    let arr = ['张三','李四','王五']
    for(const value of arr){
        console.log(value) //输出张三、李四、王五
    }

二、解构赋值

1、数组的解构

        (1)在ES5标准中赋值多个变量采用的方法是:

   var a = 10;
   var b = 20;
   var c = 30;

       ES6提供了更简洁的解构赋值来实现上述变量的定义:

  let [a,b,c] = [10,20,30];
    console.log(a);
    console.log(b);
    console.log(c);

等号右边的值会按照顺序依次赋值给左边的变量。

        (2)非一一对应关系的赋值

let [a,b] = [10,20,30]
console.log(a,b); //输出10,20

let [a,b,c] = [10,20]
console.log(a); //10
console.log(b); //20
console.log(c); //undefined

       (3)也可以通过"…"把特定的元素放在变量里

let [a,...arr] = [10,20,30]
console.log(a); //10
console.log(arr); //20,30

       (4)在ES6中可以通过解构赋值来互换变量

 let a = 10;
    let b = 20;
    [a,b] = [b,a];
    console.log(a,b)

2、对象的解构

       (1)对象解构的写法与数组解构类似

let obj = {
        name: '张三',
        age: 20,
        height: '178com'
    }

    let { name,age,height } = obj; //变量的名称必须和对象的属性名相同
    console.log(name,age,height);

        (2)也可以解构多层对象

let person = {
    name: '张三',
    age: 20,
    family: {
        father: '张武',
        mother: '李燕'
    }
}
let { name,age,family: {father,mother }} = person
console.log(name,father)

       (3)在解构对象时也可以自定义变量名称:

  let obj = {
            name: '张三',
            age: 20
        }
        let { name:myname,age:myage } = obj;
        console.log(myname,myage);

3、解构的默认值和参数的解构

        (1)不管是数组的解构赋值,还是对象的解构赋值都可以添加默认参数。如下:

let obj = {
    name: '李四',
    age: 20
}

let { name,age,height="178com" } = obj;
console.log(height); //178com

       (2)在函数参数中使用解构,参数解构也可以给默认参数

function fun({name,age,height="178com"} = {}){
    console.log(name,age); //张三,20
}
let obj = {
    name: '张三',
    age: 20
}
fun(obj)

三、模板字符串

        ES5标准中一般输出模板是通过字符串拼接的方式进行的。在ES6中可以通过模板字符串简化字符串的拼接,模板字符串通过反引号来表示"``",如果要嵌入变量通过"${ 变量名 }"来实现:

  let arr = [
           {
               name: '张三',
               age: 20
           },
           {
               name: '李四',
               age: 23
           },
           {
               name: '王五',
               age: 25
           }
       ]
       let str = "";
       for(let i=0;i<arr.length;i++){
           str += `姓名是:${ arr[i].name },年龄是:${ arr[i].age }`;
       }
       console.log(str)

四、Symbol

       ES5中提供了6种数据类型分别是:undefined、null、boolean、string、number、object。ES6中新增了一种数据类型Symbol来表示唯一的值,每个创建的Symbol都是唯一的,这样在实际运用中可以创建一些唯一的属性及定义私有变量。例如:

let s1 = Symbol; //直接创建
   let s2 = Symbol('mySymbol'); //传入字符串创建

1、目前前端项目都会采用模块化构建,为了防止对象属性名被覆盖,可以通过symbol来定义属性名;例如:

 //a.js
    const NAME = Symbol('name')
    let obj = {
        [NAME]:'张三',
        age: 20
    }
    export default obj;
//b.js
import Obj from './a.js'
const NAME = Symbol('name');
obj[NAME] = '李四';
console.log(obj); //{age:20,Symbol():'张三',Symbol():'李四'}

       2、利用Symbol作为属性名,属性名不会被Object.keys()、Object.getOwnPropertyNames()、for…in循环返回。例如:

   let obj = {
            [Symbol()]:'张三',
            age: 20,
            height:'178com'       
        }
        for(let key in obj){
            console.log(key);
        }
        let keys = Objects.keys(obj);
        console.log(keys); //输出:'age','height'
        console.log(Object.getOwnPropertyNames(obj)); //输出:'age','height'

        3、可以在类里利用Symbol来定义私有属性及方法。例如:

  let People = (function(){
            let name = Symbol("name");
            class People {
                constructor(yourName) { //构造函数
                    this[name] = yourName
                }
                sayName() {
                    console.log(this[name])
                }
            }
            return People;
        })();
        let zhangsan = new People("张三");
        console.log(zhangsan[Symbol("name")]); //undefined
        zhangsan.sayName(); //张三

五、函数

1、函数形参的默认值

       在很多情况下,需要在使用函数的时候给定默认参数,在ES5标准中一般会这样写:

  function fun(name,age,cb){
        name = typeof(name !== 'undefined')?name: '张三'
        age = typeof(age !== 'undefined')?age: 20
        cb = typeof(cb !== 'undefined')?cb:function(){}
        console.log(name,age);
    }
    fun("李四",0)

2、函数形参不定参数

       在很多情况下,使用函数传参的时候,形参的数量是不固定的,这时候要获取参数值就会比较麻烦。在ES5标准中可以通过隐藏参数arguments来获取,此时会把所有参数放在arguments中。例如:

function fun() {
    console.log(arguments);
    console.log(arguments[0]); //第一个参数
    console.log(arguments[1]); //第二个参数
    console.log(arguments[2]); //第三个参数
}
fun('张三',20,'178com')

3、箭头函数

       箭头语法最大的特点是有箭头"=>"符号,当然箭头语法有很多变式写法

(1)没有参数,用括号代替

let fun = ()=> "张三"

(2)一个参数,括号可以省略

   let fun = arg=> "李四"

(3)多个参数

   let fun = (arg1,arg2)=> arg1 + arg2
    console.log(fun(1,3))

       利用箭头语法里隐式返还的时候需要注意对象的情况,需要注意如下错误情况:

let fun = () => {
    name: '张三',
    age: 20
}

       这个代码初步感觉是返还一个对象,但是这里的大括号和函数里的大括号在含义上有冲突,系统会认为大括号是函数里的括号,而不是对象里的括号,导致报错

let fun = () => ({
    name: '张三',
    age: 20
})
console.log(fun());

(4)this指向

        箭头函数里没有this绑定,如下代码,this指向对象本身

let obj = {
        id: 2,
        fun: function() {
            console.log(this.id); // 输出:2
        }
    }
    obj.fun()

       上面代码可以打印出id为2,this指向了obj,所以this.id可以取得obj.id。如果改成箭头语法会发现,函数中this指向改变了,代码如下:

 let obj = {
     id: 2,
     fun: ()=>{
         console.log(this.id); //输出:undefined
     }
 }
 obj.fun()

       这里发现this.id获取不到值,原因是箭头函数没有this绑定,箭头函数中的this会指向最近的上层this,所以这里this的指向是window,所以最终取不到this.id

(5)参数arguments的绑定

      使用箭头语法的时候没有隐藏参数arguments的绑定,代码如下:

   let fun = (arg1,arg2) => {
            console.log(arguments); // arguments is not defined
            return arg1 + arg2
        }
        fun()

六、类(class)

1、ES5中的‘类’

      在ES5标准中通过构造函数来模拟类的功能,一般会定义一个构造函数,把一类功能做封装,通过new运算符来调用。

function Person(name) {
    this.name = name;
    this.age = 20;
}
Person.prototype.fun = function() {
    console.log("fun...")
}

2、ES6中class关键字

      在ES6标准中提供class关键字来定义类,在写法上更简洁、语义化更强。如下:

class Person {
    constructor(name){
        this.name = name;
        this.age = 20;
    }
    fun() {
        console.log("fn...")
    }
}
let zhangsan = new Person("张三");
console.log("zhangsan.name");
zhangsan.fun()

3、set和get关键字

      ES6支持通过getter、setter在原型上定义属性。创建getter的时候需要用关键字get;创建setter的时候需要用关键字set。例如:

  class Person{
        constructor(name,age){
            this.name = name;
            this.age = age;
        }
        get age(){
            return 20;
        }
        set age(newValue){
           this.age = newValue
        }
    }

    let p = new Person('张三',20);
    console.log(p.age);

4、静态成员

(1)ES5

在ES5标准中的静态成员,可以通过如下方式实现:

function Person(name){
    this.name = name;
    this.age = 20;
}
Person.num = 10;  //静态属性
Person.fun = function() { //静态方法
    console.log("fun...")
}
(2)ES6

在ES6标准中提供static关键字类声明静态成员:

 class Person{
            static num = 20; //静态属性
            constructor(name){
                this.name = name;
                this.age = 20;
            }
            static fun() { //静态的成员方法
                console.log("fun...")
            }
        }

        let p = new Person('张三');
        Person.fun();
        console.log(Person.num);

5、类的继承

(1)ES5实现继承

      在ES5标准中可以通过call、apply、bind来实现构造函数的继承,实现方式如下:

   function Dad(name) {
            this.name = name;
            this.age = 20;
        }

        function Son(name) {
            Dad.call(this,name);
            //Dad.apply(this,[name]);
            //Dad.bind(this)(name);
            this.height = "178com";
        }

      上述方式可以实现构造函数的继承,但是如果有方法在Dad原型上实现,还需要考虑原型的继承,单纯的原型赋值继承还会涉及传址问题,所以实现起来比较繁琐。

(2)ES6标准中类的继承

通过extends关键字实现。

         class Dad {
              constructor(name){
                  this.name = name;
              }
              fun() {
                  console.log('fun...')
              }
          }
          class Son extends Dad {
              constructor() {
                  super();
              }
              hobby() {
                  console.log('喜欢篮球')
              }
          }
          let son = new Son();
          son.fun();

     在继承中需要调用**super()**方法继承父类的构造方法。super()在使用过程中需要注意以下两点:
A、在访问this之前一定要调用super().
B、如果不调用super(),可以让子类构造函数返还一个对象

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值