0
点赞
收藏
分享

微信扫一扫

【JS】131-重温基础:使用对象

【JS】131-重温基础:使用对象_赋值

本文是 重温基础 系列文章的第十二篇。今日感受:需要总结下2018。

这几天,重重的感冒发烧,在家休息好几天,伤···。

本章节复习的是JS中对象的使用,这是重点。

前置知识:JavaScrip的设计是一个简单的基于对象的范式。对象是一系列属性的集合,一个属性包含一个键和一个值,也叫键值对若一个属性的值,是一个函数,则称这个属性为方法。

一个对象,可以有很多方法,就像一个杯子,可以有颜色,重量,形状等属性。

注意:对象的名称,对大小写敏感。

1.创建对象

本文主要是复习对象的使用,至于对象的创建,下面这里简单介绍一下常见的创建对象的方法:

创建方法1

  1. let obj = new Object();  // 声明一个空对象

创建方法2

  1. let obj = {};            // 声明一个空对象

上面的 ​name​​是对象 ​obj​​中的一个属性,对应的值为 ​"leo"

2.设置对象属性和访问属性

设置对象属性,也有两种方法:直接设置

  1. let obj = {};
  2. obj.name = "leo";        // 为对象添加属性和值

创建时设置

  1. let obj = {name : 'leo'};
  2. obj.name = "leo";        // 为对象添加属性和值

当一个对象定义了一个属性,我们可以用点符号( ​.​)来访问它。

  1. obj.name; // "leo"

注意

  • 属性的另外一种写法:
  1. let obj = {};
  2. let n = "name";
  3. obj[n] = "leo";   // 属性名使用变量
  4. obj["height"] = 188;     // 字符串
  5. obj["age" + "Is"] = 15;  // 字符串拼接
  6. console.log(obj);
  7. /*
  8. obj = {
  9.    ageIs: 15
  10.    height: 188
  11.    name: "leo"
  12. }
  13. */
  • 属性的值也可以是个方法:
  1. let obj = {};
  2. obj.getTime = function(){
  3.    return new Date();
  4. }
  5. obj.getTime();  // 访问属性的方法
  6. //Wed Jan 02 2019 21:07:59 GMT+0800 (中国标准时间)

3.枚举对象的所有属性

从 ECMAScript 5 开始,有三种原生的方法用于列出或枚举对象的属性:

3.1 for...in 循环

该方法依次访问一个对象及其原型链中所有可枚举的属性。

  1. let obj = {a:1, b:2, c:3};
  2. for (let i in obj) {
  3.  console.log("obj." + i + " = " + obj[i]);
  4. }
  5. /*
  6. "obj.a = 1"
  7. "obj.b = 2"
  8. "obj.c = 3"
  9. */

3.2 Object.keys(o)

该方法返回一个对象 ​o​ 自身包含(不包括原型中)的所有属性的名称的数组。

  1. let obj = {a:1, b:2, c:3};
  2. let arr = Object.keys(obj);
  3. console.log(arr);
  4. /*
  5. ["a", "b", "c"]
  6. */
  7. arr.forEach(function(val,index,array){
  8.    console.log("key:" + val+",val:"+obj[val])
  9. })
  10. /*
  11. key:a,val:1
  12. key:b,val:2
  13. key:c,val:3
  14. */

3.3 Object.getOwnPropertyNames(o)

该方法返回一个数组,它包含了对象 ​o​ 所有拥有的属性(无论是否可枚举)的名称。

  1. let obj = {a:1, b:2, c:3};
  2. let arr = Object.getOwnPropertyNames(obj);
  3. console.log(arr);
  4. /*
  5. ["a", "b", "c"]
  6. */
  7. arr.forEach(function(val,index,array){
  8.    console.log("key:" + val+",val:"+obj[val])
  9. })
  10. /*
  11. key:a,val:1
  12. key:b,val:2
  13. key:c,val:3
  14. */

4.ES6新增:对象的拓展

