JavaScript中对象方法及深拷贝

105 阅读9分钟

静态方法

  • 如何查看
console.dir(Object)
  • 复制对象 并且返回目标对象,如果源对象1和源对象2有重复属性(下标),后面会覆盖前面的 ,空元素不覆盖
  • for..in 不能遍历到的就叫做不可枚举
  • 不能复制不可枚举的属性和方法,不能复制原型和原型属性
Object.assign(目标对象,源对象1,源对象2...)
  • 案例
        var o1={a:1,b:3}
        var o2={c:7,f:3}
        var o3={...o1,...o2}
        console.log(o3) // {a: 1, b: 3, c: 7, f: 3}
​
        var a1=[1,2,3,4]
        var a2=[6,7,8,4]
        var a3=[...a1,...a2]
        console.log(a3) // (8[1, 2, 3, 4, 6, 7, 8, 4]
​
        var b1=Object.assign([],[1,2,3,4],[7,8,9,0])
        console.log(b1) // (4[7, 8, 9, 0]
  • 复制数组
        var arr=[1,2,3,4,5];
        var arr1=Object.assign([],arr);
        // arr[0]=10;  不会改变原数组的值
        console.log(arr1)
  • 数组覆盖
        var arr=[1,2,3,4,5];
        var arr2=[6,7,8]
        var arr1=Object.assign([],arr,arr2);
        // arr[0]=10;
        console.log(arr1) // 67845
        var arr=[1,2,3,4,5,6]
        var arr2=[,6,7,8]
        var arr1=Object.assign([],arr,arr2)
        arr[0]=10
        console.log(arr1) // (6[1, 6, 7, 8, 5, 6]

定义属性

  • defineProperty :直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
  • 基本使用
        var o={}
​
        Object.defineProperty(o,"c",{
            value:3,
            set:function(value){
                // setter属性
            },
            get:function(){
                //getter属性
            }
        })
        console.log(o)  // {c:3}
  • enumerable:true 是否可以枚举
  • configurable:true 是否可以删除或者修改对象该属性的描述对象
  • writable:true 是否可以修改
        Object.defineProperty(o,"c",{
            configurable:true,
            enumerable:true,
            writable:false,
            value:1
        });
  • setter和getter与value和writable冲突,使用setter和getter时不使用value和writable
  • 如果configurable:false ,那么无法使用delete来进行删除
  • 如果enumerable:false,不可枚举,控制台的颜色为浅色
  • 如果writable:false,不可修改,这个对象的属性变成一个常量
  • 当使用Object.keys 也无法获取不能枚举的key
        // 默认所有的属性都是false
        Object.defineProperty(o,"c",{
            value:2
        })
        console.log(o)
  • 设置多个属性
        var o={}
        Object.defineProperties(o,{
            a:{
                configurable:true, // 可删除
                value:1
            },
            b:{
                writable:true,  // 可修改
                value:2
            },
            c:{
                enumerable:true,    // 可枚举
                value:3
            }
        })
        console.log(delete o.a)  // 删除o.a 属性
        console.log(o.b=60)     // 将o.b 修改为60
        for(var key in o){
            console.log(key)
        }

entries

  • 将对象转换为一个二维数组,里面的数组第0项是key,第1项是value
  • 将对象转换为数组迭代器
        var o={a:1,b:2};
        console.log(Object.entries(o));

fromentries

  • 将迭代器转换为对象 一种是二维数组,另一种是map类型
    var o= Object.fromEntries([["a",1],["b",2]])
    console.log(o) //{a: 1, b: 2}
    var m=new Map();
    m.set("a",1);
    m.set("b",2);
    var o=Object.fromEntries(m);
    console.log(o)

freeze

  • 冻结:不能添加属性,不能修改属性,不能删除属性
        var o={a:1,b:2};
        Object.freeze(o);
        o.a=10;
        console.log(o)
  • 枚举类型创建
        const Mouse_Event={
            CLICK:"click",
            MOUSE_DOWN:"mousedown",
            MOUSE_UP:"mouseup",
            MOUSE_ENTER:"mouseenter",
            MOUSE_LEAVE:"mouseleave",
            MOUSE_MOVE:"mousemove",
            MOUSE_OUT:"mouseout",
            MOUSE_OVER:"mouseover",
            CONTEXT_MENU:"contextmenu"
        }
​
        Object.freeze(Mouse_Event);
        Mouse_Event.CONTEXT_MENU=1
  • 希望类里面不能更改数值,就将他使用冻结的方式设置为不可修改的只读模式
        class Box{
           static SPEED=3;
        }
       
        // 使用冻结的方式给类的静态属性设置为不可修改的只读模式
        Object.freeze(Box);
        Box.SPEED=4;
        console.log(Box.SPEED)
  • 判断是否被冻结
       var o={a:1};
       Object.freeze(o);
       console.log( Object.isFrozen(o))

