欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

ES6新特性

程序员文章站 2022-04-07 13:57:40
...

ES新特性

let、const和块级作用域

  • 原来ES6之前只有全局作用域和函数作用域,ES6增加了块级作用域,并引入了let和const关键字
  • let不像var那样会发生变量提升,所以变量一定要在声明后使用,且在相同作用域内不能再次声明,且在声明所在的块级作用域内有效,const声明的是只读的常量一旦声明不能修改,否则报错,且在声明所在的块级作用域内有效
  • 变量提升优先级:函数声明 > 参数声明 > var声明,执行顺序是先执行变量提升,再执行函数提升
/*for(var i =0;i<3;i++) {
   for(var i =0;i<3;i++) {
   	console.log(i);
   }
    console.log('内层结束 i='+i);
}*/
//只输出了三个0,1,2,内层结束i=3;要想循环嵌套输出9个
for(var i =0;i<3;i++) {
   for(let i =0;i<3;i++) {
   	console.log(i);
   }
    console.log('内层结束 i='+i);
}
//输出了三遍0,1,2和内层结束i=3;


//变量声明优先级
function a (){
    console.log('22');
}
var a;
console.log(a);//function a(){console.log('22');}


fn2();//这里能执行 函数提升
fn3();//这里不能执行 变量提升

function fn2(){
    console.log('fn2');
}
var fn3= function(){
    console.log('fn3');
}

变量提升常见面试题

//测试题1,执行顺序是先执行变量提升,再执行函数提升
function a(){};
var a ;
console.log(typeof a);//function
//测试题2
if(!(b in window)){
    var b = 1;
}
console.log(b);//undefined
//测试题3
var c = 3;
function c(c){
    console.log(c);
}
c(1);//c is not a function

变量的解构和赋值

  • 数组解构:按照对应位置,对变量赋值,可以进行嵌套

    const arr = [1,2,3];
    const [,,bar] = arr;
    console.log(bar);//3
    
    //设置默认值,对象同理
     let [x, y = 'b'] = ['a'];
     console.log(x); //a
     console.log(y); //b
    
  • 对象解构:对象的属性没有次序,先找到同名属性,然后再赋给对应的变量

    let {
        bar,
        foo
    } = {
        foo: 'aaa',
        bar: 'bbb'
    }; 
    console.log(foo);// aaa,
    console.log(bar);//bbb
    //如果变量名称已被使用,我们无法使用重复的变量名称赋值,如果想要使用则需要加冒号加上新的变量名称
    let name = 'tom';
    // const {name} = {name:'zce',age: 10};//这里会报错,name已被使用
    const {name:newName='jack'}={name:'zce',age: 10};
    console.log(newName);//zce
    

字符串扩展

  • 模板字符串:使用反引号包含字符串,变量代码可放在括号中间${}标识

    const tmpl = addrs => `
        <table>
        	${addrs.map(addr=>
        		`<tr><td>${addr.first}</td></tr><tr><td>${addr.last}</td></tr>`
        	).join('')}
        </table>`;
    const data = [{
        first:'<jane>',
        last: 'Bond'
    }, {
        first:'Lars',
        last: '<Croft>'
    }];
    console.log(tmpl(data));`<table>
        	<tr><td><jane></td></tr>
    		<tr><td>Bond</td></tr>
    		<tr><td>Lars</td></tr>
    		<tr><td><Croft></td></tr>
        </table>`
    
  • 带标签的模板字符串

    const name = 'tom';
    const gender = true;
    // 可以对模板字符串进行加工处理
    function myTagFunc(stringArr,name,gender) {
        console.log(stringArr,name,gender);//['hey,',' is a {gender}', '.']  tom, true
        return '123';
    }
    const result = myTagFunc`hey, ${name} is a {gender}.`;
    console.log(result);//123
    
  • 字符串的扩展方法

    • 检验字符串是否包含另一个字符串,ES6提供了includes(),startsWith(),endsWith(),返回布尔值,第一个参数为所需检验字符串,第二个参数为检验开始的位置,endWidth中第二个参数标识为前N个位置
    let s = 'hello world';
    console.log(s.includes('world', 6));//true
    console.log(s.startsWith('hello', 6));//false
    console.log(s.endsWith('hello', 5));//true
    

