Consider the following code
<body ng-app="myApp">
<h1>Hello {{name}}</h1>
<h2>reverse of your name is {{ name | reverse }}</h2>
<input type="text" ng-model="name">
<script src='https://ajax.googleapis.com/ajax/libs/angularjs/1.2.3/angular.js'></script>
<script>
angular.module('myApp', [])
.filter('reverse', function(){
return function(str){
return str.split('').reverse().join('');
}
});
</script>
</body>
The interested bit here is the reverse filter. Here is what I think its doing:
calls angular.filter() with two args. arg1: string & arg2: function(anonymous or no-name function to be precise)
arg2 function doesn't take a argument and instead nested another anonymous function inside it.
This nested anonymous function does takes single argument - the text or string on which this filter would apply.
this nested anonymous function takes the string, reverses it.
Q1. Is my understanding correct?
Q2. What the difference between:
normal version:
angular.filter('reverse', function(str){
return str.split('').reverse().join('');
});
nested version:
angular.filter('reverse', function(str){
return function(str){
return str.split('').reverse().join('');
}
});
Q3. Why is extra level of function nesting useful & under what circumstances will I return the value directly. Or return a function which then does return the actuall result?
Q4. How does this affects scope? does closures have any role to play here?
JSFiddle: http://jsfiddle.net/C7EDv/
(Q1.1) Right- two args, a string with the name of the filter and...
(Q2/3) The second parameter to filter (arg2) is a constructor (or "factory") function. It is only executed once upon creation of the filter.
The constructor function must return a function. That returned function will be executed when the filter it is associated with is used. Put another way, the returned function is what is injected (using $injector) in to the filter (http://docs.angularjs.org/api/ng.$filterProvider)
I've added comments below detailing this:
angular.filter('reverse', function(service){
// This is run only once- upon creation
return function(str){
// This is run every time the filter is called
return str.split('').reverse().join('');
}
});
(Q3) You'll always use this form ("the nested form") otherwise you'll get an error (Object ... has no method 'apply') as Angular needs a function to be returned which it can call (using apply()) whenever the filter is used. This works exactly like all providers in Angular including services.
(Q2) Were it possible to do what you called the "normal version" then the filter would run only once, upon creation. And whatever return value it got would be used for every invocation of the filter. Since having a filter that always returns the same value wouldn't be useful very often Angular instead uses this javascript constructor pattern (which you'll see used elsewhere in JS) so that each use of the filter results in a new invocation of the associated function.
(Q1.4)Yes, the returned function does reverse the string. Here's a nice 2 minute video on exactly this filter: http://www.thinkster.io/pick/EnA7rkqH82/angularjs-filters
(Q1.2/3)Should your filter use any services you'd pass them in where I used service above (the above link has an example of that). The parameter str is the input to the filter like you noted.
(Q4) The "run once" area does create a closure- so you can use it like you would any other closure.
Related
Starting to get into angular.js, I've seen a common pattern relating to arguments of callbacks. An example is the ui-router documentation:
var myApp = angular.module('myApp', ['ui.router']);
myApp.config(function($stateProvider, $urlRouterProvider) {
// Do stuff with ui-router providers
}):
But, from want I've seen, the myApp.config callback's arguments could be different and it would still behave as expected:
myApp.config(function(OtherProvider) {
// Do stuff with provider from another service
});
How does myApp.config know the difference? Is there done weird magic introspection going on, or is there some basic JS concept that allows for this? E.g. something like:
myApp.config = function(callback) {
var providerNames = callback.argumentNames;
var providers = this.findProviders(providerNames);
};
Perhaps I'm too used to python, where this sort of feature would only be possible through some very scary hacks.
This is basically part of javascript. Javascript is very very flexible regarding functional arguments when it is invoking.
For example you declare a function:
function abc (first, second) {
}
But when you call it, you can pass any number of arguments even without zero argument. If you don't pass an argument but it has been defined when declaring the function, you will get value 'undefined' for that particular argument but it does not interrupt code execution.
Javascript provide a special local array (Basically everything is object in JS) named 'arguments' inside the function. I am calling special because it an Array with only one Array properties 'length' other Array methods or properties are unavailable
abc(firs,second,third)
Inside abc, we can check provided arguments
function abc (first, second) {
var args_total = arguments.length;
var no_of_defined_args = Function.length;
}
Yes Function.length is another properties by which you can check how many arguments are really defined by this function
This is a "scary hack" used by Angular for one method of dependency injection - where the service names are automatically resolved from the argument names. There is no intrinsic support for such an operation at the language level.
The argument values (but not the argument names) can be accessed via the arguments object within the function. However, the argument names themselves can only be accessed by applying [[ToString]] to the function object and parsing the result1, which is a "scary hack".
In the explicit forms the injected parameters must appear in the same order as the list of supplied dependencies - in this case the names of the parameters don't matter because the service names have been supplied separately.
An excerpt of the forms, from the linked documentation, slightly modified:
// service names supplied separately
// - only Order of parameters matters
someModule.controller('MyController',
['$scope', 'greeter', function(I_am_a_scope, greeter) { ..
// service names supplied separately
// - only Order of parameters matters
var MyController = function($scope, GREETER) { ..
MyController.$inject = ['$scope', 'greeter'];
// "scary hack" magic of Angular JS, as service names not specified separately
// - the Name of the parameters is used as service name
// arguably Too Much Magic (TM), and a source of pain for minification
someModule.controller('MyController', function(greeter, $scope) { ..
The first two forms use Function.prototype.apply, which is similar to apply() in Python. The third asks for, and parses, the textual representation of the function - a "scary hack".
1To see how the magical form can be implemented, consider the following:
f = function (a,b) { return a+b }
f.toString() // e.g. -> "function (a, b) { return a + b; }"
ECMAScript 5 says Function.prototype.toString returns:
An implementation-dependent representation of the function is returned. This representation has the syntax of a FunctionDeclaration. Note in particular that the use and placement of white space, line terminators, and semicolons within the representation String is implementation-dependent.
(I'm not aware of any modern browsers that don't return a 'usable' result; the behavior of returning a representation is not optional is ES5.)
i'm trying to create a JavaScript object named human with three methods walk, eat and talk, and i want to call it like this (no method should print any values): human.talk('hello').walk('home').eat('pizza').
I have this code:
var human = {
talk : function talk(t){
},
walk : function walk(w){
},
eat : function eat(e){
}
};
console.log(human.talk('hello').walk('home').eat('pizza'));
But i'm receiving Uncaught TypeError: Cannot call method 'walk' of undefined
Why??
Each function needs to return this if you want to be able to chain functions. You are getting the error because the function talk is returning undefined and you are essentially trying to call undefined.walk('home').
You cannot just chain calls like that. Did you mean to log all three results?
console.log(human.talk('hello'),human.walk('home'),human.eat('pizza'));
If you did want to have a "fluent" call chain, your functions all need to return this (so that you can proceed to call functions on it).
its not exactly clear what you are trying to achieve with the code above but from what I can see, there are two options:
A. you are trying to call all three functions and have console.log print the results of the functions in sequence, one after the other, ie you are using the (.) operate to concatenate the results of the function calls into one long string, in this case i would like to remind you that the concatenation operator in javascript, unlike, say php is + and not (.) so rather use this:
console.log(human.talk('hello') + walk('home') + eat('pizza'));
B. You actually want to call a chain for function calls. in which case i would like to remaind you that the . operator retrieves a property or method of an object ie the syntax is (object).(property), always the identifier or value before the operator should be an object. So, in order for you chain call to work, all your given function should return an object,(well, maybe except for the last one) in this case, the appropriate return value would be "this":
var human = {
talk : function(t){
return this;
},
walk : function(w){
return this;
},
eat : function(e){
return this;//or whatever here
}
};
I'm writing a JS function that I would like to be used directly with an onfocus event, and also within another function. The problem is that if I pass the function this, then all of my function code needs to be field.value. That causes a problem when I use it inside a function, since the passed variable doesn't have a .value attribute.
My thought was then to just pass this.value to the function, so I could just work with the data passed, whether it was a field value or a function value. However, now it doesn't change the value of the original field.
Is there a good way around this, or do I just need to have two functions?
Here is my two different versions of the code:
// Can't be used with when using onfocus="makeNum(this.value)"
function makeNum(value){
value = value.match(/\d+(\.\d{1,2})?/g).join("");
}
OR
// Can't be used with a function, but works when using makeNum(this)
function makeNum(field){
field.value = field.value.match(/\d+(\.\d{1,2})?/g).join("");
}
Objects are passed by reference, but primitives are passed by value. Since this.value is a primitive (i.e. a string or number for example), it will be copied when you pass it to the function, and modifications to it only apply the the function.
Write the function so it returns a value, then assign it back to this.value like this:
this.value = makeNum(this.value);
function makeNum(value){
return value.match(/\d+(\.\d{1,2})?/g).join("");
}
If you showed the two flavors of desired code, we could probably help a lot more specifically. Objects are passed by reference, but individual variables are not so you can't have the same behavior for both.
Your options I can think of are this:
Pass an object into the function as an argument then refer to obj.value in order to change the actual value on the original object.
Pass a callback function to your main function that operates on the object and you can then have several different ways of setting the value from the same core code.
Break your core function up into smaller functions that can be called from different places and used in different ways when operating on different types of source data.
Write a single function that can tell from its arguments which type of data you are passing to it and operate accordingly.
In the case of your code examples, here's the core function that you can use in a couple different ways:
function makeNum(value){
return(value.match(/\d+(\.\d{1,2})?/g).join(""));
}
x.onfocus = function() {
this.value = makeNum(this.value);
}
why does this work ?
<html>
<body id = "body">
<div id="para">hello world, invalid js API signature doesn't throw error !!!</div>
<script>
var temp = document.getSelection("parameter");
</script>
</body>
</html>
getSelection() does not take any parameter as per standard signature.
I have tested this and it does not throw any error on JS console either.
JavaScript doesn't complain if you call a function with the wrong number of arguments. If there are fewer arguments passed than declared, the extra parameters have the value undefined. If there are more, they are simply ignored. This is a feature and not a bug!
Inside the JavaScript function you have access to the arguments value which is an array-like structure holding all the arguments passed to it. If you wanted you could define all your functions to take 0 parameters and use arguments[0], arguments[1], etc instead of the named parameters you previously had (though i don't see why one would do that) and the functions would still work exactly the same.
The benefits of having this arguments structure and no limits on how many arguments you call a function with allows you to do very powerfull things, like the bind polyfill found here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
If I have the following JavaScript function
function foo(first, last){
return "from Foo";
}
JavaScript will execute and will return for all of the following
foo();
foo(2);
foo("abc");
foo("abc", 3, 5);
If you want to deal with all the passed parameter. you can get those from "arguments".
MDN link
There's an example in Secrets of the JavaScript Ninja that provides the following code to get around JavaScript's Math.min() function, which requires a variable-length list.
Example: Math.min(1,2,3,4,5);
But, there's a problem if you have a list: [1,2,3,4,5], since you'd rather not have to loop through the list, keeping track of the current min.
The book says to use the following code to solve this issue.
function smallest(arr) {
return Math.min.apply(Math, arr);
}
alert(smallest([1,2,3,4,5,6,-33]));
What happens with Math.min.apply(Math, arr) under the hood?
The .apply method "Calls a function with a given this value and arguments provided as an array (or an array like object)."
So basically it works as if you'd passed each item of the array as a separate parameter when calling the function directly.
The first parameter to .apply() sets the value of this to be used by the function, so your example passes Math so that it works the same as if you called it directly as Math.min(...).
Note that you can do the same thing with any function:
function someFunc(a,b,c,d) { ... }
someFunc(1,2,3,4);
// or
someFunc.apply(null, [1,2,3,4]);
Per the MDN documentation for Function.prototype.apply:
Calls a function with a given this value and arguments provided as an array (or an array like object).
The apply allows you to invoke a method on a given instance and provide the arguments as an array. So when you write
Math.min.apply(Math, arr);
You are invoking the min method on the Math class and passing arr as arguments.