4.1 属性的简洁表示

  1. let a = 'a1';
  2. let b = { a };  // b => { a : 'a1' }
  3. // 等同于
  4. let b = { a : a };
  5. function f(a, b){
  6.    return {a, b};
  7. }
  8. // 等同于
  9. function f (a, b){
  10.    return {a:a ,b:b};
  11. }
  12. let a = {
  13.    fun () {
  14.        return 'leo';
  15.    }
  16. }
  17. // 等同于
  18. let a = {
  19.    fun : function(){
  20.        return 'leo';
  21.    }
  22. }

4.2 属性名表达式

JavaScript​提供2种方法定义对象的属性

  1. // 方法1 标识符作为属性名
  2. a.f = true;
  3. // 方法2 字符串作为属性名
  4. a['f' + 'un'] = true;

延伸出来的还有:

  1. let a = 'hi leo';
  2. let b = {
  3.    [a]: true,
  4.    ['a'+'bc']: 123,
  5.    ['my' + 'fun'] (){
  6.        return 'hi';
  7.    }
  8. };
  9. // b.a => undefined ; b.abc => 123 ; b.myfun() => 'hi'
  10. // b[a] => true ; b['abc'] => 123 ; b['myfun'] => ƒ ['my' + 'fun'] (){ return 'hi'; }

注意属性名表达式不能与简洁表示法同时使用,否则报错。

  1. // 报错
  2. let a1 = 'aa';
  3. let a2 = 'bb';
  4. let b1 = {[a1]};
  5. // 正确
  6. let a1 = 'aa';
  7. let b1 = { [a1] : 'bb'};

4.3 Object.is()

Object.is()​ 用于比较两个值是否严格相等,在ES5时候只要使用相等运算符( ​==​)和严格相等运算符( ​===​)就可以做比较,但是它们都有缺点,前者会自动转换数据类型,后者的 ​NaN​​不等于自身,以及 ​+0​​等于 ​-0

  1. Object.is('a','a');   // true
  2. Object.is({}, {});    // false
  3. // ES5
  4. +0 === -0 ;           // true
  5. NaN === NaN;          // false
  6. // ES6
  7. Object.is(+0,-0);     // false
  8. Object.is(NaN,NaN);   // true

4.4 Object.assign()

Object.assign()​​方法用于对象的合并,将原对象的所有可枚举属性复制到目标对象。基础用法第一个参数是目标对象,后面参数都是源对象

  1. let a = {a:1};
  2. let b = {b:2};
  3. Object.assign(a,b);  // a=> {a:1,b:2}

注意

  • 若目标对象与源对象有同名属性,则后面属性会覆盖前面属性。
  1. let a = {a:1, b:2};
  2. let b = {b:3, c:4};
  3. Object.assign(a, b); // a => {a:1, b:3, c:4}
  • 若只有一个参数,则返回该参数。
  1. let a = {a:1};
  2. Object.assign(a) === a;  // true
  • 若参数不是对象,则先转成对象后返回。
  1. typeof Object.assign(2); // 'object'
  • 由于 ​undefined​​或 ​NaN​无法转成对象,所以做为参数会报错。
  1. Object.assign(undefined) // 报错
  2. Object.assign(NaN);      // 报错
  • Object.assign()​实现的是浅拷贝。

Object.assign()​拷贝得到的是这个对象的引用。这个对象的任何变化,都会反映到目标对象上面。

  1. let a = {a: {b:1}};
  2. let b = Object.assign({},a);
  3. a.a.b = 2;
  4. console.log(b.a.b);  // 2
  • 将数组当做对象处理,键名为数组下标,键值为数组下标对应的值。
  1. Object.assign([1, 2, 3], [4, 5]); // [4, 5, 3]

5.ES8新增:Object.values(),Object.entries()

ES7中新增加的 ​Object.values()​​和 ​Object.entries()​​与之前的 ​Object.keys()​​类似,返回数组类型。回顾下 ​​Object.keys()

  1. var a = { f1: 'hi', f2: 'leo'};
  2. Object.keys(a); // ['f1', 'f2']

5.1 Object.values()

返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历属性的键值。

  1. let a = { f1: 'hi', f2: 'leo'};
  2. Object.values(a); // ['hi', 'leo']

