为函数添加属性并设置原型:Vector类代码处理技术问询
Hey there! Looks like you're building a 2D Vector class and have already laid down a constructor plus some static utility methods, though the prototype set method is incomplete. Let me walk you through how to properly add static properties/methods to the function, fix the prototype chain setup, and make this class robust and easy to work with.
一、First: Understand Static vs. Prototype Methods
It's key to keep these two straight for clean, maintainable code:
- Static methods (like your
Vector.add): Attached directly to theVectorconstructor. Use these for generic operations that don't depend on a specific instance's state (e.g., adding two vectors, generating a random vector). - Prototype methods: Mounted to
Vector.prototype, so everyVectorinstance inherits them. Use these for operations that modify or read the current instance's state (e.g., updating x/y values, cloning the vector).
二、Polish Your Existing Static Methods
Your static methods are off to a good start—we just need to make sure they play nicely with the prototype methods we'll add later. Here's the cleaned-up constructor and static method code:
function Vector(x, y) { this.x = x || 0; this.y = y || 0; } // Static: Add two vectors, return a new Vector Vector.add = function(a, b) { return new Vector(a.x + b.x, a.y + b.y); }; // Static: Subtract vector b from a, return a new Vector Vector.sub = function(a, b) { return new Vector(a.x - b.x, a.y - b.y); }; // Static: Scale a vector, return a new scaled instance Vector.scale = function(v, s) { return v.clone().scale(s); }; // Static: Generate a random vector with x/y in [-1, 1] Vector.random = function() { return new Vector(Math.random() * 2 - 1, Math.random() * 2 - 1); };
三、Set Up the Prototype Correctly (And Finish That set Method!)
When you assign a new object to Vector.prototype, you overwrite the default constructor reference—so we need to manually point it back to Vector to avoid bugs with instance type checks. Let's also finish the set method and add essential prototype methods:
Vector.prototype = { // Critical: Restore the constructor reference constructor: Vector, // Finish the set method: Support both object and individual x/y arguments set: function(x, y) { if (typeof x === 'object') { // Handle object input (e.g., {x: 5, y: 3}) this.x = x.x || 0; this.y = x.y || 0; } else { // Handle individual x/y values this.x = x || 0; this.y = y || 0; } return this; // Enable method chaining }, // Clone the current vector, return a new instance clone: function() { return new Vector(this.x, this.y); }, // Scale the current vector (modifies the instance itself) scale: function(s) { this.x *= s; this.y *= s; return this; // Enable method chaining }, // Calculate the vector's magnitude (length) magnitude: function() { return Math.sqrt(this.x * this.x + this.y * this.y); }, // Normalize the vector to a length of 1 normalize: function() { const mag = this.magnitude(); if (mag !== 0) { this.scale(1 / mag); } return this; // Enable method chaining }, // For easy debugging toString: function() { return `Vector(${this.x}, ${this.y})`; } };
四、How to Use Your Vector Class
Now your class is ready to go! Here are some quick examples to show it in action:
// Create instances const v1 = new Vector(2, 3); const v2 = new Vector(4, 1); // Use static methods const sum = Vector.add(v1, v2); console.log(sum.toString()); // Output: "Vector(6, 4)" // Use prototype methods with chaining const v3 = new Vector().set({x: 5, y: 5}).scale(2); console.log(v3.magnitude()); // Output: ~14.142 // Generate a random vector const randomVec = Vector.random(); console.log(randomVec); // Output: e.g., Vector(-0.32, 0.87)
五、Optional: ES6+ Class Refactor
If your environment supports ES6+, rewriting this with class syntax makes the code more readable and automatically handles the constructor reference:
class Vector { constructor(x = 0, y = 0) { this.x = x; this.y = y; } static add(a, b) { return new Vector(a.x + b.x, a.y + b.y); } static sub(a, b) { return new Vector(a.x - b.x, a.y - b.y); } static scale(v, s) { return v.clone().scale(s); } static random() { return new Vector(Math.random() * 2 - 1, Math.random() * 2 - 1); } set(x, y) { if (typeof x === 'object') { this.x = x.x ?? 0; // Use nullish coalescing for stricter checks this.y = x.y ?? 0; } else { this.x = x ?? 0; this.y = y ?? 0; } return this; } clone() { return new Vector(this.x, this.y); } scale(s) { this.x *= s; this.y *= s; return this; } magnitude() { return Math.hypot(this.x, this.y); // Cleaner alternative to sqrt } normalize() { const mag = this.magnitude(); mag && this.scale(1 / mag); return this; } toString() { return `Vector(${this.x}, ${this.y})`; } }
Note: Using ?? (nullish coalescing) instead of || prevents unintended overwrites of valid 0 values.
内容的提问来源于stack exchange,提问作者Pavlin Petkov

