手撕JS方法之对象

320 阅读2分钟

这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战

很久没有写文章了,刚好借更文挑战督促自己学习。最近一直忙着秋招,感觉自己对于技术的沉淀还是少了点,给自己定个小目标吧。

最近在学JS的时候的遇到了很多手写数组、对象、字符串、函数的方法,一直没有总结。想着写一篇文章总结一下,刚好可以对知识进行一个巩固。

1、Object.assign

  • 定义

Oject.assign(target,...sources)用于将可枚举属性的值从一个或者多个源对象分配到目标对象,它将返回目标对象。

简单的说,就是可以将值进行一个复制,在对象的浅拷贝中常用。

比如,将对象A拷贝到对象B

    const objectA = {
        name: 'ObjectA',
        age: 18
    };
    const objectB = {
        name: 'ObjectB',
        age: 20
    };

    const returnedTarget = Object.assign(objectA, objectB);

    console.log(objectA); // {name: 'ObjectB', age: 20}
    console.log(objectB); // {name: 'ObjectB', age: 20}
    console.log(returnedTarget); // {name: 'ObjectB', age: 20}

从上面的代码中可以看出来,对象A拷贝给对象B的时候,对象A自身也发生了变化。这也就是经典的浅拷贝。

  • 实现方法

由上面的用法中可以看出,主要是将源对象拷贝到目标对象上,而对象的复制不是复制基本数据类型,将值拷贝即可。

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

Object.defineProperty(Object, 'assign', {
    value: function(target, ...args) {
        if (target == null) {
            return new TypeError('Cannot convert undefined or null to object');
        }
        // 统一引用数据类型 
        const to = Object(target);

        for (let i = 0; i < args.length; i++) {
            // 每一个源对象
            const nextSource = args[i];
            if (nextSource !== null) {
                // 使用for...in和hasOwnProperty双重判断,确保只拿到本身的属性、方法
                for (const nextKey in nextSource) {
                    if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
                        to[nextKey] = nextSource[nextKey];
                    }
                }
            }
        }
        return to;
    },
    // 不可枚举
    enumerable: false,
    // 属性值不可被修改
    writable: true,
    // 属性的描述符不可被改变
    configurable: true,
})

1、首先明确传入对象:目标对象、源对象

2、判断目标对象是否为空

3、统一目标对象为引用数据类型

4、遍历源对象,使用for...in和hasOwnProperty双重判断,拿到本身的属性和方法,最后返回目标队形

2、Object.is

  • 定义

Oject.is(target,...sources)用于判断两个值是否为同一个值。

虽然是对象方法,但是可以判断的值不仅仅是对象。

可以判断下面两个值:

  • 传入的值都是undefined

  • 传入的值都是null

  • 传入的值都是true或者false

  • 传入的值是相同的字符串

  • 传入的值是相同对象(也就是引用地址相同)

  • 传入的值都是数字且都是+0 或者 -0 或者NaN或者其他数字

我们可以测试一下:

    let objectA = {
        name: '1',
        age: 12
    };
    let objectB = {
        name: '1',
        age: 12
    }
    let objectC = objectA
    // 1
    console.log(Object.is(undefined, undefined)); // true
    console.log(Object.is(null, null)); // true
    console.log(Object.is(true, true)); // true
    console.log(Object.is(true, false)); // false
    console.log(Object.is('str', 'str')); // true
    // 2
    console.log(Object.is(objectA, objectB)); // false
    console.log(Object.is(objectA, objectC)); // true
    // 3
    console.log(Object.is(0, 0)); // true
    console.log(NaN === NaN); // false
    console.log(Object.is(NaN, NaN)); // true
    console.log(+0 === -0); // true
    console.log(Object.is(+0, -0)); // false

第一部分没什么好说的,就是寻常的判断是否相等;第二部分需要注意的是,如果两个对象的内容相等,但是引用地址不同,Object.is返回的是false. Object.is主要是为了解决第三部分的问题的。

在普通的相等符号判断中,NaN === NaN 是返回false,这就与我们想要的效果不一样了,还有 +0-0 ,在我们的意识当中,+0-0 应该是不相等的才对。

所以实现这个方法主要就是解决这个问题。

实现的代码贴一下:

const is = (x, y) => {
    if (x === y) {
        return x !== 0 || y !== 0 || 1 / x === 1 / y
    } else {
        return x !== x && y !== y
    }
}