如果参数不是对象,则返回空数组:

  1. Object.values(10);   // []
  2. Object.values(true); // []

5.2 Object.entries()

返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历属性的键值对数组。

  1. let a = { f1: 'hi', f2: 'leo'};
  2. Object.entries(a); // [['f1','hi'], ['f2', 'leo']]
  • 用途1
    遍历对象属性。
  1. let a = { f1: 'hi', f2: 'leo'};
  2. for (let [k, v] of Object.entries(a)){
  3.    console.log(
  4.        `${JSON.stringfy(k)}:${JSON.stringfy(v)}`
  5.    )
  6. }
  7. // 'f1':'hi'
  8. // 'f2':'leo'
  • 用途2: 将对象转为真正的Map结构。
  1. let a = { f1: 'hi', f2: 'leo'};
  2. let map = new Map(Object.entries(a));

手动实现 ​Object.entries()​方法:

  1. // Generator函数实现:  
  2. function* entries(obj){
  3.    for (let k of Object.keys(obj)){
  4.        yield [k ,obj[k]];
  5.    }
  6. }
  7. // 非Generator函数实现:
  8. function entries (obj){
  9.    let arr = [];
  10.    for(let k of Object.keys(obj)){
  11.        arr.push([k, obj[k]]);
  12.    }
  13.    return arr;
  14. }

6.ES8新增:Object.getOwnPropertyDescriptors()

之前有 ​Object.getOwnPropertyDescriptor​​方法会返回某个对象属性的描述对象,新增的 ​Object.getOwnPropertyDescriptors()​方法,返回指定对象所有自身属性(非继承属性)的描述对象,所有原对象的属性名都是该对象的属性名,对应的属性值就是该属性的描述对象

  1. let a = {
  2.    a1:1,
  3.    get f1(){ return 100}
  4. }
  5. Object.getOwnPropetyDescriptors(a);
  6. /*
  7. {
  8.    a:{ configurable:true, enumerable:true, value:1, writeable:true}
  9.    f1:{ configurable:true, enumerable:true, get:f, set:undefined}
  10. }
  11. */

实现原理:

  1. function getOwnPropertyDescriptors(obj) {
  2.  const result = {};
  3.  for (let key of Reflect.ownKeys(obj)) {
  4.    result[key] = Object.getOwnPropertyDescriptor(obj, key);
  5.  }
  6.  return result;
  7. }

引入这个方法,主要是为了解决 ​Object.assign()​​无法正确拷贝 ​get​​属性和 ​set​属性的问题。

  1. let a = {
  2.    set f(v){
  3.        console.log(v)
  4.    }
  5. }
  6. let b = {};
  7. Object.assign(b, a);
  8. Object.a(b, 'f');
  9. /*
  10. f = {
  11.    configurable: true,
  12.    enumable: true,
  13.    value: undefined,
  14.    writeable: true
  15. }
  16. */

value​​为 ​undefined​​是因为 ​Object.assign​​方法不会拷贝其中的 ​get​​和 ​set​​方法,使用 ​getOwnPropertyDescriptors​​配合 ​Object.defineProperties​方法来实现正确的拷贝:

  1. let a = {
  2.    set f(v){
  3.        console.log(v)
  4.    }
  5. }
  6. let b = {};
  7. Object.defineProperties(b, Object.getOwnPropertyDescriptors(a));
  8. Object.getOwnPropertyDescriptor(b, 'f')
  9. /*
  10.    configurable: true,
  11.    enumable: true,
  12.    get: undefined,
  13.    set: function(){...}
  14. */

Object.getOwnPropertyDescriptors​​方法的配合 ​Object.create​方法,将对象属性克隆到一个新对象,实现浅拷贝。

  1. const clone = Object.create(Object.getPrototypeOf(obj),
  2.  Object.getOwnPropertyDescriptors(obj));
  3. // 或者
  4. const shallowClone = (obj) => Object.create(
  5.  Object.getPrototypeOf(obj),
  6.  Object.getOwnPropertyDescriptors(obj)
  7. );

