likes
comments
collection

你应该知道的关于Object的一些方法

作者站长头像
站长
· 阅读数 10

前言

最近接触了一下vue3里面的关于方法的源码,里面用到了很多对象的方法,看着里面的一些方法,所以就想着一边理解一边学习,希望理解知识点并且以后再次看的时候印象更深刻。想着按照增删改这种方式来学习,也方便记忆。下面是一些看vue3源码里面需要用到的object方法。

在 JavaScript 中,几乎所有的对象都是 Object 类型的实例,它们都会从 Object.prototype 继承属性和方法,虽然大部分属性都会被覆盖(shadowed)或者说被重写了(overridden),Object 还可以被故意的创建,但是这个对象并不是一个“真正的对象”。

对象初始化(增)

可以通过new Object()Object.create()等方法,或者使用字面量标记(初始化标记)初始化对象。

  • 构造函数new Object() ,构造函数将给定的值包装为一个新对象

    • 如果给定的值是 null 或 undefined, 它会创建并返回一个空对象
let emptyObject = new Object()  // {}
let emptyObject = new Object(undefined) // {}
let emptyObject = new Object(null) // {}

const valueObject1 = new Object({"name":"沐阳vae"})
console.log(valueObject1) //{name: '沐阳vae'}

const valueObject2 = new Object()
valueObject.name = "沐阳"
console.log(valueObject2) // {name: '沐阳'}
  • 静态方法

    1,Object.assign

    语法:Object.assign(target, ...sources) ,不会在 sources 对象值null或undefined时抛出错误

    const target = { a: 1, b: 2 };
    const source = { b: 4, c: 5 };
    const returnTarget = Object.assign(target, source);
    console.log(target) // {a: 1, b: 4, c: 5}
    console.log(returnTarget) // {a: 1, b: 4, c: 5}
    console.log(source) //{ b: 4, c: 5 }
    

    Object.assign()通过复制一个或多个对象来创建一个新的对象,Object.assign()  复制创建出来的对象是浅拷贝的,只会复制属性值。假如源对象是一个对象的引用,它只会复制其引用值。

    let obj = {a: 1, b: { c: 0}}
    let newObj = Object.assign({}, obj)
    console.log(newObj) // {a: 1, b: { c: 0}} ,浅拷贝,复制的是引用值,a复制的是1,b复制的就是引用地址了。
    obj.a = 3
    console.log(obj) // {a: 3, b: { c: 0}}
    console.log(newObj) // {a: 1, b: { c: 0}}
    
    // 后面在加上这里obj.b.c = 2,改变之后,整个引用地址里面的属性值也被改变了
    obj.b.c = 2
    console.log(obj) // {a: 3, b: { c: 2}}
    console.log(newObj) // {a: 1, b: { c: 2}}
    
    // 使用JSON.parse(JSON.stringify())进行深拷贝
    let deepObj = JSON.parse(JSON.stringify(obj))
    obj.a = 4
    obj.b.c = 4
    console.log(JSON.stringify(obj3)) // {"a":3,"b":{"c":2}}
    console.log(obj) // {"a":4,"b":{"c":4}}
    

    原型链上的属性和不可枚举属性不能被复制

    const obj = Object.create({ foo: 1 }, { 
      // foo 是对象原型链上的属性.
      notEnum: {
        value: 2  //没有设置enumerable,不可枚举.
      },
      canEnum: {
       value: 3,
       enumerable: true  // enumerable设置是否可以枚举.
      }
     });
    
     const copy = Object.assign({}, obj);
     console.log(copy); // { canEnum: 3 }
    
    

    2,Object.create

    语法: Object.create(proto)Object.create(proto, propertiesObject)Object.create()  方法用于创建一个新对象,使用现有的对象来作为新创建对象的原型

    const person = {
      name: 'person',
      printIntroduction: function() {
      console.log('this':this)
      console.log(`this指向新创建的对象${this.name}`);
      }
     }
     
     console.log(person.printIntroduction()) 
     //this: {name: 'person', printIntroduction: ƒ}
     //this指向新创建的对象person
     const mine = Object.create(person)
     console.log(person.printIntroduction()) 
     // this: {}
     // this指向新创建的对象person,新创建的对像上没有name属性时,回在原来的对象是取用,但是此时的this指向新创建的对象
     mine.name = 'mine'
     console.log(mine.printIntroduction() 
     // this: {name: 'mine'} ,
     //this指向新创建的对象mine
    

    异常: proto 参数为null,或除基本类型包装对象以外的对象基本类型包装对象

    除了 null 和 undefined之外,所有基本类型都有其对应的包装对象

    名字解释
    String字符串基本类型
    Number数值基本类型
    BigInt大整数基本类型
    Boolean布尔基本类型
    Symbol字面量基本类型

    使用null为原型的对象时,以 null 为原型的对象存在不可预期的行为,因为它未从 Object.prototype 继承任何对象方法。 而缺少 Object.prototype.toString()方法通常会使调试变得非常困难

    const normalObj = {};   // 普通对象
    const nullProtoObj = Object.create(null) // null作为自己的原型
    console.log(normalObj) // {}
    console.log(nullProtoObj) // {}
    console.log("" + normalObj) // [object Object]
    console.log("" + nullProtoObj) // 报错
    
    alert(normalObj); // 展示 [object Object]
    alert(nullProtoObj) // 报错
    
    normalObj.valueOf() // shows {}
    nullProtoObj.valueOf() // 报错
    
    normalObj.hasOwnProperty("p") // 展示 "false"
    nullProtoObj.hasOwnProperty("p") //报错
    
    normalObj.constructor // 出现 "Object() { [native code] }"
    nullProtoObj.constructor // 出现"undefined"
    

    可以为以 null 为原型的对象添加 toString 方法,类似于这样:

    nullProtoObj.toString = Object.prototype.toString; // 
    
    console.log(nullProtoObj.toString()); // 展示 "[object Object]"
    console.log("nullProtoObj is: " + nullProtoObj); // 展示 "nullProtoObj is: [object Object]"
    

    在实践中,以 null 为原型的对象通常用于作为 map 的替代。因为 Object.prototype 原型自有的属性的存在会导致一些错误

     const ages = { '张三': 18, '李四': 27 };
    
      function hasPerson(name) {
        return name in ages;
      }
    
      function getAge(name) {
        return ages[name];
      }
      
      hasPerson('李四') //true
      getAge('李四') // 27
      hasPerson("hasOwnProperty") // true
      getAge("toString") // ƒ toString() { [native code] }
    

    使用以 null 为原型的对象消除了这种潜在的问题,也不会给 hasPerson 和 getAge 函数引入太多复杂的逻辑:

    const ages = Object.create(null, {
    '张三': { value: 18, enumerable: true },
    '李四': { value: 27, enumerable: true },
    });
    
     function hasPerson(name) {
       return name in ages;
      }
    
     function getAge(name) {
       return ages[name];
     }
    
    hasPerson('李四') //true
    getAge('李四') // 27
    hasPerson("hasOwnProperty") // false
    getAge("toString") // undefined
    

    对象不继承 Object.prototype 原型的方法也可以防止原型污染攻击。如果恶意脚本向 Object.prototype 添加了一个属性,这个属性将能够被程序中的每一个对象所访问,而以 null 为原型的对象则不受影响

对象属性删除(删)

Object 自身没有提供方法删除其自身属性(Map 中的 Map.prototype.delete() 可以删除自身属性 )。为了删除对象上的属性,必须使用 delete 操作符

  • map原型上的delete删除
// 创建Map实例
const willDeleteMap = new Map()
// 对willDeleteMap对象设置属性和值
willDeleteMap.set('name', 'value')
// delete删除name属性,删除成功返回true
console.log(willDeleteMap.delete('name')) // true
// has查询对象里面是否还有名为name的属性,没有,结果返回false
console.log(willDeleteMap.has('name')) // false
  • delete 操作符
// 字面量创建对象
const willDeleteMap = {
  firstname: '沐',
  lastname: '阳'
}
// 删除成功返回true
delete willDeleteMap.firstname
console.log(willDeleteMap) // {lastname: '阳'}
console.log(willDeleteMap.firstname)// undefined

比较对象

语法:Object.is(value1, value2)

Object.is() ,方法判断两个值是否为同一个值。Object.is 不会强制转换两边的值,

 // Case 1
  Object.is(25, 25);                // true
  Object.is('foo', 'foo');          // true
  Object.is('foo', 'bar');          // false
  Object.is(null, null);            // true
  Object.is(undefined, undefined);  // true
  Object.is(window, window);        // true
  Object.is([], []);                // false
  
  var foo = { a: 1 };
  var bar = { a: 1 };
  Object.is(foo, foo);              // true
  Object.is(foo, bar);              // false

  // Case 2: Signed zero
  Object.is(0, -0);                 // false
  Object.is(+0, -0);                // false
  Object.is(-0, -0);                // true
  Object.is(0n, -0n);               // true

  // Case 3: NaN
  Object.is(NaN, 0/0);              // true
  Object.is(NaN, Number.NaN)        // true

冻结对象 Object.freeze()

语法:Object.freeze(obj)

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

const  freezeObj = {
  prop: function() {},
  foo: 'bar',
  childObj: {},
  childArr: []
}
freezeObj.foo = '第一次修改后的foo' //可修改
delete freezeObj.prop // 可删除
freezeObj.childArr.push(0)
freezeObj.addChild = "增加的数据"
console.log(freezeObj) // {foo: '第一次修改后的foo', childObj: {…}, childArr: Array(1), addChild: '增加的数据'}

//进行冻结
 const newObj = Object.freeze(freezeObj); // 作为参数传递的对象freezeObj与返回的对象newObj都被冻结,也不用保存返回的对象(因为两个对象全等)
 newObj === freezeObj // true, 
 newObj.foo = '第二次修改,No改动'
 newObj.childArr.push('第二次修改,可以改动,freeze只冻结第一层')
 newObj.addNewChild = "第二次增加数据,No新增"
 newObj.childObj.addName = "对对象里层的数据修改,可以新增"
 console.log(newObj)
 // {foo: '第一次修改后的foo', childObj: {…}, childArr: Array(2), addChild: '增加的数据'}
 // childArr: (2) [0, '第二次修改,可以改动,freeze只冻结第一层']
 //childObj: {addName: '对对象里层的数据修改,可以新增'}

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

结语

知识点还是要弄清楚才能融会贯通,唯有学习才能让自己不断进步。