JavaScript中Object.create与new关键字的区别及proto变量undefined问题
嘿,这个问题问到点子上了——很多刚摸透JavaScript原型链的开发者都会在这里踩坑。我分两部分给你讲清楚:
Object.create() 和 new 关键字的核心区别 这俩都是用来创建对象的,但底层逻辑和使用场景差得挺多:
1. 执行流程完全不同
new Constructor()背后偷偷做了四件事:- 先创建一个空的新对象
- 把这个新对象的
__proto__绑定到构造函数的prototype属性上 - 调用构造函数,把
this指向这个新对象(所以构造函数里的this.xxx会直接挂在新对象身上) - 如果构造函数没返回引用类型(比如对象、数组),就返回这个新对象;否则返回构造函数自己返回的那个对象
Object.create(proto, [props])就简单多了:它直接以你传入的proto对象作为新对象的__proto__来创建对象,完全不会执行任何构造函数。第二个可选参数是用来给新对象添加自身属性的,格式和Object.defineProperties()一致。
2. 原型指向的灵活性不一样
- 用
new的话,你只能间接指定原型——新对象的__proto__固定指向构造函数的prototype,除非你先改构造函数的prototype,不然没法直接把任意对象当原型。 Object.create()就灵活多了:你可以传入任何对象(甚至null,这时新对象没有原型,连toString这种基础方法都没有)作为新对象的原型。比如Object.create(null)常用来做干净的字典,不用担心和Object.prototype上的方法重名。
3. 对象自身属性的来源不同
new出来的对象,自身属性主要来自构造函数里通过this绑定的内容;原型链上的属性才来自构造函数的prototype。Object.create()创建的对象,自身属性要么是第二个参数定义的,要么是后续手动加的;原型链上的属性全来自你传入的proto对象。
undefined? 我猜你大概率是搞混了原型指向的对象——先看两个典型的对比例子:
例子1:用new能正常访问原型变量
function Animal() {} Animal.prototype.sound = "woof"; const dog1 = new Animal(); console.log(dog1.sound); // 输出 "woof"
原因很简单:dog1的__proto__指向Animal.prototype,当访问dog1.sound时,dog1自己没有这个属性,就会沿着原型链往上找,刚好在Animal.prototype上找到,所以能输出。
例子2:错误使用Object.create导致undefined
const dog2 = Object.create(Animal); // 注意这里传的是构造函数Animal,不是Animal.prototype console.log(dog2.sound); // 输出 undefined
这里的问题在于:dog2的__proto__指向的是Animal这个函数本身,而不是Animal.prototype。Animal函数的原型链是Animal → Function.prototype → Object.prototype,根本碰不到Animal.prototype上的sound属性,自然返回undefined。
例子3:正确使用Object.create就能正常访问
const dog3 = Object.create(Animal.prototype); console.log(dog3.sound); // 输出 "woof"
这时候dog3的__proto__和new Animal()出来的dog1完全一致,原型链能找到Animal.prototype上的sound,所以正常输出。
还有一种少见但容易踩的坑:如果构造函数返回了一个引用类型的值,new会直接返回这个值,而不是它创建的新对象,这时候新对象的原型链就和构造函数的prototype没关系了:
function Person() { return { name: "Charlie" }; // 返回一个对象 } Person.prototype.age = 25; const p = new Person(); console.log(p.age); // undefined —— 因为p是构造函数返回的对象,它的__proto__不是Person.prototype
总结一下:当你觉得“两种写法的原型都有某个变量”时,大概率是原型指向的对象搞错了——new绑定的是构造函数的prototype,而Object.create需要你明确传入这个prototype对象,才能和new的原型链一致。
内容的提问来源于stack exchange,提问作者Abhinav Kant

