JavaScript中避免使用this的方法及相关技术疑问
Hey there! Let’s unpack all your questions about that Raining Chain pattern, JSLint’s beef with this, and your existing class-based code—there’s a lot to cover, so let’s go step by step.
1. What does that MakeStuff factory function do?
That code is a factory function—a plain old function that creates and returns an object without needing the new keyword. Instead of using class syntax or constructor functions tied to this, it builds an object literal (const self = {}), attaches internal properties (self._param1, self._param2), and hands back the finished instance. It’s a way to create objects without relying on JavaScript’s prototype-based class mechanics, or the confusing context shifts that come with this.
2. What’s self in this pattern?
self is just a regular variable name—no special magic here! It’s a convention to refer to the object we’re building, so we can attach properties and methods to it without worrying about unexpected context changes. Unlike this, which shifts its value based on how a function is called (e.g., callbacks or event handlers can break this binding), self is a fixed reference to the object we created. You could rename it obj, instance, or even widget—it’s just there to make the code readable.
3. Can I avoid this in a JS class constructor?
Technically? Yes. But it’s going to be clunky and defeat most of the benefits of using classes in the first place.
Classes are designed around this to reference the instance being created. If you tried to avoid it, you’d have to create a separate object in the constructor, bind all your class methods to that object, and return it—like this:
class Stuff { constructor(param1) { const self = {}; self._param1 = param1; // Bind every class method to `self` to avoid `this` confusion self.doThing = this.doThing.bind(self); return self; } doThing() { console.log(self._param1); // Oops—`self` isn't in scope here! You'd need closures to fix this } }
This breaks the prototype chain (each instance gets its own copy of every method, wasting memory) and throws away class features like extends and super. Save yourself the headache—this is a core part of how classes work.
4. Why does JSLint reject this?
JSLint’s creator, Douglas Crockford, has strong opinions about JavaScript’s quirks. He sees this as a frequent source of bugs because its binding is dynamic: it depends on how a function is called, not where it’s defined. For example, if you pass a class method as a callback to an event handler, this might suddenly point to the window object (in non-strict mode) or undefined, leading to hard-to-debug errors.
Crockford prefers factory functions (like the MakeStuff pattern) or arrow functions (which capture this from their surrounding context) because they eliminate this kind of context confusion.
5. Should I switch to the MakeStuff pattern and add getters/setters/static methods via prototypes?
It depends on your priorities:
If you want to comply with JSLint without changing config: You can adapt the factory pattern to support getters/setters and static methods. For example:
- Getters/setters: Use
Object.definePropertyon theselfobject to add controlled access to internal values:const MakeStuff = function(param1) { let _param1 = param1; // Private variable, not accessible directly from outside const self = {}; Object.defineProperty(self, 'param1', { get: () => _param1, set: (value) => { // Add validation logic here if needed _param1 = value; }, enumerable: true }); return self; }; - Static methods: Attach them directly to the
MakeStufffunction itself (since functions are objects in JS):MakeStuff.staticHelper = function() { console.log("This is a static method for all MakeStuff instances!"); };
But keep in mind: converting 1500 lines of class-based code will be time-consuming, and you’ll lose class features like easy inheritance. Also, factory functions that attach methods directly to
selfcreate a copy of each method for every instance—less memory-efficient than sharing methods via class prototypes.- Getters/setters: Use
If you want to keep your class code: You might need to either adjust JSLint’s config (even if you initially didn’t want to, check if there’s an option to allow
thisin class contexts) or switch to a more flexible linter like ESLint.
At the end of the day, it’s a tradeoff between adhering to JSLint’s strict rules and keeping the maintainability of your existing class code.
内容的提问来源于stack exchange,提问作者user7047022