seal

  • 密封的,不能添加新属性,也不能删除属性,但是可以修改属性值
       var o={a:1,b:2};
        Object.seal(o);
​
        o.c=3;
        delete o.a;
​
        o.a=10;
        console.log(o)

isSealed

  • 判断对象是否是密封的
        var o={r:3,d:5}
        Object.seal(o)
        console.log(Object.isSealed(o))

preventExtensions

  • 设置对象禁止扩展,不能添加新属性,但是可以删除和修改属性
        var o={a:1,b:2}
        Object.preventExtensions(o);
        o.c=3;
        // delete o.a;
        // o.a=10;
        console.log(o)

isExtensible

  • 判断对象是否禁止扩展
        var o={a:10,b:20}
        Object.preventExtensions(o)
        console.log(Object.isExtensible(o))

易错点

  • freeze(封闭):isFrozenisSealed是true isExtensible是false
  • seal(密封):isSealed是true isExtensible,isFrozen是false
        var o={a:1};
        Object.freeze(o);//isFrozen和isSealed 是true   isExtensible是false
        // Object.seal(o);isSealed 是true   isExtensible,isFrozen是false
        console.log(Object.isFrozen(o));

获取所有对象的属性

  • getOwnPropertyNames:获取对象的所有key是字符串对象属性(不管原型属性),包括不可枚举的属性
       
        var o={};
​
         Object.defineProperties(o,{
            a:{
                configurable:true,
                value:1
            },
            b:{
                writable:true,
                value:2
            },
            c:{
                enumerable:true,
                value:3
            }
        })
​
        // console.log(Object.keys(o))
​
        // 获取对象的所有key是字符串的对象属性,包括不可枚举的属性
        var names=Object.getOwnPropertyNames(o);
        console.log(names) // a,b,c
  • getOwnPropertySymbols:获取对象的所有symbol类型的对象属性,包括不可枚举属性
        var o1={d:3,e:4}
        var o=Object.create(o1);
​
         Object.defineProperties(o,{
            a:{
                configurable:true,
                value:1
            },
            b:{
                writable:true,
                value:2
            },
            c:{
                enumerable:true,
                value:3
            },
            [Symbol()]:{
                value:4
            }
        })        
        var symbolNames=Object.getOwnPropertySymbols(o);
        console.log(symbolNames)  // [Symbol()]
  • 通过Reflect方法获取对象的所有属性,什么类型都可以
console.log( Reflect.ownKeys(o)) // reflect是object对象的映射类型
// (4['a', 'b', 'c', Symbol()]
  • getOwnPropertyDescriptor:获取对象指定属性的描述对象
    var desc=Object.getOwnPropertyDescriptor(o,"a");
    console.log(desc)
  • 重新赋值
        var o1 = { d: 3, e: 4 }
        var o = Object.create(o1);
        Object.defineProperties(o, {
            a: {
                configurable: true,
                value: 1
            },
            b: {
                writable: true,
                value: 2
            },
            c: {
                enumerable: true,
                value: 3
            },
            [Symbol()]: {
                value: 4
            }
        })   
        // 获取对象指定属性的描述对象
        var desc=Object.getOwnPropertyDescriptor(o,"a")
        // console.log(desc)
​
        desc.writable=true
        desc.value=10
        Object.defineProperty(o,"a",{
            configurable:desc.configurable,
            writable:true,  // 可以被修改,得到a:10
            enumerable:desc.enumerable,
            value:10
        })
        console.log(o) // {c: 3, a: 10, b: 2, Symbol(): 4}
  • getOwnPropertyDescriptors:获取对象所有属性的描述对象
    var descs=Object.getOwnPropertyDescriptors(o);
    console.log(descs)

对象原型

  • setPrototypeOf:设置对象原型为另一个原型
  • Object.setPrototypeOf(目标对象,原型对象);
    //   Object.setPrototypeOf(目标对象,原型对象);
    var o1={b:20,c:30};
    var o={a:1};
    // Object.setPrototypeOf(o1,o);
    // console.log(o1)
​
  • 要想修改原型就使用setPrototypeOf,不能直接使用proto
  • getPrototypeOf:获取对象的原型对象
        // 获取对象的原型对象
        var o={a:1}
        var o1=Object.create(o)
        o1.b=2
        console.log(o1.__proto__) // {a: 1}
        // 获取o1的原型对象
        console.log(Object.getPrototypeOf(o1)) //{a: 1}
  • hasOwn/hasOwnProperty:判断对象下是否有这个对象属性,对象属性返回true,没有或者原型属性返回false
    var o={a:1,b:2};
    var o1=Object.create(o);
    o1.c=3;
    o1.d=4;
