# Object方法

# 对象的数据属性

var person = {}
Object.defineProperty(person,'name',{
    configurable:false,// 能否使用delete、能否需改属性特性、或能否修改访问器属性、,false为不可重新定义,默认值为true
    enumerable:false,// 不可枚举 对象属性是否可通过for-in循环,flase为不可循环,默认值为true
    writable:false,// 对象属性是否可修改,flase为不可修改,默认值为true
    value:'xiaoming' // 对象属性的默认值,默认值为undefined
});

// value
console.log(person);// xiaoming,默认value

// writable
person.name="666";
console.log(person);// xiaoming,不可修改value

// enumerable
for(var i in person){
    console.log(person[i]) // 无结果,不可循环
}
Object.keys(person) // []  与for in区别在于不能遍历出原型链上的属性
Object.getOwnPropertyNames(person) // ['name']

// configurable
delete person.name
console.log(person.name)// xiaoming,不可删除

Object.defineProperty(person,'name',{
    configurable:true // 不可修改,将抛出错误
});

// Object.getOwnPropertyDescriptors(person) // 返回自身所有的属性
Object.getOwnPropertyDescriptor(person,'name') // 返回自身属性
{
  configurable: false
  enumerable: false
  value: "xiaoming"
  writable: false
}

# Object.create()

描述:该方法创建一个新对象,将对象继承到__proto__属性上 格式:Object.create(proto[, propertiesObject]) 用法:如果用传统的方法要给一个对象的原型上添加属性和方法,是通过 propt 实现的

通过构造函数

//创建一个构造函数或者类
var People = function(){}
People.prototype.y = 20
People.prototype.z = 40
People.prototype.showNum = function() {}
//通过构造函数创建实例
var p = new People();
console.log(p.__proto__ === People.prototype) // true

//使用Object.create()
var proto = {
    y: 20,
    z: 40,
    showNum(){}
};
var o = Object.create(proto);

# Object.defineProperty()

Object.defineProperty(obj, prop, descriptor)

obj: 需要被操作的目标对象 prop: 目标对象需要定义或修改的属性的名称 descriptor: 将被定义或修改的属性的描述符

var obj = new Object();

Object.defineProperty(obj, 'name', {
    configurable: false,
    writable: true,
    enumerable: true,
    value: '张三'
})

console.log(obj.name)  //张三

# Object.getPrototypeOf()

描述:用于读取一个对象的原型对象;

格式:Object.getPrototypeOf(obj);

用法:

Object.getPrototypeOf('foo') === String.prototype === 'foo'.__proto__ // true
Object.getPrototypeOf(true) === Boolean.prototype === true.__proto__ // true

# Object.setPrototypeOf()

描述: Object.setPrototypeOf方法的作用与_proto_相同,用来设置一个对象的prototype对象,返回参数对象本身

格式:Object.setPrototypeOf(object, prototype)

该方法等同于下面的函数:

function (obj, proto) {
  obj.__proto__ = proto;
  return obj;
}

示例

let proto = {};
let obj = { x: 10 };
Object.setPrototypeOf(obj, proto);
 
proto.y = 20;
proto.z = 40;
 
obj.x // 10
obj.y // 20
obj.z // 40

# keys & values & entries

  • Object.keys 键
  • Object.values 值
  • Object.entries 组件二级数组
const obj =  {
    address: "434343422",
    admin: "3434",
    adminCharater: '4444',
    adminRecord: '33',
    article: '22',
    artist: '11'
  }
  Object.entries(obj)// [["address", "434343422"]...]
  Object.keys(obj) //["address", "admin", "adminCharater", "adminRecord", "article", "artist"]
  Object.values(obj) //["434343422", "3434", "4444", "33", "22", "11"]

# Object 的 set 和 get 用法

在初始化对象的时候这样使用

var obj={
    a: 1,
    b: 2,
    set c(x){console.log('c被赋值:',x);c=x;},
    get c(){console.log('c被取出: ',c);return c}  
};

