实现Object.create

手写实现Object.create方法,创建一个新对象并将其原型指向指定对象

问题

Object.create() 是 ES5 中引入的一个方法,用于创建一个新对象,并将这个新对象的原型(__proto__)指向指定的对象。这道题要求我们手动实现这个方法,理解 JavaScript 中原型链的工作原理。

解答

/**
 * 手写实现 Object.create
 * @param {Object} proto - 新对象的原型对象
 * @param {Object} propertiesObject - 可选参数,为新对象定义额外属性
 * @returns {Object} 返回新创建的对象
 */
function myCreate(proto, propertiesObject) {
  // 参数校验:proto 必须是对象或 null
  if (typeof proto !== 'object' && typeof proto !== 'function') {
    throw new TypeError('Object prototype may only be an Object or null');
  }
  
  // 创建一个空的构造函数
  function F() {}
  
  // 将构造函数的原型指向传入的对象
  F.prototype = proto;
  
  // 创建新对象,其原型为 proto
  const obj = new F();
  
  // 处理第二个参数:为对象定义属性
  if (propertiesObject !== undefined && propertiesObject !== null) {
    Object.defineProperties(obj, propertiesObject);
  }
  
  return obj;
}

简化版本(只实现功能):

function myCreate(proto) {
  // 参数校验
  if (typeof proto !== 'object' && typeof proto !== 'function') {
    throw new TypeError('Object prototype may only be an Object or null');
  }
  
  // 创建空构造函数
  function F() {}
  
  // 设置原型
  F.prototype = proto;
  
  // 返回新实例
  return new F();
}

使用示例

// 示例1:基本使用
const person = {
  name: 'Unknown',
  sayHello() {
    console.log(`Hello, I'm ${this.name}`);
  }
};

const student = myCreate(person);
student.name = 'Tom';
student.sayHello(); // 输出: Hello, I'm Tom

console.log(student.__proto__ === person); // true

// 示例2:使用第二个参数定义属性
const parent = {
  greet() {
    console.log('Hello from parent');
  }
};

const child = myCreate(parent, {
  age: {
    value: 18,
    writable: true,
    enumerable: true,
    configurable: true
  },
  name: {
    value: 'Alice',
    writable: false,
    enumerable: true
  }
});

console.log(child.age); // 18
console.log(child.name); // Alice
child.greet(); // Hello from parent

// 示例3:创建原型为 null 的对象
const pureObject = myCreate(null);
console.log(pureObject.__proto__); // undefined
console.log(Object.getPrototypeOf(pureObject)); // null

// 示例4:原型链验证
const animal = { type: 'animal' };
const dog = myCreate(animal);
dog.breed = 'Husky';

console.log(dog.breed); // Husky (自身属性)
console.log(dog.type); // animal (继承自原型)
console.log(dog.hasOwnProperty('breed')); // true
console.log(dog.hasOwnProperty('type')); // false

关键点

  • 原理:利用构造函数和 new 操作符,通过设置构造函数的 prototype 属性来建立原型链关系

  • 参数校验proto 参数必须是对象或 null,否则抛出 TypeError 异常

  • 中间构造函数:创建一个临时的空构造函数 F,避免直接修改原有对象

  • 原型指向:将构造函数的 prototype 指向传入的 proto 对象,使得新创建的实例能够继承 proto 的属性和方法

  • 第二个参数:使用 Object.defineProperties() 处理可选的属性描述符对象,可以精确控制属性的特性(可写、可枚举、可配置等)

  • 与直接赋值的区别Object.create(proto) 创建的是一个全新的对象,proto 在其原型链上;而直接赋值 obj.__proto__ = proto 是修改现有对象的原型(不推荐使用)

  • 应用场景:实现继承、创建纯净对象(Object.create(null))、对象克隆等