美文网首页
JS新特性

JS新特性

作者: hellomyshadow | 来源:发表于2019-04-21 11:03 被阅读0次

    ES

    JS包含三个部分:ECMAScript(核心)、扩展浏览器端、扩展服务器端
    1. ECMAScript:2009年发布ES5,2015年发布ES6,2016年发布ES7
    2. 扩展浏览器端:BOM(浏览器对象模型)、DOM(文档对象模型)
    3. 扩展服务器端:Node.js
    

    ES5

    1. 严格模式:除了正常运行模型(混杂模型),ES5增加了严格模式(strict mode)
        1. 消除JS语法的一些不合理、不严谨、不安全之处,减少一些怪异行为;
        2. 使用:在全局/函数的首行定义'use strict'; 即使不支持,也没有任何副作用;
    2. 严格模式的语法和行为:
        1. 变量必须用var声明,混杂模式下,如果不使用,则升级为全局变量,即添加给window
        2. 禁止自定义函数中的this指向window
        function Person(name) { this.name = name; }
        Person('Mack') --> 直接调用构造函数,则this指向window,严格模式不允许
        new Person('Mack') --> 创建实例对象,则this指向当前的实例对象
        3. 创建eval作用域:var str = 'Mack';
        eval('var str = "Any"; alert(str);'); --> var str = "Any"; alert(str);
        alert(str); --> 非严格模式下,str='Any',那么就可以借助eval()攻击
        4. 对象不能有重名的属性。
    3. 为Object扩展一些静态方法,如create()、defineProperties()、defineProperty()
        var obj = { name: 'Mack', age: 30 }
        1. create(prototype, [descriptors]):以指定对象为原型,创建新的对象;
        var obj2 = {};  var obj4 = {};
        obj2 = Object.create(obj); --> obj2仍是空的,但其隐式原型指向obj
        obj4 = Object.create(obj, {
            sex: {  --------------> 扩展属性
                value: 'Male', ---> 属性值
                writale: true, ---> 当前属性值是否可修改,默认为false
                configurable: true, ---> 当前属性是否能被删除,默认为false
                enumerable: true ------> 当前属性能否被for-in遍历,默认为false
            }
        })
        2. defineProperties(object, props):为指定对象定义扩展属性。
        Object.defineProperties(obj, {
            job: { ---> 扩展新的属性
                get: function() {  --> 访问扩展属性时回调,计算当前属性值
                    return this.name + ':' + this.age;
                },
                set: function(data) { --> 修改属性时回调,用于监视属性的变化
                    var n = data.split(':')
                    this.name = n[0];  this.age = n[1]
                },
            }
        })
        console.log(obj.job) --> 回调get():'Mack:30'
        obj.job = 'Any:16' --> 回调set():name='Any',age=16
        2. getter和setter都是惰性执行的,只有在访问/修改属性时才会调用,直接打印obj并不会
        直接显示job的属性值。
        3. 对象本身也有getter和setter,其实defineProperties()中调用的也是它们;
        var obj = { name: 'Mack', age: 30,
            get job() {
                return this.name + ':' + this.age;
            },
            set job(data) {
                var n = data.split(':')
                this.name = n[0];  this.age = n[1]
            }
        }
        4. defineProperty(obj, prop, descriptor):参数3为将被定义或修改的属性描述符
    4. 数组Array扩展的方法
        1. indexOf(value)/lastIndexOf(value):返回元素的索引;
        2. forEach(function(item, index){ ... }):遍历数组;
        3. map(callback):计算每一个数组元素,并不会影响原数组,而是返回一个新的数组;
        4. reduce(function(arg1, arg2){ ... }):两两计算,返回最终结果;
        5. filter(callback):过滤数组的元素,返回一个新的数组;
        6. find(callback)、findIndex(callback):返回第一个为true的数组元素、数组角标;
        7. sort(callback)、every(callback)、some(callback);
    5. 函数扩展的方法:call(),apply(),bind()
        1. call/apply/bind()都能修改函数中的this指向;
        2. bind(obj):让函数内的this指向obj,但不会执行函数,而是将函数返回;
        3. bind()传递参数的方式与call()相同,通常用于修改回调函数中的this指向。
    

    ES6

    1. 声明变量:let、const
        1. let:块级作用域,不能重复声明,不会预处理,不存在变量提升;
        2. const:与let类似,但它定义的是常量,不能修改;
    2. 变量的结构解析(解构赋值):从对象/数组中提取数据,并赋值给一个/多个变量;
        1. 对象:let obj = { name: 'Mack', age: 12 }
        let {name, age} = obj; -->变量名必须与对象中的属性名保持一致,位置可以任意排列
        console.log(name, age); --> name='Mack', age=12
        let {name} = obj; --> 需要哪些属性,就获取哪些属性
        2. 数组:let arr = [ 1, 3, 5, false, 'hello' ]
        let [ a, b ] = arr; --> 变量值与位置有关,与数组的下标对应
        let [,,, a, b] = arr; --> a=false, b='hello'
    3. 模板字符串:``和${xxx}
        1. `` 是模板字符串的标识,${xxx} 是变化的部分,简化字符串的拼接
        2. let str = `My name is ${obj.name}`  --> 'My name is Mack'
    4. 对象属性/方法的简写方式:let name = 'Mack'
        let obj = { name,  ---> 等同于 name: name,同名属性可以省略
            getName() { ... }, --> 等同于 getName: function() { ... }
        }
    5. 形参默认值:function test(x=1, y=2, z) { ... }
    

    箭头函数

    1. 箭头函数:(arg1, arg2,) => { ... },类似于lambda表达式
        1. 无参数:let fun = () => console.log('Hello JS');
        fun(); --> fun指向箭头函数,fun()调用箭头函数
        2. "=>"后是单条语句时,可省略"{}",且该语句执行的结果会作为函数的返回值;
        3. 一个参数:let fun = a => console.log(a);
        4. 多个参数:let fun = (a, b) => console.log(a, b);
        5. 多条语句时,"{}"不能省略,函数的返回值仍默认为undefined
        let fun = () => {
            return 'Hello World';  --> 手动指定返回值
        }
    2. 箭头函数的this:
        1. 箭头函数没有自己的this,它的this不是调用时决定的,而是在定义时所处的环境决定的;
        2. 如果箭头函数直接定义在一个对象/函数中,那么箭头函数的this就是该对象/函数的this,
        否则,箭头函数的this指向window;
        3. 也就是说,箭头函数的this取决于箭头函数所定义的外层空间;
        element.onclick = function() { ...// this就是element对象 }
        element.onclick = () => {
            // 箭头函数定义在全局作用域,也即定义在window中,那么this指向window
        }
    3. 可变参数:...rest,用于取代arguments
        1. arguments只是类似于数组,但并不具备数组的方法,如forEach()
        2. ...rest只接收多余的实参,而且是一个数组;
        function(a, b, ...rest) { --> 必须放在参数的最后位置
            rest.forEach((item, index) => { ... }) --> 遍历多余参数
        }
        3. 扩展应用-三点运算符:let arr = [2, 3, 4];
        let arr2 = [1, ...arr, 5] --> [1, 2, 3, 4, 5]
        console.log(...arr2) --> 1 2 3 4 5,三点运算符会自动遍历数组元素
        4. 三点运算符还可以浅拷贝复制数组和对象
        let copyArr = [...arr]
        let obj = { name:'Mack' } ---> let copyObj = { ...obj }
    

    Promise

    1. Promise对象:表示未来某个将要发生的事件,通常是一个异步操作;
    2. Promise可以将异步操作以同步的方式表达出来,避免了层层嵌套的回调(回调地狱);
    3. 使用Promise:Promise是ES6的一个构造函数
        1. 创建Promise对象:
        let promise = new Promise((resolve, reject) => {
            //Promise为初始化状态:pending
            ...... //执行异步操作
            if(异步操作成功) { resolve(value); } -->修改Promise为成功状态:fullfilled
            else { reject(errMsg); } --> 修改Promise为失败状态:rejected
        });
        2. 调用Promise的then()
        promise.then(result => { -->执行resolve(),则回调then()的第一个函数
                console.log(result);
            }, errMsg => {  --> 执行reject(),则回调then()的第二个函数
                console.log(errMsg);
            }
        ) --> 回调参数result、errMsg分别是resolve()、reject()传递的value、errMsg
        3. 封装AJAX请求:
        fuction getNews(url) {
            let promise = new Promise((resolve, reject) => {
                let http = new XMLHttpResponse();
                http.onreadystatechange = function() {
                    if(http.readyState!==4) return;
                    if(http.status == 200) {
                        resolve(http.responseText); -->请求成功,修改状态
                    } else {
                        reject('暂时没有数据'); --->请求失败,修改状态
                    }
                };
                http.open('GET', url);  http.send(); -->发送请求
            });
            return promise;
        }
        getNews('http://').then(res => { ······
                return getNews(res.url); -->发送第二次AJAX请求,必须返回一个
            }, error => { ······ }   --------> Promise对象,then()可以链式回调;
        ).then(res => {  ----> 回调第二次AJAX的结果
            ······
        }, error => { ······ });
    

    Symbol属性

    1. ES5中对象的属性名都是字符串,容易重名,污染环境;
    2. Symbol是ES6新增的一种数据类型,它对应的值是唯一的;
    3. 不能与其他数据计算,包括字符串拼接,for-in/for-of不会遍历Symbol属性;
        let sym = Symbol();  --> Symbol不是构造函数,不用new创建
        let obj = { name: 'Mack' }
        obj[sym] = 'Hello'; --> 为对象添加Symbol属性,其属性值为'Hello'
    4. 创建Symbol时还可以传递参数,作为Symbol的标识;
        let sym = Symbol('Hello')
        const SYM = Symbol('key') ---> 定义为常量
    5. ES6还提供了11个内置的Symbol值,指向内部使用方法,如Symbol.iterator
    

    Iterator

    1. Iterator:一种接口机制,为各种不同的数据结构提供统一的访问机制;
    2. ES6提供了一种新的遍历方式:for-of,Iterator主要供for-of消费;
    3. 模拟Iterator的原理:let arr = [1, 4, 27, 'abc']
        function myIterator(arr) {
            let index = 0;  ---> 记录指针的位置
            return {  -----> 遍历器对象
                next: function() {
                    return index < arr.length ? {value:arr[index++], done:false}
                     : { value: undefined, done: true }
                }
            }
        }
        let iter = myIterator(arr);
        console.log(iter.next()); --> { value: 1, done: false }
        ......
        console.log(iter.next()); --> { value: 'abc', done: false }
        console.log(iter.next()); --> { value: undefined, done: true }
    4. 将Iterator接口部署到指定的数据类型上,则可以用for-of遍历;
        1. JS的原生数据类型中具备Iterator接口的有:String、Array、arguments、Set、Map
        2. for(let i of [1,2,3]) { ...//i就是每次循环的元素 }
    5. Symbol.iterator
        1. String、Array、arguments等对象具备Iterator接口,但普通对象并没有Iterator接口,
        所以不能使用for-of遍历;
        2. 为普通对象添加Symbol.iterator属性,指向其默认遍历器方法,for-of就能遍历该对象;
        3. Symbol.iterator属性添加给对象,就等同于为该对象部署了Iterator接口;
        let data = {
            [Symbol.iterator]: function() {
                let index = 0; ---> 记录指针的位置
                return { ... } ---> 遍历器对象
            }
        }
        4. 三点运算符和解构赋值,默认调用的就是Iterator接口。
    

    Generator

    1. Generator函数:ES6提供的解决异步编程的方案之一;
        function* test() {
            yield 'test1'
            yield 'test2'
        }
    2. 调用Generator函数,返回一个指针对象,由该对象调用next(),遇到yield返回;
        let gen = test();
        console.log(gen.next());  --> {value: "test1", done: false}
        console.log(gen.next());  --> {value: "test2", done: false}
        console.log(gen.next());  --> {value: undefined, done: true}
    3. 为普通对象添加Symbol.iterator属性,指向一个Generator函数;
        let obj = { name: 'Mack' }
        obj[Symbol.iterator] = function* gen() { -->为对象部署Iterator接口
            yield 1
            yield 2
        }
        for(let i of obj) {  ---> 具备Iterator接口的对象,for-of都可以遍历
            console.log(i)  --> 1 2
        }
    4. let result = yield 'test1'  --> yield的返回值默认是undefined
    5. next(arg):参数arg会先赋值给当前指针所在的yield,再向下移动;
        function getNews(url) {
            $.get(url, function(data) { --> jQuery-GET请求成功的回调
                HTTP.next(data.url);  ---> 获取新的URL,发送第二次请求
            });
        }
        function* sendHttp() {
            let url = yield getNews('http://...') --> 第一次next()
            yield getNews(url)  --> 第二次next(),需要传递参数
        }
        let HTTP = sendHttp(); ---> 获取遍历器对象
        HTTP.next();  --> 发送第一次GET请求
    

    async

    1. async函数:真正意义上解决回调地狱的问题,同步的方式表达异步操作;
    2. async函数的本质是Generator的语法糖,源自ES2016(ES7);
        1. async函数返回的总是Promise对象,用then()进行下一步操作;
        async function test() {  ---> 函数test变成一个异步函数
            return 'hello async';
        }
        test().then(res=>{
            console.log(res); -->hello async
        })
        2. async取代Generator函数的"*",await取代Generator函数的yield;
        async function test() {
            await 异步操作; --------> 该异步操作总是返回一个Promise对象
            await 异步操作;
        }
        3. async函数不需要像Generator函数那样去调用next(),遇到await就会等待,当前的异步操作
        完成后,则继续向下执行。
    3. async的使用方式
        1. 基本使用
        async function test() { return 'hello async'; }
        async function exec() {
            let t = await test(); --> 阻塞程序,等待异步方法test() 执行完成
            console.log(t)
        }
        2. async函数返回一个Pormise对象,await总是配合Promise使用,返回值由resolve()决定
        async function test() {
            let res = await new Promise((resolve, reject) => {
                    setTimeout(() => { resolve('Mack'); }, 2000);
                })
            console.log(res); --> 2000ms之后打印 Mack
        }
        3. await语句默认返回undefined,resolve()可以为awiat传递参数,reject()表示异步任务
        失败,会终止继续向下执行。
    4. 发送AJAX请求:
        function getNews(url) {
            return new Promise((resolve, reject) => {
                $.ajax({ method: 'GET', url,
                    success: data => resolve(data), --> GET请求成功
                    error: error => resolve(error) --> 请求失败不使用reject()
                }) ------> 那么await会继续向下执行,由此判断成功还是失败
            })
        }
        async function sendHttp() {
            let res = await getNews('http://...')
            if(res) { --> 判断res的状态
                res = await getNews(res.url) --> 继续执行异步任务
            } else { alert('请求失败') --> 请求失败,则提示给用户 }
        }
        sendHttp();
    

    其他新增

    1. class:可以定义类、实现类的继承,通过类中的constructor定义构造方法;
        1. new 创建类的实例对象,extends实现类的继承,super调用父类的构造方法;
        class Person {
            constructor(name, age) {  ---> 构造函数
                this.name = name;  this.age = age; --> 定义属性
            }
            showName() {   -------------> 类的方法必须是简写形式
                console.log(this.name);
            }
            static run() {  -----------------> 静态方法
                console.log('static method');
            }
        }
        2. 创建实例对象:let p = new Person('Mack', 18)
        3. 静态方法可以直接通过类名调用:Person.run();
        4. extends:类的继承
        class Stu extends Person {
            constructor(name, age, job) {
                super(name, age);  ------> 必须先初始化父类的构造方法
                this.job = job;
            }
            showName() {  ...... } ---> 重写父类的方法
        }
    2. 字符串的扩展
        1. includes(str):判断是否包含某个字符串;
        2. startsWidth(str)/endsWidth(str):判断是否以某个字符串开头/结尾;
        3. repeat(count):把当前字符串拼接count次,并返回;
    3. number的扩展
        1. 二进制与八进制的数值表示:0b、0o
        2. Number.isFinite(num):判断num是否是一个有限大的是数字;
        3. Number.isNaN(num)/isInteger(num):判断num是否是NaN/整数;
        4. Number.parseInt(str):把字符串转为数值;
        5. Math.trunc(num):取整,去除小数部分。
    4. Array的扩展
        1. Array.from(v):将伪数组/可遍历对象转为真实的数组,包括Set、Map、字符串
        低于ES6的版本:Array.prototype.slice.call(v)
        2. Array.of(v1, v2, v3):将一系列的值转为一个数组[v1, v2, v3]
        3. find/findIndex((value, index, arr) => { return true })
        查找第一个满足条件返回true的元素/元素下标:let arr = [2, 3, 5, 7, 1]
        let res = arr.find(function(v, i) { return v > 4 }) --> 5
    5. 对象方法的扩展
        1. Object.is(v1, v2):判断2个数据是否完全相等;,Object.is(NaN, NaN) -->true
        Object.is(0, -0) --> false,其实该方法是按照字符串的标准比较的
        2. Object.assign(target, s1,s2,...):将源对象s1、s2...的属性复制给目标对象target
        let s1 = { name:'Mack', job:'Teacher' };  let s2 = { name:'JJJ', age:20 };
        let target1 = {}
        let target2 = Object.assign(target, s1,s2); ---> s2的name属性会覆盖s1的name属性
        --> target1和target2:{ name:'JJJ', age:20, job:'Teacher' }
        3. 直接操作隐式原型__proto__,ES6之前不能访问隐式原型。
    6. 深度克隆
        1. 浅拷贝:拷贝的是引用,修改拷贝后的数据,会影响原数据;
        2. 深拷贝对象/数组:JSON.parse(JSON.stringify(data))
        3. 三点运算符、Object.assign(target, s1,s2) 都是浅拷贝
    

    Set、Map、WeakSet、WeakMap

    1. Set:无序、不可重复的、多个value的集合体;
        1. let set = new Set(); --> set.size:value的个数
        2. let set = new Set(array); -->会去除array中的重复数据;
        3. 添加、删除、清空:add(value)、delete(value)、clear()
        4. has(value):判断value是否存在。
    2. Map:无序的、key不重复的、多个key-value的集合体;
        1. let map = new Map(); --> set.size:key的个数
        2. new Map([[1], [4,5], [7,8,9]]) -->{ 1=>undefined, 4=>5, 7=>8 }
        3. 添加/修改、获取value、删除:set(key, value)、get(key)、delete(key)
        4. has(key):判断key是否存在;  clear():清空。
    3. WeakSet和WeakMap设计的目的是不干扰垃圾回收机制,避免内存泄漏。
        1. WeakSet存放的元素、WeakMap的键 都必须是对象,且它们对该对象的引用是弱引用。
        2. 正常情况下,当引用一个对象时,垃圾回收机制的计数器会 +1 ,但弱引用不会!
    
        let dom = document.getElementById('example')  // Dom对象的引用计数器 +1  --> 1
        let set = new Set()
        set.add(dom)  // Dom对象的引用计数器 +1  --> 2
        let weakset = new WeakSet()
        weakset.add(dom)  // Dom对象的引用计数器不变  --> 2
    
    4. 当访问弱引用指向的对象时,该对象可能已经被回收了。正因如此,WeakSet和WeakMap是不可枚举的(不可遍历)!
    

    ES7

    1. 指数运算符:3**3 = 27
    2. Array.prototype.includes(value):判断数组中是否包含value
    

    相关文章

      网友评论

          本文标题:JS新特性

          本文链接:https://www.haomeiwen.com/subject/klmdgqtx.html