obj.c=3  //c被赋值: 3
obj.c  //c被取出:  3

对象初始化之后可以这样添加属性

var obj={
    a: 1,
    b: 2    
};

obj.__defineGetter__('c', function(){return c});
obj.__defineSetter__('c', function(x){c = x});

或者使用

Objec(obj, c, {
  set:function(x){
    console.log('c被赋值:',x);
    c=x
  },
  get:function(){
    console.log('c被取出:',c)
    return c
  }
})
obj.c=3  //c被赋值: 3
obj.c  //c被取出:  3

# Object.fromEntries()

ES10新增
接收一个键值对的列表参数,并返回一个带有这些键值对的新对象

const entries = new Map([
  ['foo', 'bar'],
  ['baz', 42]
]);

const obj = Object.fromEntries(entries);

console.log(obj);
// expected output: Object { foo: "bar", baz: 42 }

# 浅拷贝/深拷贝

//浅克隆 [层级嵌套不能超过2级,包括2级]
const clone = source => Object.assign({}, source)
const clone = source => { ...source }

//深克隆
function deepclone(obj){
   if (typeof obj === 'object' && obj !== null) {
    let o = obj.push?[]:{};
    console.log(obj.push);
    
    for(let attr in obj){
        if(obj.hasOwnProperty(attr)){
            if(typeof obj[attr] === 'object'){
                o[attr] = deepclone(obj[attr]);
            }else{
                o[attr] = obj[attr];
            }
        }
    }
    return o;
     } else {
    return obj;
  }
}

// or 取巧方法 JSON.parse(JSON.stringify());
// 注意这种取巧方法是有限制的
// 1. 只能解析Number、String、Array等能够被json表示的数据结构(无法拷贝一写特殊的对象,诸如 RegExp, Date, Set, Map,函数 等。)
// 2. 不能处理循环引用


const obj = {val:2};
obj.target = obj;

//拷贝obj会出现系统栈溢出,因为出现了无限递归的情况。

JSON.parse(JSON.stringify(obj));

//VM913:1 Uncaught TypeError: Converting circular structure to JSON
//--> starting at object with constructor 'Object'
//--- property 'target' closes the circle
//at JSON.stringify (<anonymous>)
//at <anonymous>:1:17
//(anonymous) @ VM913:1
 

//解决死循环的问题
const isObject = (target) => (typeof target === 'object' || typeof target === 'function') && target !== null;

const deepClone = (target, map = new Map()) => { 
    
  //如果有的话直接跳过
  if(map.get(target))  return target; 
 
  if (isObject(target)) { 
    map.set(target, true); 
    const cloneTarget = Array.isArray(target) ? []: {}; 
    for (let prop in target) { 
      if (target.hasOwnProperty(prop)) { 
          cloneTarget[prop] = deepClone(target[prop],map); 
      } 
    } 
    return cloneTarget; 
  } else { 
    return target; 
  } 
}
//map 和 a一直是强引用的关系, 在程序结束之前,a 所占的内存空间一直不会被释放。
//弱引用的对象可以在任何时候被回收,而对于强引用来说,只要这个强引用还在,那么对象无法被回收。

//ES6给我们提供了这样的数据结构,它的名字叫WeakMap,它是一种特殊的Map, 其中的键是弱引用的。`其键必须是对象`,而值可以是任意的


//稍微改造一下即可:
const deepClone = (target, map = new WeakMap()) => {
  //...
}

# 对象转原始类型是根据什么流程运行的?

对象转原始类型,会优先调用内置的[ToPrimitive]函数,对于该函数而言,其逻辑如下:

如果Symbol.toPrimitive()方法,优先调用再返回 调用valueOf(),如果转换为原始类型,则返回 调用toString(),如果转换为原始类型,则返回 如果都没有返回原始类型,会报错

 const obj = {
  value: 3,
  valueOf() {
    return 4;
  },
  toString() {
    return '5'
  },
  [Symbol.toPrimitive]() {
    return 6
  }
}
console.log(obj + 1); // 输出7