Skip to content
大纲

实现一些 Object 方法

  • Object.create
  • Object.assign
  • Object.freeze

手写 Object.create

Object.create() 方法创建一个新对象,使用现有的对象来提供新创建的对象的 __proto__

js
// 简略版
// 思路:将传入的对象作为原型
function myCreate(obj) {
  function C() {}
  C.prototype = obj
  return new C()
}
// 新声明一个函数,将函数的原型指向传入的对象,并返回这个函数的实例
js
// MDN 官方版 Polyfill
// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/create#polyfill
// 请注意,尽管在 ES5 中 Object.create 支持设置为 [[Prototype]] 为 null,但因为那些 ECMAScript5 以前版本限制,此 polyfill 无法支持该特性。
if (typeof Object.create !== 'function') {
  Object.create = function (proto, propertiesObject) {
    if (typeof proto !== 'object' && typeof proto !== 'function') {
      throw new TypeError(
        'Object prototype may only be an Object: ' + proto
      )
    } else if (proto === null) {
      throw new Error(
        "This browser's implementation of Object.create is a shim and doesn't support 'null' as the first argument."
      )
    }

    if (typeof propertiesObject !== 'undefined')
      throw new Error(
        "This browser's implementation of Object.create is a shim and doesn't support a second argument."
      )

    function F() {}
    F.prototype = proto

    return new F()
  }
}

手写 Object.assign

js
// MDN 文档:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
// Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象分配到目标对象。它将返回目标对象。

// Object.prototype.hasOwnProperty.call(obj, key)
// Object.hasOwn() 目前兼容性不够好
Object.myAssign = function (target, ...source) {
  if (target == null) {
    throw new TypeError('Cannot convert undefined or null to object')
  }

  let ret = Object(target)

  source.forEach(function (obj) {
    if (obj != null) {
      for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
          ret[key] = obj[key]
        }
      }
    }
  })

  return ret
}

// MDN 官方 Polyfill
// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#polyfill_2
// 这个 polyfill 不支持 symbol 属性,由于 ES5 中本来就不存在 symbols
// ES6 的 Object.assign 是支持处理 symbol 属性的
//     考虑更周全的版本可以参见 https://github.com/es-shims/object.assign/blob/main/implementation.js
//     通过 has-symbols 模块 && Object.getOwnPropertySymbols 判断是否支持 symbol 然后做处理
if (typeof Object.assign !== 'function') {
  // Must be writable: true, enumerable: false, configurable: true
  Object.defineProperty(Object, 'assign', {
    value: function assign(target, varArgs) {
      // .length of function is 2
      'use strict'
      if (target === null || target === undefined) {
        throw new TypeError('Cannot convert undefined or null to object')
      }

      var to = Object(target)

      for (var index = 1; index < arguments.length; index++) {
        var nextSource = arguments[index]

        if (nextSource !== null && nextSource !== undefined) {
          for (var nextKey in nextSource) {
            // Avoid bugs when hasOwnProperty is shadowed
            if (
              Object.prototype.hasOwnProperty.call(nextSource, nextKey)
            ) {
              to[nextKey] = nextSource[nextKey]
            }
          }
        }
      }
      return to
    },
    writable: true,
    configurable: true
  })
}

实现 Object.freeze 方法

freeze 方法实现原理的简单模拟要使用的到方法包括 Object.definedProperty()Object.seal()

  • Object.definedProperty() 方法可以定义对象的属性的特性。如可不可以删除、可不可以修改、访问这个属性时做特殊处理
  • Object.seal() 方法可以让对象不能被扩展、删除属性等等
js
Object.defineProperty(person, 'name', {
  configurable: false, // 表示能否通过 delete 删除属性,能否修改属性的特性...
  enumerable: false, // 表示是否可以枚举。直接在对象上定义的属性,基本默认 true
  writable: false, // 表示能否修改属性的值。直接在对象上定义的属性,基本默认 true
  value: 'xm' // 表示属性的值。访问属性时从这里读取,修改属性时,也保存在这里。
})

function myFreeze(obj) {
  if (obj instanceof Object) {
    Object.seal(obj) // 封闭对象
    let p
    for (p in obj) {
      // 剔除原型属性,设置属性的 writable 特性为 false
      if (obj.hasOwnProperty(p)) {
        Object.defineProperty(obj, p, {
          writable: false
        })
        myFreeze(obj[p]) // 递归,实现更深层次的冻结
      }
    }
  }
}