函数的扩展

  • 给函数中的形参添加默认值

    function foo(enable=false) {
        console.log(enable);
    }
    foo();//false
    foo(true);//true
    
  • 箭头函数:使用箭头函数的this始终指向当前作用域的this,即就是在定义的时候所处的对象就是它的this,箭头函数的this看外层是否有函数,如果有,外层函数的this就是内部箭头函数的this,如果没有则this一般都是window

    //有两个以上的形参括号不能省略,函数体的大括号只有一条语句或者代码的时候可以省略,表示返回该条语句的执行结果
    let func = (a, b = 12) => {
        console.log(`${a}已经${b}岁了`, this);
    };
    func('tom');//tom已经12岁了 window对象
    
    // 只有一个参数的时候括号可以省略
    let func2 = a => a;
    /**等同于
    	let func2 = function (a){
    	  return a;
    	}
    */
    
    console.log(func2('22'));//22
    
  • 剩余参数(三点运算符):放在参数的最后面,接收后面的所有参数,且只能使用一次

    function foo(first, ...args) {
    	console.log(args);
    }
    foo(1,2,3,4,5);//2,3,4,5
    

数组的扩展

  • 扩展运算符:三点运算符可以将一个数组转化为用逗号分隔的序列,其底层是默认去调用iterator接口

     console.log(...[11, 222, 332, 232]); // 11,222,332,232
    
  • 扩展方法

    //Array.of(),将一组值转换为数组,参数只有一个的时候,该参数为数组长度,否则为数组具体的值
    console.log(Array.of(3, 33, 333)); //[3,33,333]
    console.log(Array.of(3)); //[,,,]
    
    //find(),用于找到第一个满足条件的元素,它的参数是一个回调函数,返回满足条件的数组元素或者undefined
    // value 为数组元素,index为索引,arr为原数组
    console.log([1, 5, 10, 15].find((value, index, arr) => {
        return value > 9;
    })); // 10
    
    //findIndex(),返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回 -1
    // value 为数组元素,index为索引,arr为原数组
    console.log([1, 5, 10, 15].findIndex((value, index, arr) => {
        return value > 9;
    })); // 2
    

对象的扩展

  • 对象字面量的增强,可简写对象

    let name = 'curyy',
        age = 12;
    let obj ={
        name:name,
        age:age,
        //[]中的值可以添加方法,方法的返回值作为属性名, 计算属性名,等价于下面的obj[Math.rondom()]=123;
        [Math.rondom()]:123,
        say:function(){
            console.log(this.name,this.age);
        }
    };
    //在ES6中,对象属性名和变量名称一致的话,可以省略简写
    let obj = {
        name,
        age,
        say(){//function可以省略
            console.log(this.name,this.age)
        }
    };
    // obj[Math.rondom()]=123;
    
  • 对象的扩展方法

    /*
    	Object.assign(target,source1,source2...)
    	将源对象的课枚举属性复制到目标对象,
         浅拷贝如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用
         只有一个参数的时候直接返回该参数,如果该参数不是对象会先转换为对象然后再返回
         当undefined和null出现在target参数位置的时候会报错
    */
    const source1 = {
            a: 1
        },
        source2 = {
            b: 2
        },
        target = {
            c: 3
        };
    Object.assign(target, source1, source2);
    console.log(target); //{c: 3, a: 1, b: 2}
    source2.b = 'fff';//浅拷贝
    console.log(target);//{c: 3, a: 1, b: 'fff'}
    
    /*
    	Object.is(obj1,obj2),判断两个值是否相等,
    	原来判断两个值是否相等使用的是==(两等运算符会转换数据类型)或者===
    */
    console.log(Object.is('foo', 'foo')); //true
    console.log(Object.is('12', 12)); //false
    console.log(Object.is(+0, -0)); //false
    console.log(Object.is(NaN, NaN)); //true
    
    /*
     *	Object.values():循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)对应的value值
     *  Object.entries():循环遍历对象自身的和继承的可枚举属性和其对应的value值
     *  Object.keys():循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)
     */
    Object.values({a:1,b:2,c: 3});// [1, 2, 3]
    Object.entries({a:1,b:2,c: 3});// [ ["a", 1], ["b", 2], ["c", 3]]
    Object.keys({a:1,b:2,c: 3});//["a", "b", "c"]
    
  • 扩展:Object.defineProperties()的使用

    /*
    	Object.defineProperties(obj,descriptors);
    	作用:为指定对象定义扩展多个属性
    	get:用来获取当前属性值的回调函数
    	set:修改当前属性值触发的回调函数,并且实参为修改后的值
    	存取器属性:setter,getter一个用来存值,一个用来取值
    */
    const obj = {firstName:'kobe', lastName: 'bryant'};
    Object.defineProperties(obj, {
        //添加的属性名称
        fullName:{
            // 获取扩展属性的值,惰性求值
            get() {
                console.log('get()');
                return this.firstName + ' ' + this.lastName;
            },
            // 监听扩展属性,当扩展属性发送变化的时候触发
            set(newValue) {
                console.log('set()',newValue);
                let name = newValue.split(' ');
                //this.fullName = newValue;直接赋值是不起左右的,因为get方法那里定义读取的方式
                this.firstName = name[0];
                this.lastName = name[1];
            }
        }
    });
    conosle.log(obj);//{firstName:'kobe', lastName: 'bryant',fullName:'kobe bryant'}这里的fullName是惰性求值
    obj.fullName = 'tim dukun';
    console.log(obj);//不设置set方法里的回调,fullName不会修改
    
    //其实每个对象都有get和set方法
    var obj2 = {
        firstName :'curry',
        lastName : 'stephen',
        //fullName为扩展的属性名称,下面同理
        get fullName() {
            return this.firstName + ' ' + this.lastName;
        },
        set fullName(data){
            let name = newValue.split(' ');
            this.firstName = name[0];
            this.lastName = name[1];
        }
    };
    console.log(obj2);
    obj2.fullName = 'tim dukun';
    console.log(obj2);
    

