• JavaScript 对象 Object


    对象


    • 对象就是一组“键值对”(key-value)的集合,是一种无序的复合数据集合
    • 对象的所有键名都是字符串(ES6 又引入了 Symbol 值也可以作为键名),所以加不加引号都可以
    • 如果键名是数值,会被自动转为字符串。
    • 对象的每一个键名又称为“属性”(property),它的“键值”可以是任何数据类型

    1 属性 property

    1.1 属性创建

    • 属性可以动态创建,不必在对象声明时就指定。
    var obj = {};
    obj.foo = 123;
    obj.foo; // 123
    
    • 1
    • 2
    • 3
    • 属性的简洁表示
      • ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。
      • 属性名就是变量名, 属性值就是变量值
    const foo = 'bar';
    
    // 简写
    const baz = {foo}; // baz = {foo: "bar"}
    
    // 等同于
    const baz = {foo: foo};
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    1.2 属性取值

    • 读取对象的属性,有两种方法,一种是使用点运算符,还有一种是使用方括号运算符。
    var obj = { p: "Hello World" };
    
    obj.p;    // "Hello World"
    obj["p"]; // "Hello World"
    
    • 1
    • 2
    • 3
    • 4
    • with语句的格式如下:
    with (对象) {
      语句;
    }
    
    • 1
    • 2
    • 3
    • 它的作用是操作同一个对象的多个属性时,提供一些书写的方便。
    // 例一
    var obj = {
      p1: 1,
      p2: 2,
    };
    with (obj) {
      p1 = 4;
      p2 = 5;
    }
    // 等同于
    obj.p1 = 4;
    obj.p2 = 5;
    
    // 例二
    with (document.links[0]) {
      console.log(href);
      console.log(title);
      console.log(style);
    }
    // 等同于
    console.log(document.links[0].href);
    console.log(document.links[0].title);
    console.log(document.links[0].style);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    1.3 属性删除 delete

    delete命令用于删除对象的属性,删除成功后返回true

    var obj = { p: 1 };
    Object.keys(obj); // ["p"]
    
    delete obj.p; // true
    obj.p; // undefined
    Object.keys(obj); // []
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    1.4 属性存在 in

    • in 运算符用于检查对象是否包含某个属性(注意,检查的是键名,不是键值),
    • 如果包含就返回true,否则返回false。它的左边是一个字符串,表示属性名,右边是一个对象。
    var obj = { p: 1 };
    "p" in obj; // true
    "toString" in obj; // true
    
    • 1
    • 2
    • 3

    1.5 属性遍历 keys

    • Object.keys:查看一个对象本身的所有属性
    var obj = { key1: 1, key2: 2};
    
    Object.keys(obj); // ['key1', 'key2']
    
    • 1
    • 2
    • 3
    • Object.getOwnPropertyNames:查看一个对象本身的所有属性

      • 数组的length属性是不可枚举的属性,所以只出现在Object.getOwnPropertyNames方法的返回结果中。
    • for...in 循环用来遍历一个对象的全部属性。

      • for...in循环遍历的是对象所有可遍历(enumerable)的属性,会跳过不可遍历的属性。
      • 它不仅遍历对象自身的属性,还遍历继承的属性。
      • 举例来说,对象都继承了toString属性,但是for...in循环不会遍历到这个属性。
    var obj = { a: 1, b: 2, c: 3 };
    
    for (var i in obj) {
      console.log("键名:", i);      // 键名: a  // 键名: b  // 键名: c
      console.log("键值:", obj[i]); // 键值: 1  // 键值: 2  // 键值: 3
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    ES6 一共有 5 种方法可以遍历对象的属性。

    • for...in循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)。
    • Object.keys返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名。
    • Object.getOwnPropertyNames返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名。
    • Object.getOwnPropertySymbols返回一个数组,包含对象自身的所有 Symbol 属性的键名。
    • Reflect.ownKeys返回一个数组,包含对象自身的(不含继承的)所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举。

    以上的 5 种方法遍历对象的键名,都遵守同样的属性遍历的次序规则。

    • 首先遍历所有数值键,按照数值升序排列。
    • 其次遍历所有字符串键,按照加入时间升序排列。
    • 最后遍历所有 Symbol 键,按照加入时间升序排列。
    Reflect.ownKeys({ [Symbol()]:0, b:0, 10:0, 2:0, a:0 })
    // ['2', '10', 'b', 'a', Symbol()]
    
    • 1
    • 2

    上面代码中,Reflect.ownKeys方法返回一个数组,包含了参数对象的所有属性。这个数组的属性次序是这样的,首先是数值属性210,其次是字符串属性ba,最后是 Symbol 属性。

    1.7 属性描述对象

    • JavaScript 提供了一个内部数据结构,用来描述对象的属性,控制它的行为,比如该属性是否可写、可遍历等等。这个内部数据结构称为“属性描述对象”(attributes object)
    • 属性描述对象提供 6 个元属性。
    {
      value: 123,    
      writable: false,
      enumerable: true,
      configurable: false,
      get: undefined,
      set: undefined
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 对象的每个属性都有一个描述对象(Descriptor),用来控制该属性的行为。
    let obj = { foo: 123 };
    Object.getOwnPropertyDescriptor(obj, 'foo')
    //  {
    //    value: 123,
    //    writable: true,
    //    enumerable: true,
    //    configurable: true
    //  }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • value 是该属性的属性值,默认为undefined

    • writable 是一个布尔值,表示属性值(value)是否可改变(即是否可写),默认为true

    • enumerable是一个布尔值,表示该属性是否可遍历,默认为true。如果设为false,会使得某些操作(比如for...in循环、Object.keys())跳过该属性。

    • configurable是一个布尔值,表示属性的可配置性,默认为true。如果设为false,将阻止某些操作改写属性描述对象,比如无法删除该属性,也不得改变各种元属性(value属性除外)。也就是说,configurable属性控制了属性描述对象的可写性。

    • get是一个函数,表示该属性的取值函数(getter),默认为undefined

    • set是一个函数,表示该属性的存值函数(setter),默认为undefined

    • Object.getOwnPropertyDescriptor()方法可以获取属性描述对象。它的第一个参数是目标对象,第二个参数是一个字符串,对应目标对象的某个属性名。

    • Object.getOwnPropertyNames方法返回一个数组,成员是参数对象自身的全部属性的属性名,不管该属性是否可遍历。

    1.7.1 属性可枚举性

    对象的每个属性都有一个描述对象(Descriptor),描述对象的“可枚举性”enumerable属性为false,表示某些操作会忽略当前属性。

    下面四个操作会忽略enumerablefalse的属性。

    • for...in循环:只遍历对象自身的和继承的可枚举的属性。返回继承的属性
    • Object.keys():返回对象自身的所有可枚举的属性的键名。忽略继承的属性,只处理对象自身的属性
    • JSON.stringify():只串行化对象自身的可枚举的属性。忽略继承的属性,只处理对象自身的属性
    • Object.assign(): 忽略enumerablefalse的属性,只拷贝对象自身的可枚举的属性。忽略继承的属性,只处理对象自身的属性,ES6 新增的

    实际上,引入“可枚举”(enumerable)这个概念的最初目的,就是让某些属性可以规避掉for...in操作,不然所有内部属性和方法都会被遍历到。比如,对象原型的toString方法,以及数组的length属性,就通过“可枚举性”,从而避免被for...in遍历到。

    2 赋值

    2.1 赋值方法

    1. 定义时初始化
    let obj = {foo: 123}; // obj = {foo: 123}
    
    • 1
    1. 动态创建,不必在对象声明时就指定
    let obj = {};
    obj.foo = 123; // obj = {foo: 123}
    
    • 1
    • 2
    1. 属性的简洁表示
    • ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。
    • 属性名就是变量名, 属性值就是变量值
    const foo = 'bar';
    const obj = {foo}; // obj = {foo: "bar"}
    
    • 1
    • 2
    1. 扩展运算符 解构赋值
    • 将目标对象自身的所有可遍历的(enumerable)、但尚未被读取的属性,分配到指定的对象上面。所有的键和它们的值,都会拷贝到新对象上面。

    • 解构赋值要求等号右边是一个对象

    • 解构赋值必须是最后一个参数

    • 变量必须与属性同名,才能取到正确的值,顺序不对没事

    • 真正被赋值的是属性后面的变量,而不是属性。

    • 默认:let { foo: foo, bar: bar } = { foo: 'aaa', bar: 'bbb' };

    • 改名:const { x, y: z } = { x: 1, y: 2 }

    let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
    x // 1
    y // 2
    z // { a: 3, b: 4 }  获取等号右边的所有尚未读取的键(`a`和`b`),将它们连同值一起拷贝过来
    
    • 1
    • 2
    • 3
    • 4
    1. 扩展运算符 取出对象可遍历属性
    • 对象的扩展运算符(...)用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。
    • 扩展运算符后面必须是一个变量名
    • 给对象加元素: v = {...v, xxx: y}
    let x = 5;
    let z = { a: 3, b: 4 };
    let n = { x, ...z };
    n // {c: 5, a: 3, b: 4}
    
    • 1
    • 2
    • 3
    • 4

    2.1.1 解构赋值

    注意,解构赋值的拷贝是浅拷贝,即如果一个键的值是复合类型的值(数组、对象、函数)、那么解构赋值拷贝的是这个值的引用,而不是这个值的副本。

    let obj = { a: { b: 1 } };
    let { ...x } = obj;
    obj.a.b = 2;
    x.a.b // 2
    // `x`是解构赋值所在的对象,拷贝了对象`obj`的`a`属性。`a`属性引用了一个对象,修改这个对象的值,会影响到解构赋值对它的引用。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 扩展运算符的解构赋值,不能复制继承自原型对象的属性。
    let o1 = { a: 1 };
    let o2 = { b: 2 };
    o2.__proto__ = o1;
    let { ...o3 } = o2;
    o3 // { b: 2 }
    o3.a // undefined
    // 上面代码中,对象`o3`复制了`o2`,但是只复制了`o2`自身的属性,没有复制它的原型对象`o1`的属性。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2.1.2 扩展运算符

    • 由于数组是特殊的对象,所以对象的扩展运算符也可以用于数组。
    • 如果扩展运算符后面是一个空对象,则没有任何效果。
    • 如果扩展运算符后面不是对象,则会自动将其转为对象。
    • 如果扩展运算符后面是字符串,它会自动转成一个类似数组的对象,因此返回的不是空对象。
    • 对象的扩展运算符,只会返回参数对象自身的、可枚举的属性,这一点要特别小心,尤其是用于类的实例对象时。
    { ...['a', 'b', 'c'] }     // {0: "a", 1: "b", 2: "c"}
    {...'hello'}               // {0: "h", 1: "e", 2: "l", 3: "l", 4: "o"}
    {...{}, a: 1}  // { a: 1 } 
    {...1}         // {}       // 等同于 {...Object(1)}  等同于 {...Number{1}}
    {...true}      // {}       // 等同于 {...Object(true)}
    {...undefined} // {}       // 等同于 {...Object(undefined)}
    {...null}      // {}       // 等同于 {...Object(null)}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 对象的扩展运算符等同于使用Object.assign()方法。
    let ab = { ...a, ...b };
    // 等同于
    let ab = Object.assign({}, a, b);
    
    • 1
    • 2
    • 3
    • 如果用户自定义的属性,放在扩展运算符后面,则扩展运算符内部的同名属性会被覆盖掉。
    let aWithOverrides = { ...a, x: 1, y: 2 };
    // 等同于
    let aWithOverrides = { ...a, ...{ x: 1, y: 2 } };
    // 等同于
    let x = 1, y = 2, aWithOverrides = { ...a, x, y };
    // 等同于
    let aWithOverrides = Object.assign({}, a, { x: 1, y: 2 });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    上面代码中,a对象的x属性和y属性,拷贝到新对象后会被覆盖掉。

    这用来修改现有对象部分的属性就很方便了。

    let newVersion = {
      ...previousVersion,
      name: 'New Name' // Override the name property
    };
    
    • 1
    • 2
    • 3
    • 4

    上面代码中,newVersion对象自定义了name属性,其他属性全部复制自previousVersion对象。

    如果把自定义属性放在扩展运算符前面,就变成了设置新对象的默认属性值。

    let aWithDefaults = { x: 1, y: 2, ...a };
    // 等同于
    let aWithDefaults = Object.assign({}, { x: 1, y: 2 }, a);
    // 等同于
    let aWithDefaults = Object.assign({ x: 1, y: 2 }, a);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.2 数据同步更改

    • 如果不同的变量名指向同一个对象,那么它们都是这个对象的引用,也就是说指向同一个内存地址。修改其中一个变量,会影响到其他所有变量。
    var o1 = {};
    var o2 = o1;
    
    o1.a = 1;
    o2.a; // 1
    
    o2.b = 2;
    o1.b; // 2
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 问题:变量赋值,修改变量会导致原始数据也同步发生改变
    • 原因:指向同一个地址
    • 解决:JSON.parse(JSON.stringify(config.data)) 转换为字符串再转换为对象后赋值

    Vue中变量赋值后,修改变量,赋值数据与被赋值数据同步改变

    类似的,在 vue 中,变量赋值,修改变量会导致原始数据也同步发生改变

    • 问题:let a = this.file, 修改 a 时,this.file 也改变了
    • 原因:这是一个引用传递而不是值传递
    • 解决:把 this.file 转换为字符串再转换为对象后赋值

    let a = JSON.parse(JSON.stringify(this.file));

    3 Object 对象

    • JavaScript 的所有其他对象都继承自Object对象,即那些对象都是Object的实例。
    • Object对象的原生方法分成两类
      • Object静态方法:直接定义在Object对象的方法
      • Object实例方法:定义在Object原型对象Object.prototype上的方法。它可以被Object实例直接使用
    // 静态方法
    Object.print = function (o) {
      console.log(o);
    };
    
    • 1
    • 2
    • 3
    • 4
    // 实例方法
    Object.prototype.print = function () {
      console.log(this);
    };
    
    var obj = new Object(); //实例`obj`直接继承了`Object.prototype`的属性和方法
    obj.print(); // Object
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3.1 Object 属性

    • constructor 指向 Object 构造函数,构造用
    • proto 指向原型对象,原型链, __proto__属性(前后各两个下划线),用来读取或设置当前对象的原型对象(prototype)。目前,所有浏览器(包括 IE11)都部署了这个属性。

    3.2 Object()

    • Object() 将任意值转为对象: Object本身是一个函数,可以当作工具方法使用,将任意值转为对象。
    • Object() 构造函数: Object不仅可以当作工具函数使用,还可以当作构造函数使用,即前面可以使用new命令。

    3.3 Object 静态方法

    (1)对象属性模型的相关方法

    • Object.keys:查看一个对象本身的所有属性,返回一个包含所有给定对象自身可枚举属性名称是数组
    • Object.getOwnPropertyNames:查看一个对象本身的所有属性,返回一个数组,成员是参数对象自身的全部属性的属性名,不管该属性是否可遍历。
      • 数组的length属性是不可枚举的属性,所以只出现在Object.getOwnPropertyNames方法的返回结果中。
    • Object.getOwnPropertyDescriptor():获取某个属性的描述对象。它的第一个参数是目标对象,第二个参数是一个字符串,对应目标对象的某个属性名。
    • Object.getOwnPropertyDescriptors()方法,返回指定对象所有自身属性(非继承属性)的描述对象。ES2017 引入
    • Object.defineProperty():通过描述对象,定义某个属性。给对象添加一个属性并指定该属性的配置
    • Object.defineProperties():通过描述对象,定义多个属性。
    • Object.hasOwn():也可以判断是否为自身的属性。

    (2)控制对象状态的方法

    • Object.preventExtensions():防止对象扩展。
    • Object.isExtensible():判断对象是否可扩展。
    • Object.seal():禁止对象配置。
    • Object.isSealed():判断一个对象是否可配置。
    • Object.freeze():冻结一个对象。
    • Object.isFrozen():判断一个对象是否被冻结。

    (3)原型链相关方法

    • Object.is() 比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致
    • Object.assign() 通过复制一个或者多个对象,创建一个对象,浅拷贝,方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。
    • Object.values() 返回给定对象自身可枚举属性的键值的数组
    • Object.create():该方法可以指定原型对象和属性,返回一个新的对象。
    • Object.entries():方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组。
    • Object.fromEntries()方法是Object.entries()的逆操作,用于将一个键值对数组转为对象。
    • Object.getPrototypeOf():获取对象的Prototype对象。该方法与Object.setPrototypeOf方法配套,用于读取一个对象的原型对象。
    • Object.setPrototypeOf:方法的作用与__proto__相同,用来设置一个对象的原型对象(prototype),返回参数对象本身。它是 ES6 正式推荐的设置原型对象的方法。

    Object.assign(obj1, obj2, obj3)

    • 合并对象: 把 obj2、obj3 合并到 obj1 上
    • 如果不是对象的话,它会在内部转换成对象
    • 所以如果碰到了 null 或者 undefined 这种不能转换成对象的值的话,assign 就会报错。
    • 但是如果源对象 (如 obj2、obj3) 的参数位置,接收到了无法转换为对象的参数的话,会忽略这个源对象参数。
    • 如果在这个过程中出现同名的属性(方法),后合并的属性(方法)会覆盖之前的同名属性(方法)

    3.4 Object 实例方法

    • Object.prototype.valueOf():返回当前对象对应的值。
    • Object.prototype.toString():返回当前对象对应的字符串形式。
    • Object.prototype.toLocaleString():返回当前对象对应的本地字符串形式。
    • Object.prototype.hasOwnProperty():判断某个属性是否为当前对象自身的属性,还是继承自原型对象的属性。
    • Object.prototype.isPrototypeOf():判断当前对象是否为另一个对象的原型。返回一个布尔值,看当前对象是否在指定对象的原型链上
    • Object.prototype.propertyIsEnumerable():判断某个属性是否可枚举。
    • Object.prototype.hasPrototypeProperty():从字面上就可以知道是检测原型对象上的属性。对象位于原型中,则返回true。

    Object.prototype.toString()

    • 数组、字符串、函数、Date 对象都分别部署了自定义的toString方法,覆盖了Object.prototype.toString方法。
    [1, 2, 3].toString(); // "1,2,3"
    
    • 1
    var obj = {};
    obj.toString(); // "[object Object]"
    
    • 1
    • 2
    • 上面代码调用空对象的toString方法,结果返回一个字符串object Object,其中第二个Object表示该值的构造函数。
    • 由于实例对象可能会自定义toString方法,覆盖掉Object.prototype.toString方法,所以为了得到类型字符串,最好直接使用Object.prototype.toString方法。通过函数的call方法,可以在任意值上调用这个方法,帮助我们判断这个值的类型。
    Object.prototype.toString.call(value);
    
    • 1
    • 不同数据类型的Object.prototype.toString方法返回值如下。

      • 数值:返回[object Number]
      • 字符串:返回[object String]
      • 布尔值:返回[object Boolean]
      • undefined:返回[object Undefined]
      • null:返回[object Null]
      • 数组:返回[object Array]
      • arguments 对象:返回[object Arguments]
      • 函数:返回[object Function]
      • Error 对象:返回[object Error]
      • Date 对象:返回[object Date]
      • RegExp 对象:返回[object RegExp]
      • 其他对象:返回[object Object]
    • 这就是说,Object.prototype.toString可以看出一个值到底是什么类型。

    Object.prototype.toString.call(2); // "[object Number]"
    Object.prototype.toString.call(""); // "[object String]"
    Object.prototype.toString.call(true); // "[object Boolean]"
    Object.prototype.toString.call(undefined); // "[object Undefined]"
    Object.prototype.toString.call(null); // "[object Null]"
    Object.prototype.toString.call(Math); // "[object Math]"
    Object.prototype.toString.call({}); // "[object Object]"
    Object.prototype.toString.call([]); // "[object Array]"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
  • 相关阅读:
    论文阅读:ECAPA-TDNN
    【JS】判断水仙花数
    聊聊Mybatis的插件接口之责任链模式
    支持目标打卡,活力三环让运动更有趣
    2023年深圳市绿色低碳产业扶持计划申报指南
    binder通信实现
    001数据安全传输-多端协议传输平台:Openssl安装和配置 - EVP代码测试
    让工程师拥有一台“超级”计算机——字节跳动客户端编译加速方案
    Go-知识sync map
    烧写最小linux失败,开机显示Wrong Ramdisk Image Format
  • 原文地址:https://blog.csdn.net/m0_49271518/article/details/127423393