admin管理员组文章数量:1419624
If I have a constructor function in JavaScript I want to be able to explicitly return something so that everything is clear when working in a team.
I read that if you use a function as a constructor in JS that if you return anything then using the new keyword will have been a wasted effort. But I am wondering if you can return this
from the constructor function and be safe, for example:
function MyConstructor(val){
this.val = val;
return this; //attention here
}
var example = new MyConstructor(val);
Does this yield the desired behavior?
If I have a constructor function in JavaScript I want to be able to explicitly return something so that everything is clear when working in a team.
I read that if you use a function as a constructor in JS that if you return anything then using the new keyword will have been a wasted effort. But I am wondering if you can return this
from the constructor function and be safe, for example:
function MyConstructor(val){
this.val = val;
return this; //attention here
}
var example = new MyConstructor(val);
Does this yield the desired behavior?
Share Improve this question edited May 24, 2015 at 5:34 Alexander Mills asked May 24, 2015 at 1:44 Alexander MillsAlexander Mills 101k166 gold badges538 silver badges919 bronze badges 6- I don't feel this is a good idea. A constructor function is unique to other functions for a reason. If you end up explicitly returning from a constructor function then your using parasitic inheritance and in essence not using a constructor function but a regular function. Why the need to confuse the two and confuse future developers by breaking de facto conventions? – Sukima Commented May 24, 2015 at 2:08
-
To be frank, your assuming you know better then others on your team. If I was on your team I would be really confused why
return this
was necessary and attempt to remove the redundant and confusing statement. I apologize if that seemed mean but I personally believe in readability and part of readability is familiarity. If there isn't a really good reason to change well known patterns it is better not to. If you do explicitly state the change with ments. This is all assuming your entire team hasn't asked for thereturn this
pattern which in case forget everything I've said. – Sukima Commented May 24, 2015 at 2:17 - IMO there needs to be some metadata in some form to mark the function as a constructor function one of them could be to explicitly return this, in some form or another :) – Alexander Mills Commented May 24, 2015 at 3:46
-
1
If you want consistent behavior regardless of whether the
new
keyword is used, don't usethis
but rather some other variable and return that, e.g.var self = {}; ... return self
– Matt Browne Commented May 24, 2015 at 3:48 - it might hv to be var self= this; for protypical inheritance to work as expected. – Alexander Mills Commented May 24, 2015 at 4:12
5 Answers
Reset to default 3Let's start with the normal behavior of JavaScript. If you do not return anything from a constructor function, it will work as expected (of course).
Hence
var Dog = function (name) {
this.name = name;
};
var alice = new Dog('Alice');
results in a new object whose name
property is set to Alice
.
Now what happens if you try to override the implicit return
statement of the constructor function by explicitly calling it?
Let's introduce a return
statement that returns something else:
var Dog = function (name) {
this.name = name;
return 23;
};
var alice = new Dog('Alice');
Which value is alice
now? Probably, you'd expect that it's 23
, but actually it isn't. It still is an object with a name
property set to Alice
.
The problem here is that JavaScript is smarter than you: It sees your return
, but realizes that the type of the thing you want to return does not match the fact that the function has been called using new
. Hence the return
is ignored.
Now what if we try to get smarter than JavaScript and return something whose type matches, i.e. an object?
var Dog = function (name) {
this.name = name;
return { foo: 'bar' };
};
var alice = new Dog('Alice');
In this case, JavaScript thinks that you have a good reason to override the implicit return
and actually uses yours. This means that alice
now points to an object with a foo
property which is set to 'bar'
.
So, to cut a long story short: If you explicitly call
return this;
you end up with the last case. You override the object that is implicitly being returned. But since the one you return is actually the same as the one that would have been returned implicitly, there is no difference.
So: Yes, it is the same, but the call to return this;
is not required.
Some developers use this behavior to trick JavaScript into always returning a new object, no matter whether you call the constructor function with or without new
:
var Dog = function (name) {
return {
name: name
};
};
var alice1 = new Dog('Alice');
var alice2 = Dog('Alice');
Both calls result in a new object with a name
property set to Alice
, but there are some differences to the previous examples:
- Both objects are just object literals, whose
constructor
property is not set toDog
, neither they use the expected prototype chain. - In the case with
new
actually two objects are being created: One bynew
, the other by you using the object literal syntax. This means more work for the garbage collector.
Hence, I think, that you should avoid this technique, and either use constructors correctly or stick to factory functions.
This is the same effect that was made use of in @maboiteaspam's answer:
function DHT (opts) {
var self = this
if (!(self instanceof DHT)) return new DHT(opts)
// ctor body
}
If you call this function with new
, this
is set to an object whose constructor
property points to the DHT
function. Hence self instanceof DHT
returns true and the actual constructor is run.
If you call this function without new
, the if
statement detects this and runs it with new
for you automatically (which is at least better than the solution described above, where you end up with two disadvantages).
Please note that in strict mode it is not allowed to access this
from a function that is not called on an object, hence calling a constructor function without the new
keyword would result in an error as soon as you tried to access this
.
You can return this
in your constructor, but will have the same effect as not returning anything.
From the MDN docs:
When the code
new Foo(...)
is executed, the following things happen:
- A new object is created, inheriting from
Foo.prototype
.- The constructor function Foo is called with the specified arguments and this bound to the newly created object. new Foo is equivalent to new
Foo()
, i.e. if no argument list is specified, Foo is called without arguments.- The object returned by the constructor function bees the result of the whole new expression. If the constructor function doesn't explicitly return an object, the object created in step 1 is used instead. (Normally constructors don't return a value, but they can choose to do so if they want to override the normal object creation process.)
A different approach could be :
function DHT (opts) {
var self = this
if (!(self instanceof DHT)) return new DHT(opts)
// ctor body
}
Credits goes to https://github./feross/bittorrent-dht/blob/master/client.js
I'm pretty sure what you are looking for is naming conventions not language features. The problem with that approach is that you obfuscating the intent. Imagine you had this coding style as part of your team. If I was on it I would probably have the following dialog in my head several times a day while working on the code:
var foo - foobar(); // Um wait is this a constructor or a function?
// I guess I'll have to look it up:
function foobar() {
// skip a bunch of code.
return this;
}
// Huh? Why do I need to return this? And what is this supposed to refer to?
// Do I need to always provide a context when calling this function?
// Oh wait that some weird convention I was told about during employee initiation.
// Guess I have to deal with this for now and note to self:
// Brush up my resume, gonna need it soon.
var foo = new foobar();
So the moral is that at the time of using a function the programmer has no information how that function is meant to be called.
A better method would be to use naming conventions in that every function that starts with a capital letter is a constructor while lowercase is just a normal function.
This convention is widely known and is used on many teams:
function FooBar() {}
var foo = new FooBar(); // Capital letter need to use new
var bar = fooBar(); // lowercase means a normal function.
Update: Actually, in strict mode, this
is undefined, so the programmer who forgot to use this
would immediately get an error - so that would be the quickest way for them to notice their mistake. So simply using strict mode might be the best solution:
"use strict";
function MyConstructor(val){
this.val = val;
}
(Or you could put "use strict" inside the function if you have other code in the same script that you don't want to run in strict mode.)
Here's an example that shows in more detail how you could make it work the same regardless of whether or not new
is used:
function MyConstructor(val) {
var self = Object.create(MyConstructor.prototype);
self.constructor = MyConstructor;
self.val = val;
return self;
}
var example = new MyConstructor(val);
var example2 = MyConstructor(val); //same result
Of course, that's a little verbose, so in practice it would probably be simpler to use the approach in @maboiteaspam's answer.
Personally I don't worry about this; if a programmer fails to use the new
keyword, they should notice the mistake since they would just get the window
object back (or null if in strict mode) instead of a new object instance.
But if you prefer a style that doesn't require the use of the new
keyword at all, then you could consider using a library intended for more prototypal usage, e.g.:
- https://github./Raynos/pd
- https://github./Gozala/selfish
Or you could just use a naming convention that makes the intended usage more clear, as @Sukima suggested. You could prefix your maker functions with "make" or "create" to make it more clear, e.g. makeUser()
or createUser()
. Or to use the style from the "selfish" library above, User.new()
.
本文标签: javascriptReturning this from a constructor function in JSStack Overflow
版权声明:本文标题:javascript - Returning this from a constructor function in JS - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1745316582a2653183.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论