JavaScript对象创建方式对比及差异探究
Great question—you’re already digging into the core of JavaScript’s object model, which is awesome! While it’s true that most object creation patterns end up with Object.prototype in their prototype chain (except edge cases like Object.create(null)), there are several key differences beyond just that. Let’s break them down:
Syntax & Readability
Literal notation (const obj = { key: value }) is the most concise and readable for simple, one-off objects. Constructor functions (function MyObj() { this.key = value }) feel more procedural and require usingnewto instantiate. ES6 classes (class MyObj { constructor() { this.key = value } }) bring a familiar class-based OOP syntax that’s easier for developers coming from languages like Java or C# to follow, making collaboration smoother on larger projects.Prototype Chain Flexibility
- Object literals default to
Object.prototypeas their__proto__, but you can customize this withObject.create()(e.g.,const obj = Object.create(customProto)). You can even create "prototype-less" objects:const obj = Object.create(null)—these act as pure dictionaries, avoiding conflicts with built-in methods liketoString()orhasOwnProperty(). - Constructor functions let you modify their prototype directly (e.g.,
MyObj.prototype.myMethod = function() {}), so all instances share the same method. However, these prototype methods are enumerable by default. - ES6 classes also attach methods to the prototype, but class methods are non-enumerable by design—matching the behavior of built-in JavaScript objects.
- Object literals default to
Instantiation &
thisBinding- Constructor functions can be called without
newin non-strict mode, which bindsthisto the global object (a common source of bugs). Strict mode fixes this by throwing an error. - ES6 classes require using
newto instantiate; calling a class withoutnewthrows an immediate error, eliminating that bug entirely. - Object literals have no
thisconfusion—they’re direct instance creations with no instantiation steps.
- Constructor functions can be called without
Private State Implementation
- ES6 classes have native support for private fields using the
#syntax:
These fields are truly private and can’t be accessed or modified outside the class.class MyObj { #privateField = 'secret'; getSecret() { return this.#privateField } } - Constructor functions need to simulate privacy using closures:
This works, but the private variable is tied to each instance (not shared on the prototype).function MyObj() { const privateVar = 'secret'; this.getSecret = () => privateVar; } - Object literals can also use closures for privacy, but it’s a manual workaround rather than native syntax.
- ES6 classes have native support for private fields using the
Inheritance Complexity
- ES6 classes make inheritance trivial with the
extendskeyword:
The engine handles prototype chaining and parent constructor calls automatically.class Child extends Parent { constructor() { super(); // Required before using `this` this.childProp = 'value'; } } - Constructor function inheritance requires manual, error-prone setup:
function Child() { Parent.call(this); // Invoke parent constructor } // Link prototype chain Child.prototype = Object.create(Parent.prototype); Child.prototype.constructor = Child; // Fix constructor reference - Object literals can inherit from another object via
Object.create(parentObj), but extending behavior requires manually copying or defining properties on the child.
- ES6 classes make inheritance trivial with the
Memory Efficiency
- Constructor functions and classes share prototype methods across all instances, saving memory (only one copy of each method exists, not one per instance).
- Object literals with inline methods (e.g.,
const obj = { myMethod() {} }) create a new copy of the method for every instance—this adds up if you’re creating many objects of the same "type".
At the end of the day, all these patterns serve the same core purpose of creating objects, but their tradeoffs in syntax, flexibility, and tooling make them better suited for different scenarios. Your observation about the shared Object.prototype chain is spot-on, but these nuanced differences are what make choosing the right pattern important for maintainable code.
内容的提问来源于stack exchange,提问作者Willem van der Veen

