探讨 Object.create()、new Object() 和 {} 创建对象的区别

1,145 阅读4分钟

🌻 前言

你有对象吗?
没有? new 一个啊~

image.png

面向对象编程是一种常见的程序设计思想,有道是,万物皆对象。身为一名前端打工仔,对象在代码中随处可见。但是你注意到没有,创建对象有很多种方式,其中最常见的自然是直接字面量创建,如let obj = {}

除了使用字面量,我们还可以用 new 操作符或者是 Object.create() 方法创建对象,如下:

  • 字面量创建 :var obj1 = {};
  • new操作符创建:new Object();
  • Object.create()创建:Object.create();

那么,他们之间有什么区别呢?OK,这就是本文的探讨的问题了~

🌈 描述

📄 字面量创建

字面量又叫对象初始化器。没有属性的空对象常用字面量直接创建,突出的就是一手简单方便:

var abin = {
  name: '前端阿彬'
};

//等价于
var abin = {}
abin.name = '前端阿彬

✨ new操作符创建

new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。

//调用 Object.constructor 构造函数创建对象
var abin = new Object();
abin.name = '前端阿彬'

//使用构造函数 配合 new 创建出的对象 constructor 会指向这个构造函数
function Person(name){
  this.name = name
}
var abin = new Person('前端阿彬')

在调用 new 的过程中会发生四件事情:

  • 创建一个空对象(即{});
  • 为新对象添加属性 __proto__ ,将该属性链接至构造函数的原型对象 ;
  • 执行构造函数方法,属性和方法被添加到this引用的对象中;
  • 如果构造函数中没有返回新对象,那么返回this,即创建这个新对象,否则,返回构造函数中返回的对象

🦚 Object.create()

Object.create() 方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__ 原型对象,这样就不用定义构造函数了。

Object.create() 方法有两个参数:

  • proto:新创建对象的原型对象(即新对象原型指向这个对象参数)
  • propertiesObject(可选):新对象指定的属性,是一个对象形式的配置,配置如下:\

value: 属性的默认值, 默认为undefined;
congigurable: 能否使用delete、能否修改属性特性、或能否修改访问器属性、false为不可重新定义,默认值为true;
enumberable: 可枚举性,对象属性能否通过for-in 循环,默认为true;
writable: 对象属性是否可修改,默认为true,可修改,设置false可理解为常量不可修改。

举个栗子🌰:

let newObj = Object.create(
 {name:'sun'},
 {
   age: {
     value: 24, // 属性默认值
     congigurable: false, // 设置false之后将不能不删除
     enumerable: true,
     writable: false,  // 设置false之后将不能修改
   }
 }
)

⚡️ 对比

  1. new 操作符 vs 字面量
  • 从创建对象的过程来讲,这两者底层实现基本是没有区别的。但是字面量创建对象时不会调用Object构造函数,简洁且性能更好;

  • 从创建出来的对象实例来讲,这两者也是一样的,它们创建的对象的原型链的尽头都是 Object.prototype

  • 字面量创建其实也是执行了 new Object(),它相当于语法糖,可以直接定义对象内的属性,而 new 操作符则不行。

  • 如果你需要批量创建多个对象,就得用到 new 操作符号了,可以使用工厂模式

function createObject(name){
    this.name = name
}
var abin = new createObject('前端阿彬');
var xiaohong = new createObject('小红');
  1. 创建空对象

如果你需要创建一个干净的空对象,推荐使用 Object.create(null) 。因为我们传入null当做它的参数,所以它创建的对象不会有原型,也不会有 Object 原型对象的任何属性(例如toString,hasOwnProperty等)。

image.png

换句话说,就是 Object.create(null) 可以创建一个干净且高度可定制的对象当做数据字典,这么做有两个好处:

  • 在调用对象时可以避免遍历原型链上的所有属性和方法,提高效率;
  • 避免 Object.prototype 原型自有的属性的存在导致的一些潜在错误,例如:
const ages = { abin: 18, xiaohong: 20 };

function getAge(name) {
  return ages[name];
}

// [Function: toString],理想状态应该返回 undefined
getAge("toString") 

另外,如果你需要用到 Object.prototype 原型 的一些方法,可以自定义添加,例如添加 toString 方法:

var nullObj = Object.create(null)
nullObj.toString = Object.prototype.toString; 
  1. 创建对象的区别

Object.create()创建的对象只是原型指向源对象,并不会继承它的任何属性;而new出来的对象是会继承原型对象的属性和方法。如下:

    let obj = {
      name: '阿彬',
      age: 10,
      foo: function() {
        console.log(this.age)
      }
    }
    let a = Object.create(obj)
    let b = new Object(obj)

    a.name = '彬'
    b.name = '彬'

    console.log(a.name,a,a.foo()); 
    //彬 
    //{name: '彬'} 
    //10
            
    console.log(b.name,b,b.foo());
    //彬 
    //{name: '彬', age: 10, foo: ƒ} 
    //10

可以看到,分别打印 a 和 b ,通过Object.create()创建的a是没有原型对象的age属性和foo方法的。而之所以它也可以执行foo方法,是因为向原型链追溯,找到了原型对象的这个方法执行了。

本文正在参加「金石计划」