​
​
    // 判断对象下是否有这个对象属性
    console.log(Object.hasOwn(o1,"a"))
    console.log(Object.hasOwn(o1,"c"))
    console.log(o1.hasOwnProperty("c")) // 两者方法相同
  • is:判断值是否是绝对相等,与===一样,可以比较NaN
    var a=3;
    var b="3";
    console.log(Object.is(a,b))
  • keys/value:不能获取原型上的值
    var o={a:1,b:2};
    var o1=Object.create(o);
    o1.c=3;
    o1.d=4;
    console.log(Object.keys(o1));    // ['c', 'd']
    console.log(Object.values(o1));  //  [3, 4]

对象方法

  • constructor :实例化构造函数

  • hasOwnProperty :判断这个属性是不是对象的对象属性,原型属性返回false

  • isPrototypeOf :判断这个对象是否是另一个对象的原型

    • 格式:原型对象.isPrototypeOf(对象)
    var o={a:1,b:2};
    var o1=Object.create(o);
    o1.c=3;
    o1.d=4;
    // 判断o是否是o1的原型对象  
    console.log(o.isPrototypeOf(o1)) // T
    console.log(o1.isPrototypeOf(o)) // F
  • 判断原型内容
    var div=document.createElement("div");
    console.log(HTMLDivElement.prototype.isPrototypeOf(div)) // T
    console.log(HTMLElement.prototype.isPrototypeOf(div))   // T
    console.log(Element.prototype.isPrototypeOf(div))   // T
    console.log(Node.prototype.isPrototypeOf(div))  // T
    console.log(EventTarget.prototype.isPrototypeOf(div)) /// T
    console.log(Object.prototype.isPrototypeOf(div))    // T
  • propertyIsEnumerable :判断是否可枚举
        var o={a:1,b:2}
        Object.defineProperty(o,"c",{
            value:3
        })
        console.log(o.propertyIsEnumerable("a"))    // T
        console.log(o.propertyIsEnumerable("c"))    // F

setter和getter

  • 属性不会执行多条语句,只能存储值
  • 方法会执行多条语句,但是方法不能存储值
  • 希望既可以像属性一样存储,也可以在存储值后执行多条语句
// 基本格式
    var o={
        _list:[],
        set list(value){
            console.log(value)
            this._list=value;
        },
        get list(){
            return this._list;
        }
    }
​
    o.list=[1,2,3];
​

setter

  • 设置 set的方法中有且只有一个参数
  • 获取 get 的方法中不能有参数,并且必须使用 return 返回一个值
        var o={
            // 设置 set的方法中有且只有一个参数
            set d(value){
                console.log(value)  // 10
            },
            // 获取 get方法中不能有参数,并且必须使用return 返回一个值
            get d(){
                return 1
            }
        }
​
​
        // o.d(1);错误
        o.d=10;  // 等号后面的值会赋值给 set d(value) 中的value
        console.log(o.d) // 1
        var o={
            _a:1,
            set a(value){
                console.log('aaaa') // aaa
                this._a=value;
            },
            get a(){
                return this._a;
            }
        }
​
        o.a=10;
        console.log(o.a) // 10
  • 方块移动案例
     <div></div>
    <style>
        div{
            width: 50px;
            height: 50px;
            background-color: red;
            position: absolute;
            left: 0;
            top: 0;
        }
    </style>
​
        var div=document.querySelector('div')
        div._x=0
        div._y=0
        // 直接在一个对象上定义新的属性或修改现有属性
        Object.defineProperties(div,{
            x:{
                // 设置x的值
                set(value){
                    // 这里的value值就是外面div传进来的值
                    this._x=value
                    // 左边的值就是value值
                    this.style.left=value+"px"
                },
                get(){
                    // 直接获取_x的值,返回上面得到的value数值
                    return this._x
                }
            },
            y:{
                set(value){
                    this._y=value
                    this.style.top=value+"px"
                },
                get(){
                    return this._y
                }
            }
        })
​
        setInterval(() => {
            div.x++
        }, 16);
  • setter、getter 语法糖 访问器属性
  • 数据驱动显示(set/get)
  • 当只写了set方法时,这个属性不可读,只能设置
  • 当只写了get方法时,这个属性只读,不能修改
  • 把类的某个静态属性设置为只读属性
    class Box{
        // 由于书写了get方法,所以这个属性是只读属性
        static get EVENT_ID(){
            return "event_id";
        }
    }