Proxy(代理对象)

const person = {
    name: 'cjr',
    age: 20
};
// 传入两个参数,代理目标对象,一个对象中含有get和set方法
const personProxy = new Proxy(person,{
    // 读取对象某个属性
    get(target, property){//目标对象,属性名
        //console.log(target,property);
        //return 100;//返回的是代理后属性值
        return property in target ? target[property] : "defalut";//可以设置默认返回值
    },
   // 写入某个属性
    set(target, property, value){ // 目标对象,写入的属性名,写入的属性名称
        // console.log(target, property, value);
        if(property === 'age') {
            // 判断值是否是整数
            if(!Number.isInteger(value)) {
                throw new TypeError(`${value} is not int`);
            }
        }
        // 把键设置到目标对象
        target[property] = value;
    }
});
//console.log(personProxy.name);//100 ,get方法那里打印 {name:'cjr',age:20} name
console.log(personProxy.noname);//default
personProxy.age = 'fff';//报错
personProxy.age = 12;//这样就可以把属性设置到目标对象上
console.log(person,personProxy);//{name: 'cjr',age: 20},{name: 'cjr',age: 20}

Proxy和Object.defineProperty()的不同

  • Proxy能监视到更多对象操作,Object.defineProperty()它只能监视属性的读写,Proxy是以非侵入的方式监管了对象的读写
//Proxy 以非侵入的方式监管了对象的读写
const person = {
    name:'cjr',
    age: 20
};
const personProxy  = new Proxy(person, {
    //监视属性删除的deleteProperty方法
    deleteProperty(target, property) {
        console.log('delete:',property);
        delete target[property];
    }
});
delete personProxy.age;//删除生成的代理对象属性
console.log(person);// {name:'cjr'}

//Object.defineProperty()
const person2 = {};
Object.defineProperty(person2,'name',{
    get() {
        console.log('name被访问');
        return person2._name;//属性名称前加_标识私有属性
    },
    set(value) {
        console.log('name被设置');
        person2._name = value;
    }
});
Object.defineProperty(person2,'age',{
    get() {
        console.log('age被访问');
        return person2._name;
    },
    set(value) {
        console.log('age被设置');
        person2._age = value;
    }
});
person2.name = 'jack';
console.log(person2.name);
//对比两种方式,Proxy更加简洁合理
  • Proxy更好的支持数组对象的监视
const list = [];
const listProxy = new Proxy(list, {
    set(target, property ,value) {
        console.log('set',property, value);//set 0 100,set length 1 这里的property为索引值
        target[property] = value;
        return true;//表示设置成功
    }
});
listProxy.push(100);

Reflect

  • Reflect属于一个静态类,无法调用new的方式构建一个实例对象,只能通过调用静态类的一些静态方法,封装了13个对对象的底层操作
  • Reflect最大的意义是提供了一套统一的操作对象的API
