• 【重温基础】12.使用对象


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

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

    系列目录:

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

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

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

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

    1.创建对象

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

    创建方法1

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

    创建方法2

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

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

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

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

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

    创建时设置

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

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

    obj.name; // "leo"
    

    注意

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

    3.枚举对象的所有属性

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

    3.1 for…in 循环

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

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

    3.2 Object.keys(o)

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

    let obj = {a:1, b:2, c:3};
    let arr = Object.keys(obj);
    console.log(arr);
    /*
    ["a", "b", "c"]
    */
    arr.forEach(function(val,index,array){
    	console.log("key:" + val+",val:"+obj[val])
    })
    /*
    key:a,val:1
    key:b,val:2
    key:c,val:3
    */
    

    3.3 Object.getOwnPropertyNames(o)

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

    let obj = {a:1, b:2, c:3};
    let arr = Object.getOwnPropertyNames(obj);
    console.log(arr);
    /*
    ["a", "b", "c"]
    */
    arr.forEach(function(val,index,array){
    	console.log("key:" + val+",val:"+obj[val])
    })
    /*
    key:a,val:1
    key:b,val:2
    key:c,val:3
    */
    

    4.ES6新增:对象的拓展

    4.1 属性的简洁表示

    let a = 'a1';
    let b = { a };  // b => { a : 'a1' }
    // 等同于
    let b = { a : a };
    
    function f(a, b){
        return {a, b}; 
    }
    // 等同于
    function f (a, b){
        return {a:a ,b:b};
    }
    
    let a = {
        fun () {
            return 'leo';
        }
    }
    // 等同于
    let a = {
        fun : function(){
            return 'leo';
        }
    }
    

    4.2 属性名表达式

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

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

    延伸出来的还有:

    let a = 'hi leo';
    let b = {
        [a]: true,
        ['a'+'bc']: 123,
        ['my' + 'fun'] (){
            return 'hi';
        }
    };
    // b.a => undefined ; b.abc => 123 ; b.myfun() => 'hi'
    // b[a] => true ; b['abc'] => 123 ; b['myfun'] => ƒ ['my' + 'fun'] (){ return 'hi'; }
    

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

    // 报错
    let a1 = 'aa';
    let a2 = 'bb';
    let b1 = {[a1]};
    
    // 正确
    let a1 = 'aa';
    let b1 = { [a1] : 'bb'};
    

    4.3 Object.is()

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

    Object.is('a','a');   // true
    Object.is({}, {});    // false
    
    // ES5
    +0 === -0 ;           // true
    NaN === NaN;          // false
    
    // ES6
    Object.is(+0,-0);     // false
    Object.is(NaN,NaN);   // true
    

    4.4 Object.assign()

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

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

    注意

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

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

    let a = {a: {b:1}};
    let b = Object.assign({},a);
    a.a.b = 2;
    console.log(b.a.b);  // 2
    
    • 将数组当做对象处理,键名为数组下标,键值为数组下标对应的值。
    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()

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

    5.1 Object.values()

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

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

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

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

    5.2 Object.entries()

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

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

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

    // Generator函数实现:  
    function* entries(obj){
        for (let k of Object.keys(obj)){
            yield [k ,obj[k]];
        }
    }
    
    // 非Generator函数实现:
    function entries (obj){
        let arr = [];
        for(let k of Object.keys(obj)){
            arr.push([k, obj[k]]);
        }
        return arr;
    }
    

    6.ES8新增:Object.getOwnPropertyDescriptors()

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

    let a = {
        a1:1,
        get f1(){ return 100}
    }
    Object.getOwnPropetyDescriptors(a);
    /*
    { 
        a:{ configurable:true, enumerable:true, value:1, writeable:true}
        f1:{ configurable:true, enumerable:true, get:f, set:undefined}
    }
    */
    

    实现原理:

    function getOwnPropertyDescriptors(obj) {
      const result = {};
      for (let key of Reflect.ownKeys(obj)) {
        result[key] = Object.getOwnPropertyDescriptor(obj, key);
      }
      return result;
    }
    

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

    let a = {
        set f(v){
            console.log(v)
        }
    }
    let b = {};
    Object.assign(b, a);
    Object.a(b, 'f');
    /*
    f = {
        configurable: true,
        enumable: true,
        value: undefined,
        writeable: true
    }
    */
    

    valueundefined是因为Object.assign方法不会拷贝其中的getset方法,使用getOwnPropertyDescriptors配合Object.defineProperties方法来实现正确的拷贝:

    let a = {
        set f(v){
            console.log(v)
        }
    }
    let b = {};
    Object.defineProperties(b, Object.getOwnPropertyDescriptors(a));
    Object.getOwnPropertyDescriptor(b, 'f')
    /*
        configurable: true,
        enumable: true,
        get: undefined,
        set: function(){...}
    */
    

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

    const clone = Object.create(Object.getPrototypeOf(obj),
      Object.getOwnPropertyDescriptors(obj));
    
    // 或者
    const shallowClone = (obj) => Object.create(
      Object.getPrototypeOf(obj),
      Object.getOwnPropertyDescriptors(obj)
    );
    

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

    7.1 介绍

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

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

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

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

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

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

    注意

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

    7.2 使用场景

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

    参考资料

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


    本部分内容到这结束

    Author 王平安
    E-mail pingan8787@qq.com
    博 客 www.pingan8787.com
    微 信 pingan8787
    每日文章推荐 https://github.com/pingan8787/Leo_Reading/issues
    JS小册 js.pingan8787.com

    bg

    个人博客:http://www.pingan8787.com 微信公众号【前端自习课】和千万网友一起,每日清晨,享受一篇前端优秀文章。 目前已连续推送文章 600+ 天,愿每个人的初心都能一直坚持下去!
  • 相关阅读:
    SQL语句——入门级
    使用Java Servlet进行简单登录
    Java简单聊天室
    Java网络编程(二)关于Socket的一些个人想法
    Java网络编程(一)
    Java多线程(三)锁对象和线程池
    Java多线程(二)同步与等待唤醒
    Java多线程(一)初步了解
    IO流(三)其他流与File类
    IO流(二)字符流
  • 原文地址:https://www.cnblogs.com/pingan8787/p/11838210.html
Copyright © 2020-2023  润新知