​
    console.log(Box.EVENT_ID)
    Box.EVENT_ID="events"   // 修改无效
  • 数组也可以使用freeze:冻结!!!!无法修改
        var arr=[1,2,3,4,5]
        Object.freeze(arr)
        arr[0]=10
        // arr.push(3)
        console.log(arr)
  • 关于冻结的易错点

            var o={
                _list:[1,2,3,4,5],
                set list(value){
                    console.log(value,"aaa")
                    this._list=value
                },
                get list(){
                    return this._list
                }
            }
            Object.freeze(o.list)
            o.list=[2,3,4,5,6]  // 更改引用地址,输出[2,3,4,5,6]
            o.list.push(6)  // 冻结引用地址,报错,无法添加
            
    ​
    ​
            Object.freeze(o)
            o.list=[1,2,3,4]    // [1, 2, 3, 4] 'aaa'
            console.log(o.list) //  [1, 2, 3, 4, 5]
    ​
            o.list.push(18)     // [1, 2, 3, 4, 5, 18]
            o.list=o.list.concat(10)    // [1, 2, 3, 4, 5, 10] 'aaa'
            o.list=o.list.slice(0,-1)   // [1, 2, 3, 4] 'aaa'
    

对象深复制

        function deepClone(source,list,target){
            // 如果没有给入目标对象,创建一个新的目标对象
            if(target===undefined){
                switch(true){
                    // 如果要复制的对象是HTMLElement,则直接深复制Node
                    case source instanceof HTMLElement:
                        target=source.cloneNode(true);
                        break;
                    // 如果要复制的对象是类型化数组  
                    case source.buffer && source.buffer instanceof ArrayBuffer:
                        // 如果要复制是日期对象
                    case source instanceof Date:
                        // 如果要复制的是正则
                    case source instanceof RegExp:
                        // 如果要复制是map
                    case source instanceof Map:
                        // 如果要复制的是set
                    case source instanceof Set:
                        // new 要复制对象的构造函数   例如  a=/a/g  a.constructor-->Regexp  b=new Regexp(a)
                        target=new source.constructor(source);
                        break;
                    case typeof source==="function":
                        var arr= source.toString().replace(/\n|\t/gi,"").match(/((.*?))\s*{(.*)}/i).slice(1);
                        target=new Function(arr[0].trim(),arr[1].trim())
                        break;
                    case !(source instanceof source.constructor):
                            // 如果要复制的是prototype对象,这个对象下自动会有一个constructor,我们创建这个prototype不能使用new constructor,所以直接创建一个对象
                        // 如果要复制是一个函数,使用正则表达式先把这个函数转换为字符串,并且删除所有换行,然后正则表达式通过群组提出参数和函数{}里面的内容
                        var arr= source.constructor.toString().replace(/\n|\t/gi,"").match(/((.*?))\s*{(.*)}/i).slice(1);
                    //   然后同new Function()将函数的参数和函数内容放入这里创建一个新函数
                        target=new Function(arr[0].trim(),arr[1].trim())
                        break;
                    default:
                        // 其他类型都是根据对象的构造函数创建
                       target=new source.constructor()
                       
                }
            }
            // 对象中如果出现互相引用关系的值,很容易出现迭代死循环,我们创建一个map用了存储,原值和目标值
            if(list===undefined){
                list=new Map();
                // 先把当前这个对象的原值和目标值存储
                list.set(source,target);
            }
            // 获取源对象下所有属性
            var keys=Reflect.ownKeys(source);
            // 遍历所有属性
            for(var i=0;i<keys.length;i++){
                // 获取当前这个属性的属性描述对象
                var desc=Object.getOwnPropertyDescriptor(source,keys[i]);
                // 判断这个属性描述对象是不是引用类型,并且不是null
                if(desc.value instanceof Object && desc.value!=null){
                    var targetValue;
                    // 判断如果这个源对象在列表已经存在,就将列表中已经复制的模板对象直接复制
                    if(list.has(desc.value)){
                        targetValue=list.get(desc.value);
                    }else{
                        // 如果不存在,判断是不是constructor,是constructor跳出
                       if(keys[i]==="constructor"){
                            continue;
                       }
                    //   深 复制一个新的目标对象
                        targetValue= deepClone(desc.value,list);
                        // 判断是否是原型,如果是原型,这个创建一个构造函数放在里面
                        if(keys[i]==="prototype"){
                            Object.defineProperty(targetValue,"constructor",{
                             value:targetValue
                         })
                     }
                        // 将当前的源对象和创建出的目标存储在map中
                        list.set(desc.value,targetValue);
                    }
                    // 定义当前目标对象中的对应属性的值
                    Object.defineProperty(target,keys[i],{
                        configurable:desc.configurable,
                        writable:desc.writable,
                        enumerable:desc.enumerable,
                        value:targetValue
                    })
                }else{
                    // 非引用类型值直接赋值
                    Object.defineProperty(target,keys[i],desc);
                }
            }
            return target;
        }
​