1. 如何定义一个对象

ES6之前

var a = new Object()
var a = {}

这两种定义对象是有原型链存在,并可以使用原型链上方法如toString()等

ES6新增另外一种

var b=Object.create()

b.toString()  // 报错:b.toString is not a function

这种方法定义对象,是不存在原型的,里面就真的啥方法都没有,空空如也。

2. 对象如何获取变量key

ES6之前

var name='a'
var object={}
object[name]=1
console.log(object)  // {a:1}

ES6修改成

var name='a'
var object={[name]:1}  // 直接一步到位
console.log(object)  // {a:1}

3. 对象属性定义

ES6之前

var o = {
  a: a,
  b: b,
  c: c
};

var o={
  sayHi:function(){}
}

ES6修改成

var o = {a, b, c}; // key和valuet同名可以进行缩写

var o={
  sayHi(){}  // 省略了function
}

4. ES6对象新增了getter和setter方法

var o={
  _age:18,   // 加下划线防止名字相同
  get age(){return o._age},  // 获取
  set age(value){if(value<100){o._age=value}else{o._age=100}}   // 设置
}

调用o.age就相当调用了getter方法,调用o.age=1000就相当调用了setter方法。ES6使得对象属性可以自行定义获取和设置。

5. 对象的浅拷贝(两个地址不一样的对象)

ES6之前

var obj1={a:1,b:2,c:3}
var obj2={}
for(let key in obj1){
   obj2[key]=obj1[key]
}
console.log(obj2)   // {a: 1, b: 2, c: 3}

不包含函数的对象

对于不包含函数的简单对象,可以用JSON.stringify()JSON.parse()方法来进行对象的深拷贝(对象中含有其他简单对象也可以)。

注意:这种不是浅拷贝,修改obj2会导致obj1也会变

var obj1={a:1,b:2,c:3}
obj2= obj1   // 这不是浅拷贝,而是引用同一个地址的对象

ES6新增

(1) Object.assign()方法

var obj1={a:1,b:2,c:3}
var obj2=Object.assign({},obj1)  // 将后面对象拷贝到前面的空对象
console.log(obj2)   // {a: 1, b: 2, c: 3}

(2) 用剩余参数方法

var obj1={a:1,b:2,c:3}
var obj2={...obj1}
console.log(obj2)   // {a: 1, b: 2, c: 3}

6. 对象的原型

var  a={}

a.__proto__===Object.prototype   // 打印出true,说明Object.prototype 是a的原型

如何修改对象原型

var b={
   sayHi(){
   console.log('hi')
  }
}

a=Object.create(b)  // 修改原型方法,不推荐用__proto__

a.__proto__===b  // 打印出true,说明 b 是a的原型

如何找出对象原型

a.__proto__    // 不推荐的方法
Object.getPrototypeOf(a)   // ES6推荐方法

6. 属性修饰符

Object.defineProperty()方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。

对象的深拷贝

简易深拷贝

我先写了个简单的对象深拷贝函数,不考虑循环引用问题。

const _sampleDeepClone = target => {
    // 补全代码
    let ans = {};
    for(let key in target){
        if(target[key] instanceof Array){
            ans[key] = [...target[key]];
        }else if(typeof target[key] === 'object' && target[key] != null){
            ans[key] = _sampleDeepClone(target[key]);
        }else{
            ans[key] = target[key];
        }
    }
    return ans;
}

包含循环引用的对象的深拷贝

export const deepClone = (obj, hash = new WeakMap()) => {
    // 直接返回基础数据类型及函数类型
    if (target === null || typeof target !== 'object'){
        return target;
    }
    // 日期对象直接返回一个新的日期对象
    if (obj instanceof Date){
        return new Date(obj);
    } 
    //正则对象直接返回一个新的正则对象     
    if (obj instanceof RegExp){
        return new RegExp(obj);     
    }
    //如果循环引用,就用 weakMap 来解决
    if (hash.has(obj)){
        return hash.get(obj);
    }
    // 获取对象所有自身属性的描述
    let allDesc = Object.getOwnPropertyDescriptors(obj);
    // 遍历传入参数所有键的特性
    let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc);

    hash.set(obj, cloneObj)
    for (let key of Reflect.ownKeys(obj)) { 
        if(typeof obj[key] === 'object' && obj[key] !== null){
            cloneObj[key] = deepClone(obj[key], hash);
        } else {
            cloneObj[key] = obj[key];
        }
    }
    return cloneObj
}

知识点补充

包含循环引用的对象

深拷贝一个包含循环引用的 JavaScript 对象需要特殊处理。循环引用是指对象属性之间存在相互引用的情况,导致深拷贝时陷入无限循环的问题。如: obj1.self = obj1; // 添加循环引用

WeakMap

WeakMap是 JavaScript 的内置对象,它是一种弱引用的映射结构。WeakMap可以用来存储键值对,其中键是对象,值可以是任意类型的值。与Map不同,WeakMap中的键是弱引用的,这意味着当键对象没有其他引用时,它们可能会被垃圾回收机制回收,并从WeakMap中自动移除。


A Student on the way to full stack of Web3.