导航菜单
  • 首页
  • 首页>前端万博manbext备用网址>JavaScript万博manbext备用网址

    JavaScript(ES6)的常用特性!(下)

    现在几乎都是使用ES6了,浏览器要兼容的时代随着移动端的统一归于平静,想起以前轰轰烈烈的浏览器大战,为了兼容性痛苦不堪的年代,这一切都过去了。况且随着脚手架等自动化构建工具的使用,直接写最新最酷的代码,再babel一下,放心大胆的用。

    所以,我也建议直接用最新版本的语法,不管是语法糖,还是效率,用起来都更舒心。

    1、Symbol

    ES6为JavaScript引入了一个新的原生类型:Symbol,但是,和其他原生数据类型不一样,symbol没有字面量形式。作为构造函数来说它并不完整,因为它不支持语法:"new Symbol()"。

    下面是创建Symbol的过程:

    var sym = Symbol('something');
    console.log(typeof sym); //symbol
    console.dir(Symbol)

    注意事项:

  • 不能也不应该对Symbol()使用new。它不是一个构造器,也不会创建一个对象。

  • 传给Symbol(...)的参数是可选的,如果传入了的话,应该是一个为这个symbol的用途给出用户友好描述的字符串。

  • typeof的输出是一个新的值"symbol",这是识别symbol的首选方法。

  • 两个Symbol永远不会相等

    console.log(Symbol('name') === Symbol('name')); // false

    1.1 为对象添加私有成员

    利用Symbol不重复的特性,可以为对象添加不重复的键名。

    每个从 Symbol() 返回的 symbol 值都是唯一的。一个 symbol 值能作为对象属性的标识符;这是该数据类型的目的。

    //使用 Symbol 为对象添加不重复的键
    
    const obj = {}
    obj[Symbol()] = '123'
    obj[Symbol()] = '456'
    console.log(obj)
    
    //也可以在计算属性名中使用
    
    const obj = {
      [Symbol()]: 123
    }
    console.log(obj)

    利用两个Symbol不相等的特性,可以为对象创建私有成员,外部不能访问。

    const axisName = Symbol('obj的别名');
    const obj = {
        // 利用这种方式可以创建私有成员,外部不能访问
        [Symbol('obj的id')]: 1,
        [Symbol('obj的name')]: 'obj',
        [axisName]: 'myObj',
        url: 'http://www.xxx.com'
    
    }
    // 外部不能访问,因为没有两个Symbol是一样的。
    console.log(obj[Symbol('obj的id')]);
    console.log(obj[axisName]);
    console.log(obj.url);

    常规的方式不能获取Symbol属性名

    // for...in遍历不出Symbol,仅包含了以字符串为键的属性
    for(const key in obj){
        console.log(key)
    }
    // 也不能返回Symbol,仅包含了对象自身的、可枚举的、以字符串为键的属性
    console.log(Object.keys(obj))
    // 当使用 JSON.stringify() 时,以 symbol 值作为键的属性会被完全忽略:
    console.log(JSON.stringify(obj))

    ES6的Object专门提供了一个方法用于获取对象的Symbol属性。

    //其包含的是以 Symbol 为键的属性
    console.log(Object.getOwnPropertySymbols(obj)); //[Symbol(obj的id), Symbol(obj的name), Symbol(obj的别名)]

    1.2 内置的Symbol属性

    除了自己创建的 symbol,JavaScript 还内建了一些在 ECMAScript 5 之前没有暴露给开发者的 symbol,它们代表了内部语言行为。它们可以使用以下属性访问:

    1、迭代 symbols Symbol.iterator

    一个返回一个对象默认迭代器的方法。被 for...of 使用。

    object默认没有这个迭代属性,所以不能使用for...of,但是可以自己定义

    //通过数组可以观察这个迭代器
    const arr = [1, 2, 3];
    console.log(arr[Symbol.iterator]); // f()
    const it = arr[Symbol.iterator](); //{next: f(){}}
    console.log(it.next()); //{value: 1, done: false}
    console.log(it.next())
    console.log(it.next())
    console.log(it.next()); //{value: undefined, done: true}
    const myObj = {
        name: '诸葛',
        age: 18,
        address: '卧龙岗',
        [Symbol.iterator]: function () {
            const _this = this;
            let num = 0;
            const keys = Object.keys(_this);
            return {
                next: function () {
                    return {
                        value: _this[keys[num++]],
                        done: num > keys.length
                    }
                }
            }
        }
    }
    //现在可以使用for...of迭代obj了
    for(const value of myObj){
        console.log(value);
    }

    2、Symbol.replace

    一个替换匹配字符串的子串的方法。被 String.prototype.replace() 使用。

    3、Symbol.split

    一个在匹配正则表达式的索引处拆分一个字符串的方法.。被 String.prototype.split() 使用。

    更多了解:

    https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Symbol

    2、Set数据结构

    Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。

    Set对象是值的集合,Set 中的元素只会出现一次,即 Set 中的元素是唯一的。

    NaN 和 undefined 都可以被存储在 Set 中,NaN 被视为相同的值(NaN 被认为是相同的,尽管 NaN !== NaN)。

    2.1 创建一个set数据结构的实例

    //Set 构造函数能创建 Set 对象实例
    const mySet = new Set(); // Set(0) {size: 0}

    参数iterable 可选,如果传递一个可迭代对象,它的所有元素将不重复地被添加到新的 Set 中。如果不指定此参数或其值为 null,则新的 Set 为空,返回一个新的 Set 对象。  

    const mySet1 = new Set([1,2,2,3,3,4,5]);
    console.log(mySet1);//Set(5) {1, 2, 3, 4, 5}

    2.2 实例的方法

    2.2.1 添加add(value)

    如果 Set 对象中没有具有相同值的元素,则 add() 方法将插入一个具有指定值的新元素到 Set 对象中。 并返回Set 对象本身,因此可以链式调用。

    const mySet = new Set();
    mySet.add(1).add(true).add('Tom').add(18)
    console.log(mySet); //Set(4) {1, true, 'Tom', 18}

    2.2.2 移除某个值delete(value)

    delete() 方法从 Set 对象中删除指定的值(如果该值在 Set 中)。

    成功删除返回 true,否则返回 false

    console.log(mySet.delete('Tom')); // true
    console.log(mySet.delete('daisy'));//false

    从Set中删除对象。

    因为对象是通过引用比较的,所以如果没有对原始对象的引用,就必须通过检查单个属性来删除它们。

    const setObj = new Set();
    setObj.add({ x: 5, y: 20 }).add({ x: 20, y: 30 });
    // 删除任何x > 10 的对象 
    setObj.forEach(point => {
        if (point.x > 10) {
            setObj.delete(point);
        }
    })
    console.log(setObj)

    2.2.3 移除所有元素 clear()

    clear() 方法移除 Set 对象中所有元素。 返回值为undefined.

    mySet.clear()

    2.2.4 has(value)

    has() 方法返回一个布尔值来指示对应的值是否存在于 Set 对象中。

    const setObj = new Set();
    setObj.add({ x: 5, y: 20 }).add({ x: 20, y: 30 }).add('circle');
    console.log(setObj.has({ x: 20, y: 30})); // false,不是一个对象的引用
    console.log(setObj.has('circle')); //true

    https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Set

    2.3 实例的属性

    size 属性将会返回 Set 对象中(唯一的)元素的个数。

    size 的值是一个整数,表示 Set 对象有多少条目。size 的 set 访问函数是 undefined;你不能改变这个属性。 也就是只能读不能写。

    2.4 遍历

    2.4.1 forEach()

    forEach() 方法对 Set 对象中的每个值按插入顺序执行一次提供的函数。

     setObj.forEach(item => console.log(item))

    2.4.2 for ... of

    for(const item of setObj){
        console.log(item);
    }

    2.5 应用场景

    2.5.1 和数组的转换

    // 数组去重
    const arr = [1,1,2,2,3,3,4,5];
    //const newArr = Array.from(new Set(arr));
    const newArr = [...new Set(arr)];
    console.log(newArr); // [1, 2, 3, 4, 5]

    2.5.2 和字符串相关

    let str1 = 'javascript';
    let str2 = 'JAvaScript';
    let s1 = new Set(str1);
    let s2 = new Set(str2);
    //大小写敏感
    console.log(s1); //Set(9) {'j', 'a', 'v', 's', 'c', …}
    console.log(s2); //Set(10) {'J', 'A', 'v', 'a', 'S', …}

    3、Map数据结构

    Map 对象保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者基本类型)都可以作为一个键或一个值。

    3.1 对键值的操作

    // 创建一个map实例
    const myMap = new Map();
    // 这种方式赋值不能改变map的数据结构,所以不推荐
    // myMap.name = 'mrszhao';
    // 正确的方式,使用set方法
    myMap.set('name', 'mrszhao');
    myMap.set('age', 18);
    myMap.set('city', '成都');
    myMap.set('city', '重庆');
    // 使用get方法获取键的值
    console.log(myMap.get('city'));
    // size属性获取键值对的数量
    console.log(myMap.size);
    // 删除键值对,返回布尔值
    myMap.delete('age');
    // 判断知否存在某个键,返回布尔值
    console.log(myMap.has('name'))
    console.log(myMap);

    注意:键名具有唯一性,重复设置,后面的值会覆盖前面的值,而且,键值对是根据设置的顺序保存的。

    3.2 键名可以是任意的数据类型

    object对象的键名只能是string和symbol,而map数据的键名可以是任意数据类型,包括复杂数据类型。

    //对象的键名会被转成字符串,对象转成字符串是[object Object]
    const obj = {
        [Symbol()]: 'mrszhaoObj',
        name: '诸葛',
        1: 1,
        true: true,
        [{a: 1}]: 'a'
    }
    console.log(obj);
    obj[{b: 1}] = 'b';
    console.log(Object.keys(obj))
    console.log(obj['[object Object]'])

    map的优势在于可以使用对象作为键名。

    const o = {b: 2};
    myMap.set({a: 1}, 'a');
    myMap.set(o, 'b');
    myMap.set(function a(){} , 1);
    myMap.set(true, 1);
    //对象的key只能出现一次  要用对象来设键名的时候,一定要在外面设变量
    // 因为{} === {} false
    console.log(map.get({a: 1})); //undefined
    console.log(map.get(o)); // 'b'

    3.3 map的遍历

    // 第一个参数是value,第二个参数是key
     myMap.forEach((value, key) => console.log(key, value));

    因为拥有迭代器属性,可以使用for...of

    // 遍历出来的item是一个包含key和value的数组
    // 使用数组的解构方式
    for(const [key, value] of myMap){
        console.log(key, value)
    }
    //只遍历键名。myMap.keys()返回一个有迭代器的对象,所以可以用for...of
    for(const key of myMap.keys()){
        console.log(key)
    }
    // 只遍历值
    for(const value of myMap.values()){
        console.log(value)
    }

    3.4 map和数组的转换

    const arrMap = [['key1', 'value1'], ['key2', 'value2']];
    //使用常规的 Map 构造函数可以将一个二维键值对数组转换成一个 Map 对象
    const myMap1 = new Map(arrMap);
    // const newArr = Array.from(myMap1);
    //最简单的是使用...扩展运算符展开对象,再放入数组。
       const newArr = [...myMap];

    更多Map和Object对象的差别,可以查看文档:

    https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Map

    4、Object相关

    4.1 Object.assign()

    Object.assign(target, ...sources)

    Object.assign() 方法将所有可枚举(Object.propertyIsEnumerable() 返回 true)的自有(Object.hasOwnProperty() 返回 true)属性从一个或多个源对象复制到目标对象,返回修改后的对象。

    如果目标对象与源对象具有相同的 key,则目标对象中的属性将被源对象中的属性覆盖,后面的源对象的属性将类似地覆盖前面的源对象的属性。

    const target = {
        a: 1,
        b: 2
    }
    const source1 = {
        a: 2,
        c: 3
    }
    const source2 = {
        a: 3,
        d: 4
    }
    const result = Object.assign(target, source1, source2);
    console.log(result, result === target);

    点击空白页面产生小球,点击小球删除自己。试试。看到空白页面使劲点。

        function Circle(option) {
          option = option || {};
          this.shape = {
            r: 30,
            bgColor: '#f60',
            x: 0,
            y: 0
          }
          Object.assign(this.shape, option);
        }
        Circle.prototype.removeCircle = function() {
          console.log('我被干掉了');
        }
    
        const oHTML = document.documentElement;
        oHTML.addEventListener('click', function (e) {
           var circle = new Circle({
            r: getRandom(20, 50),
            bgColor: `rgba(${getRandom(0, 255)},${getRandom(0, 255)},${getRandom(0, 255)}, ${Math.random()})`,
            x: e.x,
            y: e.y,
          })
          console.log(circle);
          const oDiv = document.createElement('div');
          oDiv.style.cssText = `width:${circle.shape.r * 2}px;height:${circle.shape.r * 2}px;background-color:${circle.shape.bgColor};border-radius:50%;position:absolute;left:${circle.shape.x - circle.shape.r}px;top:${circle.shape.y - circle.shape.r}px;transition:0.2s`;
          document.body.appendChild(oDiv);
          oDiv.addEventListener('click', function(e) {
            this.remove();
            circle.removeCircle();
            e.stopPropagation();
          })
        })
        function getRandom(min, max) {
          return Math.floor(Math.random() * (max - min + 1) + min);
        }

    4.2 Object.is()

    Object.is() 方法判断两个值是否为同一个值。

    Object.is(value1, value2);

    Object.is的判断规则和==和===都不同。

    console.log(Object.is(+0, -0), +0 === -0); // false true
    console.log(Object.is(NaN, NaN), NaN === NaN); //true false
    console.log(Object.is(undefined, null), undefined == null); //false true

    4.3 Object.create()

    Object.create() 方法用于创建一个新对象,使用现有的对象来作为新创建对象的原型(prototype)。

    比如下面这个万博manbext3.0首页登录,点击页面产生一个圆,点击圆本身,会弹出它的面积。试试

    核心代码:

    // 创建一个大类 圆,有半径,x坐标, y坐标三个属性
        function Circle(r = 20, x = 0, y = 0) {
          this.r = r;
          this.x = x;
          this.y = y;
        }
        // 有一个原型上的获取面积的方法
        Circle.prototype.getArea = function () {
          return Math.floor(Math.PI * (this.r ** 2));
        }
        // 创建一个子类,产生有背景颜色的圆
        function BgcolorCircle(bgcolor = '#000') {
          this.bgcolor = bgcolor;
          // 执行大类的构造函数,让子类也有这三个属性,并且有默认值
          Circle.call(this);
        }
        // 把大类的原型对象当作小类的原型对象的原型,小类的实例也可以访问大类原型上的方法
        BgcolorCircle.prototype = Object.create(Circle.prototype);
        // 如果不指定构造函数,会默认指向Circle上级对象
        BgcolorCircle.prototype.constructor = BgcolorCircle;
        
        const oHTML = document.documentElement;
        oHTML.addEventListener('click', function (e) {
          const cc = new BgcolorCircle();
          cc.r = getRandom(10, 100);
          cc.x = e.x;
          cc.y = e.y;
          cc.bgcolor = `rgba(${getRandom(70, 255)},${getRandom(70, 255)},${getRandom(70, 255)})`;
          const oDiv = document.createElement('div');
          oDiv.style.cssText = `width: ${cc.r * 2}px;height:${cc.r * 2}px;position:absolute;left:${cc.x - cc.r}px;top:${cc.y - cc.r}px;background-color: ${cc.bgcolor};border-radius:50%`;
          document.body.appendChild(oDiv);
          oDiv.addEventListener('click', function (e) {
            alert(`我的面积是:${cc.getArea()}`);
            e.stopPropagation();
          })
        })
        function getRandom(min, max) {
          return Math.floor(Math.random() * (max - min + 1) + min);
        }

    5、class类

    看看Class类这个语法糖有多甜。

    5.1 声明类

    // 类声明,没有提升,先声明再实例化
    class Circle {
        //用于创建和初始化一个由class创建的对象
        constructor(r = 20, x = 0, y = 0) {
            this.r = r;
            this.x = x;
            this.y = y;
        }
        // getter 只读属性
        get area() {
            return this.getArea();
        }
        // 原型方法
        getArea() {
            return (Math.PI * (this.r ** 2)).toFixed(2);
        }
    }
    
    const c = new Circle(50);
    console.log(c);
    console.log(c.area);
    console.log(c.getArea());

    5.2 静态属性和方法

    不能在类的实例上调用静态方法,而应该通过类本身调用。

    // 类声明,没有提升,先声明再实例化
    class Circle {
        //用于创建和初始化一个由class创建的对象
        constructor(r = 20, x = 0, y = 0) {
            this.r = r;
            this.x = x;
            this.y = y;
        }
        // getter 只读属性
        get area() {
            return this.getArea();
        }
        // 静态属性,只有类能访问,实例不能访问
        static proname = 'Circle';
        // 原型方法
        getArea() {
            return (Math.PI * (this.r ** 2)).toFixed(2);
        }
        // 静态方法
        static createDefaultCircle() {
            return new Circle();
        }
    }
    // 只能类访问静态属性和静态方法
    console.log(Circle.proname);
    console.log(Circle.createDefaultCircle());

    5.3 extends扩展子类

    extends 关键字用于类声明中,以创建一个类,该类是另一个类的子类。

    class ChildClass extends ParentClass { ... }
    // 使用extends扩展内置对象,产生一个内置对象的子类
    class myDate extends Date {
        constructor(date = new Date()) {
            // super 关键字用于调用对象的父对象上的函数。
            // 调用超类构造函数并执行。相当于Circle.call(this);
            // 并把子类的参数传递给父类。
            super(date);
        }
        getFormattedDate() {
            const week = ['星期天', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
            let date = this.getDate();
            let month = this.getMonth() + 1;
            let year = this.getFullYear();
            date = date < 10 ? '0' + date : date;
            month = month < 10 ? '0' + month : month;
            return `${year}.${month}.${date} ${week[this.getDay()]}`;
        }
    } 
    
    const mydate = new myDate();
    console.log(mydate.getFormattedDate());
    const mydate1 = new myDate('2023-10-1')
    console.log(mydate1.getFormattedDate());

    扩展Circle的子类对象

    class ColorCircle extends Circle {
        constructor(bgcolor = '#000') {
            // 如果子类中定义了构造函数,那么它必须先调用 super() 才能使用 this 。
            super();
            // console.log(super());//返回了子对象,后面才能使用this来指向子对象。
            this.bgcolor = bgcolor;
        }
    }
    
    const bgcircle = new ColorCircle('#f30');
    console.log(bgcircle);
    bgcircle.r = 30;
    console.log(bgcircle.area);
    
    class BorderCircle extends Circle {
        constructor(border = '1px solid #000') {
            super();
            this.border =border;
        }
    }
    
    const bordercircle = new BorderCircle();
    console.log(bordercircle);
    bordercircle.border = `5px solid ${bgcircle.bgcolor}`
    bordercircle.r = 40;
    console.log(bordercircle.area);

    现在Class类的写法比起以前原型的复杂写法看起来要清爽很多。

    还有一些高级的内容,比如Promise异步、代理等等,后面有时间再写了。

    点赞


    2
    保存到:

    相关文章

    发表评论:

    ◎请发表你卖萌撒娇或一针见血的评论,严禁小广告。

    Top