7.ES9新增:对象的拓展运算符

7.1 介绍

对象的拓展运算符,即对象的Rest/Spread属性,可将对象解构赋值用于从一个对象取值,搜键值对分配到指定对象上,与数组的拓展运算符类似:

  1. let  {x, y, ...z} = {x:1, y:2, a:3, b:4};
  2. x;  // 1
  3. y;  // 2
  4. z;  // {a:3, b:4}

对象的解构赋值要求等号右边必须是个对象,所以如果等号右边是 ​undefined​​或 ​null​就会报错无法转成对象。

  1. let {a, ...b} = null;      // 运行时报错
  2. let {a, ...b} = undefined; // 运行时报错

解构赋值必须是最后一个参数,否则报错。

  1. let {...a, b, c} = obj;     // 语法错误
  2. let {a, ...b, c} = obj;     // 语法错误

注意

  • 1.解构赋值是浅拷贝。
  1. let a = {a1: {a2: 'leo'}};
  2. let {...b} = a;
  3. a.a1.a2 = 'leo';
  4. b.a1.a2 = 'leo';
  • 2.拓展运算符的解构赋值,不能复制继承自原型对象的属性。
  1. let o1 = { a: 1 };
  2. let o2 = { b: 2 };
  3. o2.__proto__ = o1;
  4. let { ...o3 } = o2;
  5. o3;    // { b: 2 }
  6. o3.a;  // undefined

7.2 使用场景

  • 1.取出参数对象所有可遍历属性,拷贝到当前对象中。
  1. let a = { a1:1, a2:2 };
  2. let b = { ...a };
  3. b;   // { a1:1, a2:2 }
  4. // 类似Object.assign方法
  • 2.合并两个对象。
  1. let a = { a1:1, a2:2 };
  2. let b = { b1:11, b2:22 };
  3. let ab = { ...a, ...b }; // {a1: 1, a2: 2, b1: 11, b2: 22}
  4. // 等同于
  5. let ab = Object.assign({}, a, b);
  • 3.将自定义属性放在拓展运算符后面,覆盖对象原有的同名属性。
  1. let a = { a1:1, a2:2, a3:3 };
  2. let r = { ...a, a3:666 };  
  3. // r {a1: 1, a2: 2, a3: 666}
  4. // 等同于
  5. let r = { ...a, ...{ a3:666 }};
  6. // r {a1: 1, a2: 2, a3: 666}
  7. // 等同于
  8. let r = Object.assign({}, a, { a3:666 });
  9. // r {a1: 1, a2: 2, a3: 666}
  • 4.将自定义属性放在拓展运算符前面,就会成为设置新对象的默认值。
  1. let a = { a1:1, a2:2 };
  2. let r = { a3:666, ...a };
  3. // r {a3: 666, a1: 1, a2: 2}
  4. // 等同于
  5. let r = Object.assign({}, {a3:666}, a);
  6. // r {a3: 666, a1: 1, a2: 2}
  7. // 等同于
  8. let r = Object.assign({a3:666}, a);
  9. // r {a3: 666, a1: 1, a2: 2}
  • 5.拓展运算符后面可以使用表达式。
  1. let a = {
  2.    ...(x>1? {a:!:{}),
  3.    b:2
  4. }
  • 6.拓展运算符后面如果是个空对象,则没有任何效果。
  1. {...{}, a:1};  // {a:1}
  • 7.若参数是 ​null​​或 ​undefined​则忽略且不报错。
  1. let a = { ...null, ...undefined }; // 不报错
  • 8.若有取值函数 ​get​则会执行。
  1. // 不会打印 因为f属性只是定义 而不没执行
  2. let a = {
  3.    ...a1,
  4.    get f(){console.log(1)}
  5. }
  6. // 会打印 因为f执行了
  7. let a = {
  8.    ...a1,
  9.    ...{
  10.        get f(){console.log(1)}
  11.    }
  12. }

参考资料

1.MDN 使用对象2.W3school JavaScript 对象

【JS】131-重温基础:使用对象_运算符_02

举报

相关推荐

0 条评论