General Object Initiating Function by the Example of $injector.instantiate Implementation in angularjs
8 Jun 2017
,
0.00 (No votes)
Rate this:
The article discovers object instantiating within angularjs applications in details and contains an example of universal function implementation
0.00 (No votes) |
|
Have you ever thought about initiating objects within your angularjs applications? Controllers,factories,services,decorators and even values - all of them finally are created using the instantiating method of the$injector
class and here is a very interesting line code that I'd like to discover for you.
Today I'm going to describe the next code line:
return new (Function.prototype.bind.apply(ctor,args))();
Is the work principle of this line obvIoUs to you? If the answer is "Yes",so,thanks for your time and patience,hope to see you in next articles. :)
Now,when all the readers who cut their teeth on JavaScript have left us,I'd like to answer my own question: When I saw this line for the first time,I was confused and didn't understand anything about these "relationships" amongbind
,apply
,0)">newand()
. Let's try to puzzle out! I offer to start doing it from the end,meaning: assume that we have some parameterized constructor,the instance of which we would like to instantiate:
function Animal(name,sound) {
this.name = name;
this.sound = sound;
}
new
"What could be easier?" - you would say and will be absolutely right:
var dog = new Animal('Dog',Woof!');
Thenew
operator is the first thing that we will need if we'd like to get a new instance of theAnimal
constructor. Let me give you some details about thenew
operator usage details:
Quote:When the code new Foo(...) is executed,the following things happen:
- A new object is created,inheriting from Foo.prototype.@H_403_229@
- The constructor function Foo is called with the specified arguments,and with 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.@H_403_229@
- The object returned by the constructor function becomes 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.)@H_403_229@
For further details:https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/new
Great,now let's wrap ourAnimal
constructor execution with the function to make it common for all possible calls in the application:
function CreateAnimal(name,sound) {
new Animal(name,sound);
}
After a while,we decide to initiate not only an animal but also a human being (I agree with you,this is not the best example ever) and this means that we have at least 2 possible ways to achieve it:
- Implement the factory which will initiate the required constructor instance itself depending on the required type@H_403_229@
- Extend the declaration of our current function with an argument that should contain the constructor function and,based on this constructor,return the new function with the bounded arguments (and in this case
bind
is really helpful)@H_403_229@
In case of$injector.instantiate
implementation,the second point was chosen.
bind
Hide
Copy Code
function Create(ctorFunc,name,255)">new (ctorFunc.bind(null,sound));
}
console.log( Create(Animal,128)">Woof') );
console.log( Create(Human,128)">Person') );
Let me give you some details aboutbind
function usage details:
The bind() method creates a new function that,when called,has its this keyword set to the provided value,with a given sequence of arguments preceding any provided when the new function is called.For further details:https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
In our case,we passnull
asthis
because we are going to use the returned frombind
function with the new operator,which ignores existingthis
and replaces it with a new empty object. The result of thebind
function call is the new function with the already bounded arguments (in other words,0)">return new fn;wherefn
is thebind
call result).
Excellent,now we can use ourCreate
function to create any animals and human beings whose constructors... are declared with thename
andsound
arguments. "But it's not true that all the arguments that are required for animal constructors will be required for human constructors in the same way" - you could reply and will be absolutely right again. And there are 2 problems that we could notice:
- Constructor declaration can vary or be changed (for example,the order or quantity of parameters),which means that we have to make changes simultaneously in a few places: constructor declaration,lines of code that call the
Create
function and instantiating line of the instancereturn new (ctorFunc.bind(null,sound))
;@H_403_229@ - The more constructors we have,the higher chance that required arguments will be different and we won't be able to continue using the same
Create
function (otherwise,we have to pass all the declared arguments for each constructor,but use only the required ones).@H_403_229@
apply
<Meta charset="utf-8" />The transparent passing arguments from theCreate
function directly to the constructor could become the solution of the above problems,in other words - the universal function that accepts the constructor and required by-passed constructor array of arguments and returns a new function with the already bounded arguments. There is a wonderful function namedapply
in JavaScript for this case (or its analogue namedcall
if we know the number of arguments beforehand).
Let me give you a small excursus aboutapply
function usage details:
The apply() method calls a function with a given this value,and arguments provided as an array (or an array-like object).apply is very similar to call(),except for the type of arguments it supports. You use an arguments array instead of a list of arguments (parameters).
For further details:https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Function/apply
<Meta charset="utf-8" />I think here is the most difficult part of the article,because first of all,we have toapply
our constructor asbind
functionthis
keyword (similarly toctorFunc.bind
). Secondly,we have to pass the shifted right by 1 constructor arguments usingctorArgs.unshift(null)
tobind
function (as we remember,the first argument of the bind function will be used asthis
function's keyword).
bindfunction is not accessible inside theCreate
,because thewindow
object is used as the function context and that's why we have to access it usingFunction.prototype
.
Finally,we get the next universal instantiating function:
null);
new (Function.prototype.bind.apply(ctorFunc,ctorArgs ));
}
console.log( Create(Animal,[Woof']) );
console.log( Create(Human,128)">Person',128)">John',128)">Engineer',128)">Moscow']) );
If we return to angularJS,we will notice thatAnimal
andHuman
are used precisely as factory constructors,and their array arguments are represented by found and resolved dependencies.
angular
.module(app')
.factory(function($scope) {
// constructor
});
or:
angular
.module(app')
.factory([$scope', constructor
}]);
All we have to do at the final stage of implementing our own$injector.instantiate
method is to find out the instantiating instance constructor,receive (as possible resolve) the required arguments and that's it. :)
Feel free to send comments,vote and thank you for reading. Hope to see you in the next articles.
License
This article,along with any associated source code and files,is licensed underThe Code Project Open License (CPOL)
原文链接:https://www.f2er.com/angularjs/146841.htmlThis article,along with any associated source code and files,is licensed underThe Code Project Open License (CPOL)