object.create才是原型模式的精髓

174 阅读5分钟

Object.create属于那种见的很少用的更少的API,平时也就在阅读源码时会发现有在使用,但是JS设计这个API的初衷真的是这样吗?让人诟病的IE浏览器都在IE9中兼容性了Object.create的使用,可能你对IE9没什么概念,又比如谷歌浏览器在2010-2-25已经兼容了Object.create,已经过去10多年了~~~

如果Object.create真的是落伍的、糟糕的API,为什么ECMA规范没有去删除和更新他喃?其实Object.create创建对象的方式才是正宗的JS原型模式,无论是ES6的class语法糖,还是构造函数,其实都是为了让其他语言的开发者能快速入门JS而设计,也就是在JS原型模式上面包裹一层,而object.create才能体现原型模式的精髓

Object.create的功能

来自MDN的功能介绍:

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

  • 创建新对象
  • 现有的对象作为新对象的原型对象

看过JS不能没有原型模式的XDM,应该知道JS的继承使用的是原型继承,原型继承的本质就是:克隆对象得到新对象,把对象作为新对象的原型

对比Object.create实现的功能,是不是发现非常满足原型继承喃?这也就是为什么会在开头说(object.create才能体现原型模式的精髓)的原因了~~

可能你会疑问?通过字面量、构造函数创建对象就不好,只通过object.create创建对象可以吗?

显然不是这样的,更多时候都不会选择object.create创建对象,而是选择字面量和构造函数创建对象,之所以说object.create是精髓,是因为通过字面量和构造函数创建对象,底层实现都是类似于object.create

object.create的使用

object.create(o: object | null, properties: object ): object

通过功能介绍就可以推测出,会传入一个对象作为新建对象的原型,返回值是新建的对象

  • proto(必填):成为新对象的原型对象;该参数值可以是null、对象
  • properties(可选):该参数对象的属性(添加的属性是其自身可枚举的属性,而不是对象原型链上的属性)会添加到新对象上;(遵循Object.defineProperties第二个参数的配置)

先看看最常见的用法

let obj = Object.create({
    sex: "男"
})

image-20230215181736491

是不是和你想的一样喃,传入的对象成为新对象的原型对象

let properties = {
    name: {
        value: "李白",
        enumerable: true,
        writable: true,
        configurable: true
    }
}
let obj = Object.create({
    sex: "男"
}, properties)

image-20230215182015957

properties就是属性名称和对应的属性描述符的集合对象

这里要介绍一个比较特殊的的写法

let obj = Object.create(null)

可以先猜一猜obj对象长什么样~~~

image-20230215182445353

这好像不是空对象,但是又是空对象,对照一下平时看到的空对象let obj = {}

image-20230215182606332

对比发现,Object.create(null)创建的空对象不仅没有属性和方法,还没有Object.prototype原型对象

let obj = Object.create(null)
obj.toString()                  //Uncaught TypeError: obj.toString is not a function

obj对象连访问toString 方法的资格都没有,那创建他的意义在哪里喃?

意义就是他不会使用Object.prototype对象上的所有方法和属性,如果Object.prototype被修改,会影响到所有原型链上有Object.prototype的对象,也就是几乎所有对象,但是唯独不会影响到obj对象,因为obj对象原型链是空的,所以不会受到影响,因为没有所以纯净~~~

如果你想你创建的空对象,不被原型链影响,那Object.create(null)就是你的不二选择~~~

Object.create、字面量、构造创建函数的区别

Object.create原理

首先实现一个Object.create的简单版本,这是网上广为流传的版本

const mYcreate = function (proto, prototyObject = undefined) {
  function F() {}
  F.prototype = proto           // 设置原型
  const obj = new F()           // 创建新对象
  
  if (propertyObject !== undefined) {
    Object.defineProperties(obj, propertyObject)    //  设置新对象属性
  }
  return obj
}

前面介绍过,Object.create是原型模式的精髓,是因为他采用了克隆的思想

function F() {}
F.prototype = proto         // 设置原型
const obj = new F()         // 创建新对象

很简单的几句代码,其实个人感觉通过构造函数实现克隆很不严谨

// 浏览器除了`Object.create`能创建纯净的空对象,没有其他方式创建了,这里意想一想就可以,嘿嘿
let obj = { }           // 纯净洁的空对象(意想)
obj.__proto__ = proto   // 设置原型

这才符合原型模式创建一个新对象,是通过克隆已有对象产生新对象,而不是什么实例构造函数

字面量创建对象原理

let obj = { }

let obj = { }                      //  纯净的空对象(意想)
obj.__proto__ = Object.prototype    // 设置原型

是不是感觉和使用Object.create(Object.prototype)效果一样的喃?

构造函数创建对象原理

又是网上的现成代码,偷懒了~~~

function myNew(Constructor) {
    // 1.从 Object.prototype 上克隆一个空的对象
    var person = new Object();
    // 2.取出构造函数
    var constructor = [].shift.call(arguments);
    // 3.继承构造函数的原型
    person._proto_ = constructor.prototype;
    // 4.为新建的对象调用构造函数,生成内部属性
    let ret = constructor.apply(person, arguments);
    // 5.返回对象
    return typeof ret === 'object' ? ret : person;
}

直接取其精髓

function myNew(Constructor) {
    var person = { };   // 纯净的空对象(意想)
    var constructor = [].shift.call(arguments);
    person._proto_ = constructor.prototype;     // 设置原型
    let ret = constructor.apply(person, arguments);
    return typeof ret === 'object' ? ret : person;  // 返回对象
}

是不是创建对象的过程Object.create是一样的

这里我又要强势宣布**object.create 是原型模式的精髓**

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 15 天,点击查看活动详情