/*
	Reflect.apply(target, thisArg, args)
	Reflect.construct(target, args)//等同于new target(...args),这提供了一种不使用new来调用构造函数的方法
	Reflect.get(target, name, receiver) //查找并返回target对象的name属性,如果没有该属性,则返回 undefined 
	Reflect.set(target, name, value, receiver)//设置 target 对象的 name 属性等于 value
	Reflect.defineProperty(target, name, desc)//基本等同于 Object.defineProperty,后者即将被废弃
	Reflect.deleteProperty(target, name)//等同于 delete obj[name]
	Reflect.has(target, name)//对应 name in obj 里面的 in 运算符
	Reflect.ownKeys(target)//方法用于返回对象的所有属性(包含Symbol)
	Reflect.isExtensible(target)
	Reflect.preventExtensions(target)
	Reflect.getOwnPropertyDescriptor(target, name)
	Reflect.getPrototypeOf(target)
	Reflect.setPrototypeOf(target, prototype)
*/

class(类)

  • class关键字声明一个类型
  • static关键字声明静态方法
//传统创建构造对象的方法
/*function Person(name) {
    this.name = name;
}
Person.prototype.say = function() {
    console.log(`hi ,my name is ${this.name}`);
}*/
class Person{
    // 通过constructor定义构造方法
    constructor(name){
        this.name = name;
    }
    // 实例方法
    say(){
		console.log(`${this.name}说话了`);
    }
    // 静态方法,使用static关键字添加静态方法
    static create(name){
        console.log(this);//静态方法中的this指向不是当前实例对象,而是当前类型Person
        return new Person(name);
    }
}
const tom =	Person.create('tom');//调用静态方法
tom.say();
const jack = new Person('jack');//创建对象实例
jack.say();

  • 使用extends来对类进行继承
class Person{
    constructor(name){
        this.name = name;
    }
    say(){
		console.log(`${this.name}说话了`);
    }
    showName(){
        console.log(`父类的方法:${this.name}`);
    }
}
class Student extends Person{
    constructor(name, number){
        // 使用super来调用父类的构造函数来继承父类
        super();
        this.number = number;
    }
    hello() {
        super.say();//调用父类的方法
        console.log(`my school number is${this.number}`)
    }
    // 父类方法中的重写
    showName(){
        console.log(`子类的方法:${this.name}`);
    }
}
const s = new Student('jack', 100);
s.hello();//打印 jack说话了  my school number is 100

Set数据结构

  • 新的数据结构Set**,类似于数组,成员的值是唯一的**,没有重复的,可用于去除重复数组
//数组去重
const s1 = new Set([1, 22, 22, 31, 21, 34, 1]);
console.log(s1);
console.log(s1.size); //5
console.log([...s1]); //[1, 22, 31, 21, 34]
let set = new Set();
let a1 = NaN;
let b1 = NaN;
set.add(a1);
set.add(b1);
// 在这里NaN是相等的,但2和'2'是不等的,{}和{}也是不等的
console.log(set); //Set(1) {NaN}

/*
 * Set的几个实例方法
 * add(value),添加方法,返回整个Set结构,因此可以链式调用add
 * delete(value),删除方法,返回布尔值确定是否删除成功
 * has(value),返回布尔值表示该值是否为Set的成员
 * clear():清除所有成员没有返回值
 */
let s2 = new Set();
s2.add(1).add(2).add(2);
console.log(s2); //Set(2) {1, 2}
// 注意2被加入了两次
console.log(s2.size); // 2
console.log(s2.has(1)); // true
console.log(s2.has(2)); // true
console.log(s2.has(3)); // false
console.log(s2.delete(2)); //true
console.log(s2.has(2)); // false

Map数据结构

  • Map能用任意类型的值作为键名,而对象使用了都会默认转为字符串
const obj = {};
obj[true] = 'value';
obj[123] = 'value';
obj[{a:1}] = 'value';
console.log(Object.keys(obj));//['true','123','[object object]'],键名都被转化为字符串,无法与原来相同
//因此引入了Map
const m = new Map();
const tom = {name:'tom'};
m.set(tom,90);
console.log(m);// Map:{ {name:'tom'} => 90 }
console.log(m.get(tom));// 90

