Object.create()方法

151 阅读3分钟

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

语法:

Object.create(proto)
Object.create(proto, propertiesObject)

示例:

let obj = {
    name:'张三',
    sex:'男',
};
let newObj = Object.create(obj);
console.log(obj); // 结果:{name: '张三', sex: '男'}
console.log(newObj); 
// 结果:{}  -->  [[Prototype]]: Object {name: "张三",sex: "男"}


let obj = {
    name:'张三',
    age:18,
    log(){
        console.log(`我的名字叫${this.name},今年${this.age}岁!`);
    }
};
let newObj = Object.create(obj);
newObj.name = '李四';
newObj.age = 28;
newObj.log(); // 结果:我的名字叫李四,今年28岁!

参数

proto 新创建对象的原型对象。

propertiesObject (可选)

如果该参数被指定且不为 undefined,则该传入对象的自有可枚举属性(即其自身定义的属性,而不是其原型链上的枚举属性)将为新创建的对象添加指定的属性值和对应的属性描述符。这些属性对应于 Object.defineProperties() 的第二个参数。

返回值

一个新对象,带着指定的原型对象及其属性。

异常

proto 参数需为

  • null
  • 除基本类型包装对象以外的对象

如果 proto 不是这几类值,则抛出一个 TypeError 异常。

使用 null 原型的对象

null 为原型的对象存在不可预期的行为,因为它未从 Object.prototype 继承任何对象方法。特别是在调试时,因为常见的对象属性的转换/检测工具可能会产生错误或丢失信息(特别是在静默模式,会忽略错误的情况下)。

let nullObject = Object.create(null);
console.log(nullObject);
// 结果:{} 展开看 --> 无属性
// 小结:使用null创建的新对象没有原型链以及对象原型方法

在实践中,以 null 为原型的对象通常用于作为 map 的替代。因为 Object.prototype 原型自有的属性的存在会导致一些错误:

const ages = { alice: 18, bob: 27 };

function hasPerson(name) {
  return name in ages;
}

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

hasPerson("hasOwnProperty") // true
getAge("toString") // [Function: toString]

使用以 null 为原型的对象消除了这种潜在的问题,且不会给 hasPerson 和 getAge 函数引入太多复杂的逻辑:

const ages = Object.create(null, {
  alice: { value: 18, enumerable: true },
  bob: { value: 27, enumerable: true },
});

hasPerson("hasOwnProperty") // false
getAge("toString") // undefined

在这种情况下,应谨慎添加任何方法,因为它们可能会与存储的键值对混淆。

令你使用的对象不继承 Object.prototype 原型的方法也可以防止原型污染攻击。如果恶意脚本向 Object.prototype 添加了一个属性,这个属性将能够被程序中的每一个对象所访问,而以 null 为原型的对象则不受影响。

const user = {};

// A malicious script:
Object.prototype.authenticated = true;

// Unexpectedly allowing unauthenticated user to pass through
if (user.authenticated) {
  // access confidential data...
}

用 Object.create() 实现类式继承

下面的例子演示了如何使用 Object.create() 来实现类式继承。这是一个所有版本 JavaScript 都支持的单继承。

// Shape - 父类(superclass)
function Shape() {
  this.x = 0;
  this.y = 0;
}
// 父类的方法
Shape.prototype.move = function(x, y) {
  this.x += x;
  this.y += y;
  console.info('Shape moved.');
};
// Rectangle - 子类(subclass)
function Rectangle() {
  Shape.call(this); // call super constructor.
}
// 子类续承父类
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;

var rect = new Rectangle();

console.log('Is rect an instance of Rectangle?',
  rect instanceof Rectangle); // true
console.log('Is rect an instance of Shape?',
  rect instanceof Shape); // true
rect.move(1, 1); // Outputs, 'Shape moved.'

如果你希望能继承到多个对象,则可以使用混入的方式

function MyClass() {
     SuperClass.call(this);
     OtherSuperClass.call(this);
}
// 继承一个类
MyClass.prototype = Object.create(SuperClass.prototype);
// 混合其它
Object.assign(MyClass.prototype, OtherSuperClass.prototype);
// 重新指定constructor
MyClass.prototype.constructor = MyClass;
MyClass.prototype.myMethod = function() {
     // do something
};

使用 Object.create 的 propertyObject参数

let o;
// 创建一个原型为null的空对象
o = Object.create(null);
o = {};
// 以字面量方式创建的空对象就相当于:
o = Object.create(Object.prototype);
o = Object.create(Object.prototype, {
  // foo会成为所创建对象的数据属性
  foo: { 
    writable:true,
    configurable:true,
    value: "hello" 
  },
  // bar会成为所创建对象的访问器属性
  bar: {
    configurable: false,
    get: function() { return 10 },
    set: function(value) {
      console.log("Setting `o.bar` to", value);
    }
  }
});

function Constructor(){}
o = new Constructor();
// 上面的一句就相当于:
o = Object.create(Constructor.prototype);
// 当然,如果在Constructor函数中有一些初始化代码,Object.create不能执行那些代码

// 创建一个以另一个空对象为原型,且拥有一个属性p的对象
o = Object.create({}, { p: { value: 42 } })

// 省略了的属性特性默认为false,所以属性p是不可写,不可枚举,不可配置的:
o.p = 24
o.p
//42

o.q = 12
for (var prop in o) {
   console.log(prop)
}
//"q"

delete o.p          //false
//创建一个可写的,可枚举的,可配置的属性p
o2 = Object.create({}, {
  p: {
    value: 42, 
    writable: true,
    enumerable: true,
    configurable: true 
  } 
});