• JavaScript 数据结构与刷题基础


    零、引言-JS中8种数据类型

    参考: JS中8种数据类型 - 不知名前端李小白 - 博客园

    0.1. 按照类型来分有基本数据类型和引用数据类型

    JS数据类型
    值类型(基本类型):字符串(String)、数字(Number)、布尔(Boolean)、空(Null)、未定义(Undefined)、Symbol、BigInt。

    引用数据类型(对象类型):对象(Object)、数组(Array)、函数(Function),还有两个特殊的对象:正则(RegExp)和日期(Date)。

    其中Object是个大类,function函数、array数组、date日期…等都归属于Object。

    • 在ES5的时候,我们认知的数据类型确实是 6种:Number、String、Boolean、undefined、object、Null
    • ES6 中新增了一种 Symbol 。这种类型的对象永不相等,即始创建的时候传入相同的值,可以解决属性名冲突的问题,做为标记。
    • 谷歌67版本中还出现了一种 bigInt。是指安全存储、操作大整数。

    0.2 基本数据类型和引用数据类型的区别

    1. 声明变量时不同的内存分配
    • 基本数据类型由于占据的空间大小固定且较小,会被存储在栈当中,也就是变量访问的位置
    • 引用数据类型存储在堆当中,变量访问的其实是一个指针,它指向存储对象的内存地址
    1. 正是因为内存分配不同,在复制变量时结果也不一样
    • 基本数据类型复制后2个变量是独立的,因为是把值拷贝了一份
    • 引用数据类型则是复制了一个指针,2个变量指向的值是该指针所指向的内容,一旦一方修改,另一方也会受到影响
    1. 参数传递不同
    • 基本数据类型把变量里的值传递给参数,之后参数和这个变量互不影响
    • 引用数据类型(这里拿函数举例):虽然函数的参数都是按值传递的,但是引用值传递的值是一个内存地址,实参和形参指向的是同一个对象,所以函数内部对这个参数的修改会体现在外部

    0.3 JS中的深浅拷贝

    参考:【JS深拷贝】 js如何进行深拷贝? - 知乎

    一、JS数据结构与方法操作

    1.1 数组

    数组存储了一系列同一种数据类型的值,虽然在JavaScript中,也可以在数组中保存不同类的值,但我们还是需要遵守最佳实践,避免这样做。

    • 创建和初始化
    new Array(7)         // 创建一个长度为7的数组
    new Array(1, 2, 3)   // 创建一个数组,其元素为1, 2, 3
    let arr2 = [1, 2, 3]
    
    • 1
    • 2
    • 3
    • 添加元素
    numbers[numbers.length] = 10; 
    
    numbers.push(10)// 数组尾添加
    
    numbers.unshift(0); // 数组头添加  (所有的往后移动一位)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 删除元素
    numbers.pop(); //从末尾删除元素
    
    numbers.shift()// 从数组头删除元素
    
    • 1
    • 2
    • 3

    使用pop()方法,而通过push()方法和pop()方法,就能用数组来模拟栈结构。
    使用shift()方法和unshift()方法可以让数组模拟基本的队列数据结构。

    • 任意位置添加和删除元素
      使用splice()方法可以让我们在数组中的任意位置删除或添加元素,其参数为:
    • 第一个参数:表示想要删除或插入的元素的索引。
    • 第二个参数:表示删除元素的格式。
    • 第三个参数:表示添加到数组中的值。
    let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    numbers.splice(0, 1) // 表示:在索引为0处,删除一个元素
    console.log(numbers) // 结果:[2, 3, 4, 5, 6, 7, 8, 9, 10]
    numbers.splice(3, 2) // 表示:在索引为3处,删除两个元素
    console.log(numbers) // 结果:[2, 3, 4, 7, 8, 9, 10]
    numbers.splice(5, 0, 0, 1) // 表示:在索引为5处,添加0和1这两个元素
    console.log(numbers) // 结果:[2, 3, 4, 7, 8, 0,1,9, 10]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    注意:const使用时必须要初始化且不可以更改变量。【只可以添加数值】

    以下为 ES5方法:

    方法描述
    concat连接2个或者更多数组,并返回结果
    every对数组中的每一个元素运行给定的函数,如果该函数对每一个元素都返回true,则返回true
    filter对数组中的每一个元素运行给定的函数,返回该函数会返回true的元素组成的数组
    forEach对数组中的每一个元素运行给定的函数
    join将所有的数组元素以指定的字符链接成一个字符串
    indexOf返回第一个与给定参数相等的数组元素的索引,没有找到则返回-1
    lastIndexOf从数组末尾开始搜索,并返回第一个与给定参数相等的数组元素的索引,没有找到则返回-1
    map对数组中的每一个元素运行给定的函数,返回每次函数调用的结果组成的数组
    reverse颠倒数组中元素的顺序
    slice传入索引值,将数组里对应索引范围内的元素作为新数组返回
    some对数组中的每个元素运行给定的函数,如果任一元素返回true,则返回true
    sort按照元素的ASCII值进行排序
    reduce返回数组中所以元素值的合计
    toString将数组作为字符串返回
    valueOf和toString类似,将数组作为字符串返回

    以下为 ES6新增方法:

    方法描述
    @@iterator返回一个包含数组键值对的迭代器对象,可以通过同步调用的方式得到数组元素的键值对
    copyWhthin复制数组中的一系列元素到同一数组指定的起始位置
    entries返回包含数组所有键值对的@@iterator
    find根据回调函数给定的条件从数组中查找元素,如果找到则返回该元素
    findIndex根据回调函数给定的条件从数组中查找元素,如果找到则返回该元素的索引
    fill用静态值填充数组
    from将一个类数组转换为一个真正的数组
    of根据传入的参数创建一个新数组
    values返回包含数组中所以值的@@iterator

    ES7中新增数组方法:

    方法描述
    includes如果数组中存在某个元素,则返回true,否则返回false

    数组扩展方法使用:

    • 扩展运算符的使用

      • 深拷贝一维数组

        const list = [1, 2, 3, 4, 5];
        const list1 = [...list];
        console.log(list1);
        
        • 1
        • 2
        • 3
      • 分割数组

        const list = [1, 2, 3, 4, 5];
        const [, ...list1] = list;
        console.log(list1);
        //[2, 3, 4, 5]
        
        • 1
        • 2
        • 3
        • 4
      • 将数组转化成参数传递给函数

        const list = [1, 2];
        function xd(a, b) {
          console.log(a + b);
        }
        xd(...list);
        
        • 1
        • 2
        • 3
        • 4
        • 5
    • 新增的常用方法

      • fill(会改变原数组)

        const list = [1, 2, 3, 4, 5];
        const list1 = [...list].fill(6);
        const list2 = [...list].fill(6, 1, 4);
        console.log(list1);
        console.log(list2);
        // output: (5) [6, 6, 6, 6, 6]
        //         (5) [1, 6, 6, 6, 5]
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
      • find/findIndex

        const list = [
          { hobby: '吃饭', id: 1 },
          { hobby: '睡觉', id: 1 },
          { hobby: '敲代码', id: 1 },
          { hobby: '吃饭', id: 2 },
        ];
        let str = '';
        for (let i = 0; i < list.length; i++) {
          if (list[i].hobby === '吃饭') {
            str = list[i];
          }
        }
        console.log(str);
        
        const result = list.find(function (item) {
          return item.hobby === '吃饭';
        });
        const result1 = list.findIndex(function (item) {
          return item.hobby === '吃饭';
        });
        console.log(result, result1);
        
        // {hobby: '吃饭', id: 2}
        // {hobby: '吃饭', id: 1} 0
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
        • 24
      • flat

        const list = [1, 2, 3, ['2nd', 5, 6, ['3rd', 7, 8]]];
        const list1 = [].concat(...list);
        console.log(list1);
        const list2 = list.flat(2);
        console.log(list2);
        // 展开 铺平
        // (7) [1, 2, 3, '2nd', 5, 6, Array(3)]
        // (9) [1, 2, 3, '2nd', 5, 6, '3rd', 7, 8]
        
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
      • filter(改变长度过滤,返回数组)

        const list = [
          { hobby: '吃饭', id: 1 },
          { hobby: '睡觉', id: 1 },
          { hobby: '敲代码', id: 1 },
          { hobby: '吃饭', id: 2 },
        ];
        
        const result = list.filter(function (item) {
          return item.hobby === '吃饭';
        });
        console.log(result); // [{ hobby: '吃饭', id: 1 },{ hobby: '吃饭', id: 2 }]
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • map(改变内容)
          const list = [
            { hobby: '吃饭', id: 1 },
            { hobby: '睡觉', id: 1 },
            { hobby: '敲代码', id: 2 },
          ];
          const list1 = list.map(function (i) {
            // return {
            //   action: i.hobby,
            //   state: i.id === 1 ? '喜欢' : '沉迷',
            // };
            let obj = {};
            Object.assign(obj, i);
            obj.state = i.id === 1 ? '喜欢' : '沉迷';
            return obj;
          });
          console.log(list);
          console.log(list1);
          
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • find()
        find返回数组或类似结构中满足条件的第一个元素
    const posts = [
      {id: 1, title: 'Title 1'},
      {id: 2, title: 'Title 2'}
    ];
    // 找出id为1的posts 
    // find返回数组或类似结构中满足条件的第一个元素
    const title = posts.find(p => p.id === 1).title;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • some()

    some找到数组中符合条件的一项就不会在找,类似于find只找第一项。

    [1,2,3,4,5].some(v=>v>4) // true 符合有某一项满足条件
    
    • 1
    • every()

    every数组中每个条件都为真才会返回真。

    [1,2,3,4,5].every(v=>v>1) // false 数组中每一项都大于1才会返回true 
    
    • 1
    • reduce()

    当你想要将多个数据放进一个实例中时,你可以使用一个reducer.
    传给reduce的第一个参数函数还可以增加2个参数:
    第三个参数:每个元素在原数据结构中的位置,比如数组下标。
    第四个参数:调用reduce方法的数据集合,比如例子中的posts

    const posts = [
      {id: 1, upVotes: 2},
      {id: 2, upVotes: 89},
      {id: 3, upVotes: 1}
    ];
    const totalUpvotes = posts.reduce((totalUpvotes, currentPost) =>     
      totalUpvotes + currentPost.upVotes, //reducer函数
      0 // 初始化投票数为0
    );
    console.log(totalUpvotes)//输出投票总数:92
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    1.2 Map与WeakMap对象的特点

    简介:详细介绍Map与WeakMap结构的特点

    • 背景

      JavaScript中的对象,实质就是键值对的集合,但是在对象里却只能用字符串作为键名。在一些特殊的场景里就满足不了我们的需求了,正因为此,Map这一数据提出了,它是JavaScript中的一种更完善Hash结构。

    • Map对象

      • 用于保存键值对,任何值(对象或者原始值)都可以作为一个键名或一个值。

      • 使用介绍

        // 通过构造函数创建一个Map
        let m = new Map();
        
        m.set([1,2],'张三')
        
        • 1
        • 2
        • 3
        • 4
    • 构造函数

      • Map()
      • 创建 Map 对象。
    • 静态属性

      • get Map[@@species]
      • 用于创建派生对象的构造函数。
    • 实例属性

      • Map.prototype.size
      • 返回 Map 对象中的键值对数量。
    • 实例方法

    • 内置API

      属性/方法作用例子
      size返回键值对的数量m.size
      clear()清除所有键值对m.clear()
      has(key)判断键值对中是否有指定的键名,返回值是布尔值m.has(key)
      get(key)获取指定键名的键值,如不存在则返回undefinedm.get(key)
      set(key, value)添加键值对,如键名已存在,则更新键值对m.set(key, value)
      delete(key)删除指定键名的键值对m.delete(key)
      • 遍历器生成函数
        • keys() //返回一个新的迭代对象,其中包含 Map 对象中所有的键,并以插入 Map 对象的顺序排列。
        • values() //返回一个新的迭代对象,其中包含 Map 对象中所有的值,并以插入 Map 对象的顺序排列。
        • entries() //返回一个新的迭代对象,其为一个包含 Map 对象中所有键值对的 [key, value] 数组,并以插入 Map 对象的顺序排列。
        • forEach() //以插入的顺序对 Map 对象中存在的键值对分别调用一次 callbackFn。如果给定了 thisArg 参数,这个参数将会是回调函数中 this 的值。否则,undefined 将会被用作 this 的值。
    myMap.forEach((value, key) => {
      console.log(`${key} = ${value}`);
    });
    // 0 = zero
    // 1 = one
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • WeakMap

      • 只接受对象作为键名

        const weakMap = new WeakMap([[{ name: 1 }, '张三']]);
        console.log(weakMap);
        
        • 1
        • 2
      • 无法遍历

    1.3 Set与WeakSet结构的特点

    • 介绍

      Set是ES6给开发者提供的一种类似数组的数据结构,可以理解为值的集合。它和数组的最大的区别就在于: 它的值不会有重复项。

    • 基本使用

      // 创建
      let set = new Set();
      let set2 = new Set([1,2,3])
      
      // 添加元素
      set.add(1)
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
    • 特点

      • 成员值唯一
    • 属性及方法

      属性/方法作用例子
      size返回成员个数s.size
      clear()清除所有成员s.clear()
      has(value)判断键值对中是否有指定的值,返回值是布尔值s.has(key)
      delete(value)删除指定值s.delete(key)
      // 注意
      const a = { aa: 1 };
      const list = new Set([a]);
      console.log(list.has(a));
      
      
      key和value相等
      const list = new Set([1, 2, 3, 4]);
      console.log(list.keys());
      console.log(list.values());
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
    • 用途

      // 去重
      let arr = [1,2,2,3,4,4,4];
      let s = new Set(arr);
      //结果:Set {1,2,3,4}
      
      let newArr = Array.from(s);
      //结果:[1,2,3,4],完成去重
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
    • WeakSet

      • 数组成员必须是对象
      • WeakSet结构也提供了add( ) 方法,delete( ) 方法,has( )方法给开发者使用,作用与用法跟Set结构完全一致。
      • WeakSet 结构不可遍历。因为它的成员都是对象的弱引用,随时被回收机制回收,成员消失。所以WeakSet 结构不会有keys( ),values( ),entries( ),forEach( )等方法和size属性。

    1.4 String介绍ES6提供的新的字符串方法及模板字符串

    1.4.1 String基础

    • 扩展的API

      方法描述
      includes(string, index)判断字符串中是否包含指定字符串,返回值是布尔值
      startsWith(string, index)判断字符串的开头是否包含指定字符串,返回值是布尔值
      endsWith(string, index)判断字符串的尾部是否包含指定字符串,返回值是布尔值
      repeat(n)repeat() 方法返回一个新字符串,表示将原字符串重复n 次。
      字符串补全第一个参数是补全后的字符串长度,第二个参数是用于补全的字符串
      padStart(length, str)用于头部补全
      padEnd(length, str)用于尾部补全
    • 模板字符串

      // 语法
      const name = "张三"
      const age = 18
      const hobbies = "打篮球"
      
      // ES5写法
      const str1 = '我的名字是'+name+',我今年'+age+'岁,我喜欢'+hobbies
      console.log(str1)
      
      // ES6写法
      const str2 = `我的名字是${name},我今年${age}岁,我喜欢${hobbies}`
      console.log(str2)
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
    • 使用模板字符串的注意事项

      • 使用模板字符串表示多行字符串时,所有的空格和缩进都会被保留在输出之中
      • 模板字符串中引入变量,要用 ${变量名} 这样的形式引入才可以
      • 模板字符串中的${…} 大括号内部可以放入任意的 JavaScript 表达式,可以进行运算、可以引用对象属性、可以调用函数、可以甚至还能嵌套,甚至还能调用自己本身

    1.4.2 基本字符串和字符串对象的区别

    请注意区分 JavaScript 字符串对象和基本字符串值 . ( 对于 Boolean 和Numbers 也同样如此.)

    字符串字面量 (通过单引号或双引号定义) 和 直接调用 String 方法 (没有通过 new 生成字符串对象实例) 的字符串都是基本字符串。JavaScript 会自动将基本字符串转换为字符串对象,只有将基本字符串转化为字符串对象之后才可以使用字符串对象的方法。当基本字符串需要调用一个字符串对象才有的方法或者查询值的时候 (基本字符串是没有这些方法的),JavaScript 会自动将基本字符串转化为字符串对象并且调用相应的方法或者执行查询。

    var s_prim = "foo";
    var s_obj = new String(s_prim);
    
    console.log(typeof s_prim); // Logs "string"
    console.log(typeof s_obj);  // Logs "object"
    
    • 1
    • 2
    • 3
    • 4
    • 5

    当使用 eval时,基本字符串和字符串对象也会产生不同的结果。eval 会将基本字符串作为源代码处理; 而字符串对象则被看作对象处理,返回对象。例如:

    s1 = "2 + 2";               // creates a string primitive
    s2 = new String("2 + 2");   // creates a String object
    console.log(eval(s1));      // returns the number 4
    console.log(eval(s2));      // returns the string "2 + 2"
    
    • 1
    • 2
    • 3
    • 4

    由于上述原因,当一段代码在需要使用基本字符串的时候却使用了字符串对象就会导致执行失败 (虽然一般情况下程序员们并不需要考虑这样的问题)。

    利用 valueOf 方法,我们可以将字符串对象转换为其对应的基本字符串。

    console.log(eval(s2.valueOf())); // returns the number 4
    
    • 1

    1.5 Object

    • 新增方法
      • Object.is

        let res = Object.is(NaN, NaN);
        console.log(res, NaN === NaN);  // 比较相等,最佳用法~相比于===  一是+0不等于-0,二是NaN等于自身。
        
        • 1
        • 2
      • Object.assign

        let a = { aa: 1, bb: 2 };
        let b = { aa: '1' };
        let c = Object.assign(b, a);  // (目标值, 源值)  源值会替换目标值
        console.log(c);
        // {aa:1, bb:2}
        
        • 1
        • 2
        • 3
        • 4
        • 5
      • Object.keys

        let xd = {
          hobby1: '吃饭',
          hobby2: '睡觉',
          hobby3: '敲代码',
        };
        console.log(Object.keys(xd));
        // ['hobby1', 'hobby2', 'hobby3']
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
      • Object.values

        let xd = {
          hobby1: '吃饭',
          hobby2: '睡觉',
          hobby3: '敲代码',
        };
        console.log(Object.keys(xd));
        // 获取对应keys  ['吃饭', '睡觉', '敲代码']
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
      • Object.entries

        let xd = {
          hobby1: '吃饭',
          hobby2: '睡觉',
          hobby3: '敲代码',
        };
        console.log(Object.keys(xd));
        // 对象变为数组值 ——  3行2列 的数组!
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7

    Object和Map比较

    Object 和 Map 类似的是,它们都允许你按键存取一个值、删除键、检测一个键是否绑定了值。因此(并且也没有其他内建的替代方式了)过去我们一直都把对象当成 Map 使用。

    不过 Map 和 Object 有一些重要的区别,在下列情况中使用 Map 会是更好的选择:

    MapObject
    意外的键Map 默认情况不包含任何键。只包含显式插入的键。一个 Object 有一个原型,原型链上的键名有可能和你自己在对象上的设置的键名产生冲突。【备注:虽然可以用 Object.create(null) 来创建一个没有原型的对象,但是这种用法不太常见。】
    键的类型一个 Map 的键可以是任意值,包括函数、对象或任意基本类型。一个 Object 的键必须是一个 String 或是 Symbol。
    键的顺序Map 中的键是有序的。因此,当迭代的时候,一个 Map 对象以插入的顺序返回键值。虽然 Object 的键目前是有序的,但并不总是这样,而且这个顺序是复杂的。因此,最好不要依赖属性的顺序。【自 ECMAScript 2015 规范以来,对象的属性被定义为是有序的;ECMAScript 2020 则额外定义了继承属性的顺序。参见 OrdinaryOwnPropertyKeys 和 EnumerateObjectProperties 抽象规范说明。但是,请注意没有可以迭代对象所有属性的机制,每一种机制只包含了属性的不同子集。(for-in 仅包含了以字符串为键的属性;Object.keys 仅包含了对象自身的、可枚举的、以字符串为键的属性;Object.getOwnPropertyNames 包含了所有以字符串为键的属性,即使是不可枚举的;Object.getOwnPropertySymbols 与前者类似,但其包含的是以 Symbol 为键的属性,等等。) 】
    SizeMap 的键值对个数可以轻易地通过 size 属性获取。Object 的键值对个数只能手动计算。
    迭代Map 是 可迭代的 的,所以可以直接被迭代。Object 没有实现 迭代协议,所以使用 JavaSctipt 的 for…of 表达式并不能直接迭代对象。【备注:对象可以实现迭代协议,或者你可以使用 Object.keys 或 Object.entries。】for…in 表达式允许你迭代一个对象的可枚举属性。
    性能在频繁增删键值对的场景下表现更好。在频繁添加和删除键值对的场景下未作出优化。
    序列化和解析没有元素的序列化和解析的支持。(但是你可以使用携带 replacer 参数的 JSON.stringify() 创建一个自己的对 Map 的序列化和解析支持。参见 Stack Overflow 上的提问:How do you JSON.stringify an ES6 Map?原生的由 Object 到 JSON 的序列化支持,使用 JSON.stringify()。 原生的由 JSON 到 Object 的解析支持,使用 JSON.parse()。

    二、开发中常用的JS方法

    2.1 向对象数组添加新元素

    const books = [];
    const newBook = {title: 'Alice in wonderland', id: 1};
    const updatedBooks = [...books, newBook];
    //updatedBooks的值为[{title: 'Alice in wonderland', id: 1}]
    
    • 1
    • 2
    • 3
    • 4

    2.2 为一个数组创建视图

    如果需要实现用户从购物车中删除物品,但是又不想破坏原来的购物车列表,可以使用filter方法
    
    const myId = 6;
    const userIds = [1, 5, 7, 3, 6];
    const allButMe = userIds.filter(id => id !== myId);
    // allButMe is [1, 5, 7, 3]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.3 向数组中新增元素

    const books = ['Positioning by Trout', 'War by Green'];
    const newBooks = [...books, 'HWFIF by Carnegie'];
    // newBooks are now ['Positioning by Trout', 'War by Green', 'HWFIF // by Carnegie']
    
    • 1
    • 2
    • 3

    2.4 为对象新增一组键值对

    const user = {name: 'Shivek Khurana'};
    const updatedUser = {...user, age: 23};
    //updatedUser的值为:{name: 'Shivek Khurana', age: 23}
    
    • 1
    • 2
    • 3

    2.5 使用变量作为键名为对象添加键值对

    const dynamicKey = 'wearsSpectacles';
    const user = {name: 'Shivek Khurana'};
    const updatedUser = {...user, [dynamicKey]: true};
    // updatedUser is {name: 'Shivek Khurana', wearsSpectacles: true}
    
    • 1
    • 2
    • 3
    • 4

    2.6 修改数组中满足条件的元素对象

    const posts = [
      {id: 1, title: 'Title 1'},
      {id: 2, title: 'Title 2'}
    ];
    const updatedPosts = posts.map(p => p.id !== 1 ?
      p : {...p, title: 'Updated Title 1'}
    );
    /*
    updatedPosts is now 
    [
      {id: 1, title: 'Updated Title 1'},
      {id: 2, title: 'Title 2'}
    ];
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2.7 找出数组中满足条件的元素

    const posts = [
      {id: 1, title: 'Title 1'},
      {id: 2, title: 'Title 2'}
    ];
    const postInQuestion = posts.find(p => p.id === 2);
    // postInQuestion now holds {id: 2, title: 'Title 2'}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.8 删除目标对象的一组属性

    //方法一
    const user = {name: 'Shivek Khurana', age: 23, password: 'SantaCl@use'};
    const userWithoutPassword = Object.keys(user)
      .filter(key => key !== 'password')
      .map(key => {[key]: user[key]})
      .reduce((accumulator, current) => 
        ({...accumulator, ...current}),
        {}
      )
    ;
     
    // 方法二
    const user = {name: 'Shivek Khurana', age: 23, password: 'SantaCl@use'};
    const userWithoutPassword = (({name, age}) => ({name, age}))(user);
     
    // userWithoutPassword becomes {name: 'Shivek Khurana', age: 23}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    2.9 将对象转化成请求串

    const params = {color: 'red', minPrice: 8000, maxPrice: 10000};
    const query = '?' + Object.keys(params)
      .map(k =>   
        encodeURIComponent(k) + '=' + encodeURIComponent(params[k])
      )
      .join('&')
    ;
    // encodeURIComponent将对特殊字符进行编码。
    // query is now "color=red&minPrice=8000&maxPrice=10000"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2.10 获取数组中某一对象的下标

    const posts = [
      {id: 13, title: 'Title 221'},
      {id: 5, title: 'Title 102'},
      {id: 131, title: 'Title 18'},
      {id: 55, title: 'Title 234'}
    ];
    // 找到id为131的元素
    const requiredIndex = posts.findIndex(obj=>obj.id===131);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    三、Object 常用API

    关于Object参考JavaScript对象

    3.1. Object.assign()

    Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象分配到目标对象。它将返回目标对象。常用来合并对象。

    const obj1 = { a: 1, b: 2 }
    const obj2 = { b: 4, c: 5 }
    
    const obj3 = Object.assign(obj1, obj2)
    
    const obj4 = Object.assign({}, obj1) // 克隆了obj1对象
    
    console.log(obj1) // { a: 1, b: 4, c: 5 } 对同名属性b进行了替换 obj1发生改变是因为obj2赋给了obj1
    
    console.log(obj2) // { b: 4, c: 5 }
    
    console.log(obj3) // { a: 1, b: 4, c: 5 }
    
    console.log(obj4) // { a: 1, b: 4, c: 5 }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 语法
    Object.assign(target, ...sources)
    
    • 1
    • 参数:target 目标参数,sources源对象
    • 返回值:目标对象

    注意

    • 如果目标对象中的属性具有相同的键,则属性将被源对象中的属性覆盖。
    • Object.assign 方法只会拷贝源对象自身的并且可枚举的属性到目标对象。
    • assign其实是浅拷贝而不是深拷贝

    也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。同名属性会替换。

    const obj5 = {
      name: 'dengke',
        a: 10,
      fn: {
        sum: 10
      }
    }
    
    const obj6 = Object.assign(obj1, obj5)
    console.log(obj6) // { a: 10, b: 2, name: 'dengke', fn: {…}}
    console.log(obj1) // {a: 10, b: 2, name: 'dengke', fn: {…}} 对同名属性a进行了替换
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • Object.assign 不会在那些source对象值为null或undefined的时候抛出错误。

    3.2. Object.keys()

    上边枚举对象属性时有用到了Object.keys(),在这里就具体为大家介绍一下它。
    Object.keys() 方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致。与Object.values()相似,区别在于这个返回的是数据的属性就是key。接下来就会介绍Object.values(),不要着急。😊

    const arr = ['a', 'b', 'c']
    console.log(Object.keys(arr)) // ['0', '1', '2']
    
    const obj = { 0: 'a', 1: 'b', 2: 'c' }
    console.log(Object.keys(obj)) // ['0', '1', '2']
    
    const obj2 = { 100: 'a', 2: 'b', 7: 'c' }
    console.log(Object.keys(obj2)) // ['2', '7', '100']
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 语法
    Object.keys(obj)
    
    • 1
    • 参数:obj要返回其枚举自身属性的对象。
    • 返回值:一个表示给定对象的所有可枚举属性的字符串数组。
    • 注意
      在ES5里,如果此方法的参数不是对象(而是一个原始值),那么它会抛出 TypeError。在ES2015中,非对象的参数将被强制转换为一个对象。
    Object.keys("foo") // TypeError: "foo" is not an object       (ES5 code)
    
    Object.keys("foo") // ["0", "1", "2"]                         (ES2015 code)
    
    • 1
    • 2
    • 3

    3.3. Object.values()

    Object.values() 方法返回一个给定对象自身的所有可枚举属性值的数组,值的顺序与使用for…in循环的顺序相同 ( 区别在于 for-in 循环枚举原型链中的属性 )。与Object.keys()相似,区别在于这个返回的是数据的值也就是value

    const obj1 = { foo: 'bar', baz: 42 }
    console.log(Object.values(obj1)) // ['bar', 42]
    
    const obj2 = { 0: 'a', 1: 'b', 2: 'c' }
    console.log(Object.values(obj2)) // ['a', 'b', 'c']
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 语法
    Object.values(obj)
    
    • 1
    • 参数:obj被返回可枚举属性值的对象。

    • 返回值:一个包含对象自身的所有可枚举属性值的数组。

    • 注意
      对象key为number的话,会从升序枚举返回。

    const obj3 = { 100: 'a', 2: 'b', 7: 'c' }
    console.log(Object.values(obj3)) // ['b', 'c', 'a']
    
    • 1
    • 2

    3.4. Object.entries(obj)

    Object.entries() 方法返回一个给定对象自身可枚举属性的键值对数组。可使用Object.fromEntries()方法,相当于反转了Object.entries()方法返回的数据结构。接下来也会介绍Object.fromEntries()

    const obj1 = {
      name: 'dengke',
      age: 18
    };
    
    for (const [key, value] of Object.entries(obj1)) {
      console.log(`${key}: ${value}`);
    }
    // "name: dengke"
    // "age: 18"
    
    const obj2 = { foo: 'bar', baz: 42 }
    console.log(Object.entries(obj2)) // [ ['foo', 'bar'], ['baz', 42] ]
    
    const obj3 = { 0: 'a', 1: 'b', 2: 'c' }
    console.log(Object.entries(obj3)) // [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 语法
    Object.entries(obj)
    
    • 1
    • 参数:obj可以返回其可枚举属性的键值对的对象。
    • 返回值:给定对象自身可枚举属性的键值对数组。
    • 补充

    将Object转换为Map,new Map()构造函数接受一个可迭代的entries。借助Object.entries方法你可以很容易的将 Object转换为Map:

    const obj = { foo: "bar", baz: 42 }
    const map = new Map(Object.entries(obj))
    console.log(map) // Map { foo: "bar", baz: 42 }
    
    • 1
    • 2
    • 3

    3.5. Object.fromEntries()

    Object.fromEntries() 方法把键值对列表转换为一个对象。与Object.entries()相反。相当于反转了Object.entries()方法返回的数据结构。(下面补充里有具体的演示)

    const entries = new Map([
      ['foo', 'bar'],
      ['baz', 42]
    ]);
    
    const obj = Object.fromEntries(entries);
    
    console.log(obj);
    // Object { foo: "bar", baz: 42 }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 语法
    Object.fromEntries(iterable)
    
    • 1
    • 参数:iterable类似Array、Map或者其它实现了可迭代协议的可迭代对象。

    • 返回值:一个由该迭代对象条目提供对应属性的新对象。

    • 补充

    Map 转化为 Object

    通过 Object.fromEntries, 可以将Map转换为Object:
    const map = new Map([ ['foo', 'bar'], ['baz', 42] ])
    const obj = Object.fromEntries(map)
    console.log(obj)
    // { foo: "bar", baz: 42 }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    Array 转化为 Object
    通过 Object.fromEntries, 可以将Array转换为Object:

    const arr = [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ]
    const obj = Object.fromEntries(arr)
    console.log(obj)
    // { 0: "a", 1: "b", 2: "c" }
    
    • 1
    • 2
    • 3
    • 4

    对象转换

    Object.fromEntries 是与 Object.entries()相反的方法,用 数组处理函数 可以像下面这样转换对象:

    const object1 = { a: 1, b: 2, c: 3 }
    
    const object2 = Object.fromEntries(
      Object.entries(object1)
      .map(([ key, val ]) => [ key, val * 2 ])
    )
    
    // Object.entries(object1) >>> [["a",1],["b",2],["c",3]]
    
    console.log(object2) // { a: 2, b: 4, c: 6 }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    3.6. Object.prototype.hasOwnProperty()

    上边枚举对象属性时为了避免for…in遍历继承来的属性,给大家补充了可以借助Object.prototype.hasOwnProperty()方法进行判断,在这里也具体为大家介绍一下它。
    hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)。

    const obj1 = {};
    obj1.property1 = 42
    
    console.log(obj1.hasOwnProperty('property1')) // true
    console.log(obj1.hasOwnProperty('toString')) // false
    console.log(obj1.hasOwnProperty('hasOwnProperty')) // false
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 语法
    obj.hasOwnProperty(prop)
    
    • 1
    • 参数:prop 要检测的属性的String字符串形式表示的名称,或者Symbol。

    • 返回值:用来判断某个对象是否含有指定的属性的布尔值Boolean。

    • 注意

    只会对自身属性进行判断,继承来的一律返回false。配合for…in使用,可以避免其遍历继承来的属性。

    const o = new Object()
    o.prop = 'exists'
    
    console.log(o.hasOwnProperty('prop')) // true
    console.log(o.hasOwnProperty('toString')) // false
    console.log(o.hasOwnProperty('hasOwnProperty')) // false
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    即使属性的值是 null 或 undefined,只要属性存在,hasOwnProperty 依旧会返回 true。

    const o = new Object();
    o.propOne = null
    o.propTwo = undefined
    
    console.log(o.hasOwnProperty('propOne')) // true
    console.log(o.hasOwnProperty('propTwo')) // true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3.7. Object.getOwnPropertyNames()

    上边枚举对象属性时也有用到该方法,在这里也具体为大家介绍一下它。
    Object.getOwnPropertyNames() 返回一个数组,该数组对元素是 obj自身拥有的枚举或不可枚举属性名称字符串。 数组中枚举属性的顺序与通过for…in循环Object.keys迭代该对象属性时一致。数组中不可枚举属性的顺序未定义。

    const arr = ["a", "b", "c"];
    console.log(Object.getOwnPropertyNames(arr).sort()) // ["0", "1", "2", "length"]
    
    // 类数组对象
    const obj = { 0: "a", 1: "b", 2: "c"};
    console.log(Object.getOwnPropertyNames(obj).sort()) // ["0", "1", "2"]
    
    // 使用Array.forEach输出属性名和属性值
    Object.getOwnPropertyNames(obj).forEach(function(val, idx, array) {
      console.log(val + " -> " + obj[val]);
    })
    // 0 -> a
    // 1 -> b
    // 2 -> c
    
    // 不可枚举属性
    const my_obj = Object.create({}, {
      getFoo: {
        value: function() { return this.foo; },
        enumerable: false
      }
    });
    my_obj.foo = 1;
    
    console.log(Object.getOwnPropertyNames(my_obj).sort())
    // ["foo", "getFoo"]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 语法
    obj.getOwnPropertyNames(obj)
    
    • 1
    • 参数:obj一个对象,其自身的可枚举和不可枚举属性的名称被返回。

    • 返回值:在给定对象上找到的自身属性对应的字符串数组。

    • 补充

    Object.getOwnPropertyNames和Object.keys的区别:Object.keys只适用于可枚举的属性,而Object.getOwnPropertyNames返回对象的全部属性名称(包括不可枚举的)。

    'use strict'
    (function () {
        // 人类的构造函数
        const person = function (name, age, sex) {
            this.name = name
            this.age = age
            this.sex = sex
            this.sing = () => {
                console.log('sing');
            }
        }
    
        // new 一个ladygaga
        const gaga = new person('ladygaga', 26, 'girl')
        
        // 给嘎嘎发放一个不可枚举的身份证
        Object.defineProperty(gaga, 'id', {
            value: '1234567890',
            enumerable: false
        })
    
        //查看gaga的个人信息
        const arr = Object.getOwnPropertyNames(gaga)
        console.log(arr) // name, age, sex, sing, id
        
        // 注意和getOwnPropertyNames的区别,不可枚举的id没有输出
        const arr1 = Object.keys(gaga)
        console.log(arr1) // name, age, sex, sing
    })()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    如果你只要获取到可枚举属性,可以用Object.keys或用for…in循环(for…in会获取到原型链上的可枚举属性,可以使用hasOwnProperty()方法过滤掉)。

    获取不可枚举的属性,可以使用Array.prototype.filter()方法,从所有的属性名数组(使用Object.getOwnPropertyNames()方法获得)中去除可枚举的属性(使用Object.keys()方法获得),剩余的属性便是不可枚举的属性了:

    const target = myObject;
    const enum_and_nonenum = Object.getOwnPropertyNames(target);
    const enum_only = Object.keys(target);
    const nonenum_only = enum_and_nonenum.filter(function(key) {
        const indexInEnum = enum_only.indexOf(key);
        if (indexInEnum == -1) {
            // 没有发现在enum_only健集中意味着这个健是不可枚举的,
            // 因此返回true 以便让它保持在过滤结果中
            return true;
        } else {
            return false;
        }
    });
    
    console.log(nonenum_only);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 注意

    在 ES5 中,如果参数不是一个原始对象类型,将抛出一个 TypeError异常。在 ES2015 中,非对象参数被强制转换为对象。

    Object.getOwnPropertyNames('foo') // TypeError: "foo" is not an object     (ES5 code)
    
    Object.getOwnPropertyNames('foo') // ['length', '0', '1', '2']             (ES2015 code)
    
    • 1
    • 2
    • 3

    3.8. Object.freeze()

    Object.freeze() 方法可以冻结一个对象。一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。此外,冻结一个对象后该对象的原型也不能被修改。freeze() 返回和传入的参数相同的对象。

    const obj = {
      prop: 42
    }
    
    Object.freeze(obj)
    
    obj.prop = 33
    
    console.log(obj.prop)
    //  42
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 语法
    obj.freeze(obj)
    
    • 1
    • 参数:obj要被冻结的对象。
    • 返回值:被冻结的对象。
    • 补充

    被冻结的对象是不可变的。但也不总是这样。下例展示了冻结对象不是常量对象(浅冻结)。

    const obj1 = {
      internal: {}
    }
    Object.freeze(obj1)
    
    obj1.internal.a = 'aValue'
    console.log(obj1.internal.a) // 'aValue'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    要使对象不可变,需要递归冻结每个类型为对象的属性(深冻结)。

    // 深冻结函数.
    function deepFreeze(obj) {
      // 取回定义在obj上的属性名
      const propNames = Object.getOwnPropertyNames(obj)
    
      // 在冻结自身之前冻结属性
      propNames.forEach(function(name) {
        const prop = obj[name]
    
        // 如果prop是个对象,冻结它
        if (typeof prop == 'object' && prop !== null)
          deepFreeze(prop)
      })
    
      // 冻结自身
      return Object.freeze(obj);
    }
    
    const obj2 = {
      internal: {}
    }
    
    deepFreeze(obj2)
    obj2.internal.a = 'anotherValue'
    obj2.internal.a // undefined
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    3.9. Object.isFrozen()

    Object.isFrozen() 方法判断一个对象是否被冻结。

    // 一个对象默认是可扩展的, 所以它也是非冻结的。
    Object.isFrozen({}) // false
    
    // 一个不可扩展的空对象同时也是一个冻结对象。
    var vacuouslyFrozen = Object.preventExtensions({})
    Object.isFrozen(vacuouslyFrozen) // true
    
    var frozen = { 1: 81 }
    Object.isFrozen(frozen) // false
    
    // 使用Object.freeze是冻结一个对象最方便的方法.
    Object.freeze(frozen)
    Object.isFrozen(frozen) // true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 语法
    obj.isFrozen(obj)
    
    • 1
    • 参数:obj被检测的对象。

    • 返回值:表示给定对象是否被冻结的Boolean。

    • 注意

    在 ES5 中,如果参数不是一个对象类型,将抛出一个TypeError异常。在 ES2015 中,非对象参数将被视为一个冻结的普通对象,因此会返回true。

    Object.isFrozen(1) // (ES5 code)
    // TypeError: 1 is not an object 
    
    Object.isFrozen(1) // (ES2015 code)
    // true   
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • 相关阅读:
    2022杭电多校第一场
    贯彻学习大会文件精神主题知识竞赛
    onclick传参的使用遇到XXX is not defined
    Flutter 3.0 之 PlatformView :告别 VirtualDisplay ,拥抱 TextureLayer
    掘金拉美,大有可为,2023美客多官方招商会-深圳站成功举办!
    【06】VirtualService高级流量功能
    新冠疫情历史数据可视化分析
    iommu=pt内核参数解析
    公司关键业务协同管理平台建设规划
    第1章 Linux基础知识 -- 掌握linux常用命令(1)
  • 原文地址:https://blog.csdn.net/weixin_45918732/article/details/127755965