//Map实例的方法
const m1 = new Map();
const o1 = {
    p: 'Hello world'
};
m1.set(o1, 'content');
console.log(m1);
m1.get(o1);
console.log(m1.get(o1));//content
console.log(m1.has(o1));//true
console.log(m1.delete(o1));//true
console.log(m1.has(o1));//false

Symbol(全新的基本数据类型)

  • Symbol对象是新的一种数据类型,标识独一无二的值,它是JavaScript的第七种数据类型(Undefined,Null,Number,String,Boolean,Object,Symbol)
const cache ={};
//在a.js中
cache['foo'] = Math.random();
//在b.js中,键名在没有约定的时候容易重复,因此引入了Symbol
cache['foo'] = 123;

const obj = {};
obj[Symbol()] = '123';
obj[Symbol()] = '456';
console.log(obj);//{Symbol(): "123", Symbol(): "456"}
  • Symbol使用
/*
 * Symbol.for()它接受一个字符串作为参数,如果不是字符串则默认转为字符串,然后搜索有没有以该参数作为名称的Symbol 值。如果有,就返回这个 Symbol 值,否则就新建并返回一个以该字符串为名称的 Symbol 值。
 * Symbol.keyfor()会返回一个已被登记的Symbol类型值的key
 * Symbol.for()和Symbol()这两个方法前者会被登记在全局环境*搜索,后者不会
 * Symbol作为名称的属性不会被常规方法遍历到,
 */
const s1 = Symbol.for('foo');
const s2 = Symbol.for('foo');
console.log(s1===s2);//true
console.log(Symbol.for(true) === Symbol.for('true'));//true
console.log(Symbol.keyFor(s2)); // "foo"
const obj = {};
let a = Symbol('a'),
    b = Symbol('b');
obj[a] = 'hello';
obj[b] = 'world';
const objectSymbols = Object.getOwnPropertySymbols(obj); //获取指定对象的所有Symbol属性名
console.log(objectSymbols); // [Symbol(a), Symbol(b)]

Iterator遍历器接口机制

  • 概念:Iterator是一种接口机制,为不同的数据结构提供了统一的访问机制
  • 只要数据实现了Iterator接口就能被for…of遍历,当使用for…of去遍历数据的时候,该数据会自动去找Symbol.iterator属性,该属性指向对象的默认遍历器方法
//浏览器打印这些数据,查看原型链__proto__是否有Symbol.iterator,有的就是实现了Iterator接口
console.log({});//没有
console.log([]);//有
console.log(new Map());//有
console.log(new Set());//有
const arr = [1,2,3];
const iterator = arr[Symbol.iterator]();
iterator.next();
iterator.next();
iterator.next();
iterator.next();

ES6新特性

  • 实现Iterator接口

  • 工作原理:1)创建一个指针对象(遍历器对象),指向数据结构的起始位置

    2)第一次调用next方法,指针自动指向数据结构的第一个成员

    3)接下来不断调用next方法,指针会一直往后移动直到找到最后一个成员

    4)每次调用next方法返回的是一个包含value和done的对象,{value:当前成员的值,done:布尔值}

    5)done表示当前的数据结构是否遍历结束

const obj1 ={
    [Symbol.iterator]:function() {
        return {
	    	next:function(){
			 return {
                 value:'cjr',
                 done:true
             }
            }
        }
    }
};
// 在指定数据部署了iterator接口
const obj2 = {
    store:['foo','bar','baz'],
    // iterator接口实现
    [Symbol.iterator]:function() {
        let index = 0,
            self = this;
        // 返回next方法
        return {
	    	next:function(){
                //返回一个包含value和done的对象
			 const result = {
                 value:self.store[index],
                 done:index>=self.store.length
              };
              index++;
              return result;
            }
        }
    }
}
for(let item of obj2) {
	console.log(item);
}

n {
next:function(){
return {
value:‘cjr’,
done:true
}
}
}
}
};
// 在指定数据部署了iterator接口
const obj2 = {
store:[‘foo’,‘bar’,‘baz’],
// iterator接口实现
[Symbol.iterator]:function() {
let index = 0,
self = this;
// 返回next方法
return {
next:function(){
//返回一个包含value和done的对象
const result = {
value:self.store[index],
done:index>=self.store.length
};
index++;
return result;
}
}
}
}
for(let item of obj2) {
console.log(item);
}


相关标签: javascript es6