The interviewer is very busy, but I am not just a hot spot, the topic of today’s chat is absolutely the knowledge point with a high hit rate in the interview. When I was reviewing javascript functions, I noticed an interesting point about the explicit return constructor, which led to a wave of brainstorming……
We know that the following steps will happen to the new constructor if we do nothing special.
- First create a new object, the new object’s
__proto__
Property that points to the constructorprototype
attribute - At this point the constructor executes in the environment
this
Point to this new object - Execute the code in the constructor, usually by
this
Adds a new member property or method to a new object. - Finally, return the new object.
Let’s verify:
function Test() {
console.log(JSON.stringify(this));
console.log(this.__proto__.constructor === Test);
this.name = 'jack';
this.age = 18;
console.log(JSON.stringify(this)); } var a = new Test(); // The Chrome console will print the following / / {} // true // {"name":"jack","age":18} Copy the code
That’s exactly what we know. Nothing wrong with it.
Implement a new
So after recognizing the key steps in the new instantiation process, we can also answer a common interview question: How do you implement a new?
Implementing a new means you can’t use the new keyword, so you have to do a series of steps, of course, through functions.
// func is the constructor,... Args are the arguments that need to be passed to the constructor
function myNew(func, ... args) {
// Create an empty object and specify func.prototype
var obj = Object.create(func.prototype);
// The new constructor executes the function and specifies this
func.call(obj, ... args); // Finally return the object return obj; } Copy the code
With these four key steps as a guide, we quickly wrote the code implementation. From this POINT I can also realize the importance of thinking, don’t be a tool person, code is a tool!
That’s not a problem from the implementation logic, so let’s verify that.
function Test(name, age) {
this.name = name;
this.age = age;
}
myNew(Test, 'Ming'.18); // The Chrome console will print the following // Test {name: "小明", age: 18} Copy the code
The constructor returns explicitly
An explicit return is when an Object is returned in the constructor. An explicit return is when an Object is returned in the constructor.
We can try this:
function Test() {
this.name = 'jack';
this.age = 18;
return {
content: 'I have a freestyle'
} } new Test(); // The Chrome console will print the following // {content: "freestyle"} Copy the code
How useful is a return of a normal type? Like strings, numbers? Try it.
function Test() {
this.name = 'jack';
this.age = 18;
return 'I have a freestyle'
}
new Test(); // The Chrome console will print the following // Test {name: "jack", age: 18} Copy the code
As you can see, when we return a data of a normal type, it doesn’t affect the result, it still returns the new object from new.
We should also know that the new constructor is designed to create objects. It doesn’t make any sense for you to return a normal type of data such as a string, so our concern should be to return a special object. Please read on.
No new instantiation
By “no new instantiation”, we mean that the object is instantiated without the new keyword (of course, this is not new, just at the call level, the bottom level is still new). We’ve already experienced this with jQuery.
// Instantiate a jQuery object without using new
var ele = jQuery('<div>freestyle</div>');
Copy the code
So how does this dark technology work?
As mentioned earlier, we can return a custom object with an explicit return in the constructor, so there is room here. Let’s take a look at a simple example:
function Shadow() {
this.name = 'jack';
this.age = 18;
}
function jQuery() { return new Shadow(); } var obj1 = jQuery(); console.log(obj1) // The Chrome console will print the following // Shadow {name: "jack", age: 18} Copy the code
JQuery () uses a trick to instantiate the object. The hidden new Shadow() lets you think you can create an instance without calling new directly.
If we try new jQuery(), we’ll see, “Wow, that’s exactly what jQuery() does!”
var obj2 = new jQuery();
console.log(obj2)
// The Chrome console will print the following
// Shadow {name: "jack", age: 18}
Copy the code
This is because the new constructor explicitly returns new Shadow(), so the result returned is the object instantiated by new Shadow(). Instead of calling jQuery() directly with new, we just execute jQuery() as if it were a normal function. The result is, of course, the object instantiated by new Shadow().
So, new jQuery() and jQuery() are equivalent here.
JQuery has become less and less used, but its design ideas are well worth learning. So what’s so cool about jQuery? It’s a lot of things, chaining, plugins and all of those features that we’ve heard a lot about. Without going too far, let’s take a quick look at how jQuery instantiates.
I’ve got jQuery v1.12.4 code here, which is about 1W lines, which is pretty comfortable.
Scrolling through the pages, I came to line 71 and saw the following code:
jQuery = function( selector, context ) {
return new jQuery.fn.init( selector, context );
}
Copy the code
Isn’t that what we’re familiar with? Jquery.fn. Init seems to be the Shadow in the example above. It’s starting to look like it, but it’s worth studying.
Why do you want jquery.fn?
Jquery. fn is an alias for jquery. prototype. Refer to line 91 of the source code for this.
jQuery.fn = jQuery.prototype = {
/ /...
Copy the code
How do you ensure that the prototype is pointing?
We know that there is a problem with just using new jquery.fn. init(selector, context). The problem is that the instance is not an instance of jQuery, but an instance of jquery.fn. init. So how to deal with this problem?
If we look at line 2866 of the source code, we can see:
init = jQuery.fn.init = function( selector, context, root ) {
// Create instance logic
}
Copy the code
The details of how init methods create a jQuery object and what logic they do are not the focus of this article. What we need to focus on is, how does jQuery ensure that the instantiated object’s prototype is pointing correctly? How else would the instantiated object use the various methods mounted on jquery.prototype, such as this.show() and this.hide()?
And then on line 2982, I have the answer:
init.prototype = jQuery.fn;
Copy the code
Brilliant, this hand fixes the problem perfectly by modifying the operation that the prototype points to. In this way, the instance of new init() is also an instance of jQuery.
jQuery.prototype.init.prototype === jQuery.prototype; // true
var a = $('<div>123</div>')
a instanceof jQuery // true
a instanceof jQuery.fn.init // true
Copy the code
In this way, we can get a basic design idea:
function myModule(params) {
return new myModule.fn.init(params);
}
myModule.fn = myModule.prototype = {
constructor: myModule
} myModule.fn.init = function(params) { // You can perform various operations on instance objects } myModule.fn.init.prototype = myModule.prototype; Copy the code
From there, we can extend the static methods and prototype methods, and the myModule module becomes richer and richer.
The last
Nice, a constructor that got me thinking…… Help me up! I can still learn!
This article uses mdnice smart blue theme layout