I am wondering why I need the PhoneNumberFormatter.prototype.slice method below.
Why can't I just use slice(3,6).join('') inside my other methods without needing to add PhoneNumberFormatter.prototype.slice method? When the interpreter doesn't find the method on the PhoneNumberFormatter object, wouldn't it just look up the prototype chain to find slice and join on the Array prototype?
function PhoneNumberFormatter(numbers) {
this.numbers = numbers;
}
PhoneNumberFormatter.prototype.render = function() {
var string = '';
string += this.parenthesize(this.getAreaCode());
string += ' ';
string += this.getExchangeCode();
string += '-';
string += this.getLineNumber();
return string;
};
PhoneNumberFormatter.prototype.getAreaCode = function() {
return this.slice(0, 3);
};
PhoneNumberFormatter.prototype.getExchangeCode = function() {
return this.slice(3, 6);
};
PhoneNumberFormatter.prototype.getLineNumber = function() {
return this.slice(6)
};
PhoneNumberFormatter.prototype.parenthesize = function(string) {
return '(' + string + ')';
};
// why do I need the following method?
PhoneNumberFormatter.prototype.slice = function(start, end) {
return this.numbers.slice(start, end).join('');
};
var phoneNumberOne = new PhoneNumberFormatter([6, 5, 0, 8, 3, 5, 9, 1, 7, 2]);
phoneNumberOne.render()
I guess it was created to make the code cleaner and prevents code duplication.
As the use of the slice keyword in two different places seems to confuse you I'll explain briefly the differences.
In your prototype methods (e.g. getAreaCode, getExchangeCode, ...), the keyword this represents a PhoneNumberFormatter object. When you call this.slice() (in the methods), you are calling the slice method of this object hence the one created in your code.
But in your slice method (the one in your code), you are calling this.numbers.slice(). Here you call the slice method on an array (this.numbers). You are using the native array method slice.
You could write your methods like so and remove the slice method created in your code:
PhoneNumberFormatter.prototype.getAreaCode = function() {
return this.numbers.slice(0, 3).join('');
};
I think the main confusion you seem to have is the name of the method slice. This leads you to believe that it is the array method, but in fact it is not.
The slice method mentioned in your code is the slice method of the PhoneNumberFormatter, not the one of the array.
Because of this, the interpreter could never find the slice method in the prototype chain, because the prototype of the PhoneNumberFormatter would be object, and just calling slice would rather throw an error that the method is undefined.
The this.slice method refers to PhoneNumberFormatter.prototype.slice and this one will refer to it's own numbers property and will then call the slice method on the numbers Array.
Maybe it is simply easier to rename the code like:
function PhoneNumberFormatter( numberArr ) {
this.numbers = numberArr.slice(); // copy numbers
}
PhoneNumberFormatter.prototype.getAreaCode = function() {
return this.take(0, 3);
};
PhoneNumberFormatter.prototype.take = function() {
return Array.prototype.splice.apply( this.numbers, arguments ).join('');
};
var formatter = new PhoneNumberFormatter([0,1,2,3,4,5,6,7,8,9]);
console.log(formatter.getAreaCode());
Which maybe makes it more clear to you, that the take method is simply a utility method simplifying your code, cause be honest, why would you want to repeat for 5 different methods the slice and join part, and then take a potential risk of copying an error 5 times, or in case you need to change something, need to make the same change 5 times
Related
I have code that is using currying to get the average on an array that results from concatenating two arrays: an n size array and an m size array.
var avg = function(...n){
let tot=0;
for(let i=0; i<n.length; i++){
tot += n[i];
}
return tot/n.length;
};
var spiceUp = function(fn, ...n){
return function(...m){
return fn.apply(this, n.concat(m));
}
};
var doAvg = spiceUp(avg, 1,2,3);
console.log(doAvg(4,5,6));
In this line return fn.apply(this, n.concat(m));, I don't understand why do we need to use apply. What is the object we are binding with the average function and why does just normal calling (return fn(n.concat(m));) not work?
In that example, this is not that important. It would also work if instead of this you would pass an empty object instead. It's just an example on how to use apply.
What you need to focus is on the second parameter n.concat(m). They key concept here is that passing an array as a second argument you are calling that function (fn) passing each value in the array as an argument.
About your second question: no, it won't work because fn expects several arguments (one per value to calculate the average) while by doing return fn(n.concat(m)); you are just passing one argument, an array containing all values
Maybe you would understand it better with a simpler example:
function sum3params(a,b,c){
return a+b+c;
}
console.log(sum3params([3,4,2])) // won't work
console.log(sum3params.apply(this, [3,4,2])) // will work ('this' is not important here)
For this use case, it does not. But consider the following:
var foo = {
bar: 3
};
var addBar = function(a, b) { return a + b + this.bar };
foo.add3AndBar = spiceUp(addBar, 3);
foo.add3AndBar(1); // 7
Using apply means that your spiceUp function can be applied to methods as well as normal functions. For more likely example, consider partially applying when defining a method on a prototype:
const ENV = "linux";
DoesSomePlatformSpecificStuff.prototype.getPath = spiceUp(ENV);
apply also will spread the gathered array of arguments back out into positional arguments which can also be done like so:
return fn(...n.concat(m));
Which can be simplified as
return fn(...n, ...m);
Which is equivalent to
return fn.apply(undefined, n.concat(m));
There is a lot of explanation about how to convert a function's arguments to a real array.
But I have found it very interesting when you simplify the code with the help of bind.
MDN Array.prototype.slice - Array-like objects
MDN Function.prototype.bind - Creating shortcuts
For example:
function list() {
return Array.prototype.slice.call(arguments);
}
var list1 = list(1, 2, 3); // [1, 2, 3]
Simplified call:
var unboundSlice = Array.prototype.slice;
var slice = Function.prototype.call.bind(unboundSlice);
function list() {
return slice(arguments);
}
var list1 = list(1, 2, 3); // [1, 2, 3]
It is working the same way if you use apply instead of call:
var slice = Function.prototype.apply.bind(unboundSlice);
Which can be even shortened by using call from any function instance, since is the same as the one in the prototype and same approach with slice from an array instance:
var slice = alert.call.bind([].slice);
You can try
var slice = alert.call.bind([].slice);
function list() {
console.log(slice(arguments));
}
list(1, 2, 3, 4);
So the first very weird thing is coming into my mind is calling bind on apply, but the first argument of bind should be an object (as context) and not a function (Array.prototype.slice).
The other is that is working with both call and apply the same way.
I am writing javascript for quite a long time and using these methods day to day confidently but I can not wrap my head around this.
Maybe I am missing some very fundamental detail.
Could somebody give an explanation?
the first argument of bind should be an object (as context)
Yes.
and not a function (Array.prototype.slice).
Why not? For one, all functions are objects, so nothing wrong here.
From another perspective, if you use slice.call(…) or slice.apply(…) then the slice object is the context (receiver) of the call/apply method invocations.
What is the difference between binding apply and call?
There is no difference between applyBoundToSlice(arguments) and callBoundToSlice(arguments). The difference is applyBoundToSlice(arguments, [0, n]) vs callBoundToSlice(arguments, 0, n) if you want pass start and end arguments to the slice.
One can use call keyword to implement method borrowing. Below example shows method borrowing.
var john = {
name: "John",
age: 30,
isAdult: function() {
console.log(this.name+"'s age is "+this.age);
if (this.age > 18) {
return true;
} else {
return false;
}
}
}
console.log(john.isAdult());
var rita = {
name: "Rita",
age: 15
}
console.log(john.isAdult.call(rita));
Observe the last line. How the keyword call is used and how rita object is passed to the function.
I'm trying to define a "dot function" where there are no parameters but has a . and a string or number before it like these:
.toUpperCase()
.toLowerCase()
.indexOf()
.charAt()
.substring()
You do 2..toString, not toString(2).
How do you define one of them?
Defining a "dot function" is easy. Here's how you can define it on a single object.
var a = {}, or a = function() {}, or a = [], etc.
a.dotFunction = function() { return 'hi'; }
console.log(a.dotFunction());
If you want to define it on all instances of a "class", use prototype.
function someClass() {
}
someClass.prototype.dotFunction = function() { return 'hi'; };
console.log(new someClass().dotFunction());
You can even do this on built-in types (some, like Prototype.js, do this, though most recommended against it).
Number.prototype.dotFunction = function() { return 'hi'; };
console.log((0).dotFunction());
I'd strongly recommend not trying to replace any built-in methods, however, you're free to define your own methods however you like.
You can do this by attaching the method to the Number or String type's prototype:
Number.prototype.foo = function(n) { return this * n; };
String.prototype.bar = function(n) { return this.length * n; };
alert(4..foo(2)); // 8
alert("4".bar(2)); // 2
Further Reading
Inheritance and the prototype chain
I'll give it a shot because nobody mentioned that you can already do this without having to define anything yourself.
A thing to take care of is if you have a number you have to place 2 dots after it where as if you have a function that returns a number or a variable that holds one you don't:
1..toString()
.indexOf("1")//<=returns number 0
//even though a number is returned we only need one dot here
.toString();//<="0"
var num = 1234;
num.toString()//<=one dot
.indexOf("23");//<=1
Your example would already work but since indexOf would return a number if you give it an argument that makes sense and a number doesn't have a charAt method.
"hello".toUpperCase()
.toLowerCase()
.indexOf("h")//<=returns a number
//number has no charAt method
.toString()
.charAt(0)
.substring(0);//<="0"
I'm a long-time browser but a first time participator. If I'm missing any etiquette details, please just let me know!
Also, I've searched high and low, including this site, but I haven't found a clear and succinct explanation of exactly what I'm looking to do. If I just missed it, please point me in the right direction!
Alright, I want to extend some native JavaScript objects, such as Array and String. However, I do not want to actually extend them, but create new objects that inherit from them, then modify those.
For Array, this works:
var myArray = function (n){
this.push(n);
this.a = function (){
alert(this[0]);
};
}
myArray.prototype = Array.prototype;
var x = new myArray("foo");
x.a();
However, for String, the same doesn't work:
var myString = function (n){
this = n;
this.a = function (){
alert(this);
};
}
myString.prototype = String.prototype;
var x = new myString("foo");
x.a();
I've also tried:
myString.prototype = new String();
Now, in trying to research this, I've found that this does work:
var myString = function (n){
var s = new String(n);
s.a = function (){
alert(this);
};
return s;
}
var x = myString("foo");
x.a();
However, this almost feels like 'cheating' to me. Like, I should be using the "real" inheritance model, and not this shortcut.
So, my questions:
1) Can you tell me what I'm doing wrong as regards inheriting from String? (Preferably with a working example...)
2) Between the "real" inheritance example and the "shortcut" example, can you name any clear benefits or detriments to one way over the other? Or perhaps just some differences in how one would operate over the other functionally? (Because they look ultimately the same to me...)
Thanks All!
EDIT:
Thank you to everyone who commented/answered. I think #CMS's information is the best because:
1) He answered my String inheritance issue by pointing out that by partially redefining a String in my own string object I could make it work. (e.g. overriding toString and toValue)
2) That creating a new object that inherits from Array has limitations of its own that weren't immediately visible and can't be worked around, even by partially redefining Array.
From the above 2 things, I conclude that JavaScript's claim of inheritablity extends only to objects you create yourself, and that when it comes to native objects the whole model breaks down. (Which is probably why 90% of the examples you find are Pet->Dog or Human->Student, and not String->SuperString). Which could be explained by #chjj's answer that these objects are really meant to be primitive values, even though everything in JS seems to be an object, and should therefore be 100% inheritable.
If that conclusion is totally off, please correct me. And if it's accurate, then I'm sure this isn't news to anyone but myself - but thank you all again for commenting. I suppose I now have a choice to make:
Either go forward with parasitic inheritance (my second example that I now know the name for) and try to reduce its memory-usage impact if possible, or do something like #davin, #Jeff or #chjj suggested and either psudo-redefine or totally redefine these objects for myself (which seems a waste).
#CMS - compile your information into an answer and I'll choose it.
The painfully simple but flawed way of doing this would be:
var MyString = function() {};
MyString.prototype = new String();
What you're asking for is strange though because normally in JS, you aren't treating them as string objects, you're treating them as "string" types, as primitive values. Also, strings are not mutable at all. You can have any object act as though it were a string by specifying a .toString method:
var obj = {};
obj.toString = function() {
return this.value;
};
obj.value = 'hello';
console.log(obj + ' world!');
But obviously it wouldn't have any string methods. You can do inheritence a few ways. One of them is the "original" method javascript was supposed to use, and which you and I posted above, or:
var MyString = function() {};
var fn = function() {};
fn.prototype = String.prototype;
MyString.prototype = new fn();
This allows adding to a prototype chain without invoking a constructor.
The ES5 way would be:
MyString.prototype = Object.create(String.prototype, {
constructor: { value: MyString }
});
The non-standard, but most convenient way is:
MyString.prototype.__proto__ = String.prototype;
So, finally, what you could do is this:
var MyString = function(str) {
this._value = str;
};
// non-standard, this is just an example
MyString.prototype.__proto__ = String.prototype;
MyString.prototype.toString = function() {
return this._value;
};
The inherited string methods might work using that method, I'm not sure. I think they might because there's a toString method. It depends on how they're implemented internally by whatever particular JS engine. But they might not. You would have to simply define your own. Once again, what you're asking for is very strange.
You could also try invoking the parent constructor directly:
var MyString = function(str) {
String.call(this, str);
};
MyString.prototype.__proto__ = String.prototype;
But this is also slightly sketchy.
Whatever you're trying to do with this probably isn't worth it. I'm betting there's a better way of going about whatever you're trying to use this for.
If you want an absolutely reliable way of doing it:
// warning, not backwardly compatible with non-ES5 engines
var MyString = function(str) {
this._value = str;
};
Object.getOwnPropertyNames(String.prototype).forEach(function(key) {
var func = String.prototype[key];
MyString.prototype[key] = function() {
return func.apply(this._value, arguments);
};
});
That will curry on this._value to every String method. It will be interesting because your string will be mutable, unlike real javascript strings.
You could do this:
return this._value = func.apply(this._value, arguments);
Which would add an interesting dynamic. If you want it to return one of your strings instead of a native string:
return new MyString(func.apply(this._value, arguments));
Or simply:
this._value = func.apply(this._value, arguments);
return this;
There's a few ways to tackle it depending on the behavior you want.
Also, your string wont have length or indexes like javascript strings do, a way do solve this would be to put in the constructor:
var MyString = function(str) {
this._value = str;
this.length = str.length;
// very rough to instantiate
for (var i = 0, l = str.length; i < l; i++) {
this[i] = str[i];
}
};
Very hacky. Depending on implementation, you might just be able to invoke the constructor there to add indexes and length. You could also use a getter for the length if you want to use ES5.
Once again though, what you want to do here is not ideal by any means. It will be slow and unnecessary.
This line is not valid:
this = n;
this is not a valid lvalue. Meaning, you cannot assign to the value referenced by this. Ever. It's just not valid javascript. Your example will work if you do:
var myString = function (n){
this.prop = n;
this.a = function (){
alert(this.prop);
};
}
myString.prototype = new String; // or String.prototype;
var x = new myString("foo");
x.a();
Regarding your workaround, you should realise that all you're doing is making a String object, augmenting a function property, and then calling it. There is no inheritance taking place.
For example, if you execute x instanceof myString in my example above it evaluates to true, but in your example it isn't, because the function myString isn't a type, it's just a regular function.
You can't assign the this in a constructor
this=n is an error
Your myarray is just an alias for the native Array- any changes you make to myarray.prototype are changes to Array.prototype.
I would look into creating a new object, using the native object as a backing field and manually recreating the functions for the native objects. For some very rough, untested sample code...
var myString = {
_value = ''
,substring:_value.substring
,indexOf:_value.indexOf
}
Now, I'm sure this wont work as intended. But I think with some tweaking, it could resemble on object inherited from String.
The ECMAScript6 standard allows to inherit directly from the constructor functions of a native object.
class ExtendedNumber extends Number
{
constructor(value)
{
super(value);
}
add(argument)
{
return this + argument;
}
}
var number = new ExtendedNumber(2);
console.log(number instanceof Number); // true
console.log(number + 1); // 4
console.log(number.add(3)); // 5
console.log(Number(1)); // 1
In ECMAScript5 it is possible to inherit like this:
function ExtendedNumber(value)
{
if(!(this instanceof arguments.callee)) { return Number(value); }
var self = new Number(value);
self.add = function(argument)
{
return self + argument;
};
return self;
}
However, the generated objects are not primitive:
console.log(number); // ExtendedNumber {[[PrimitiveValue]]: 2}
But you can extend a primitiv type by extending his actual prototype that is used:
var str = "some text";
var proto = Object.getPrototypeOf(str);
proto.replaceAll = function(substring, newstring)
{
return this.replace(new RegExp(substring, 'g'), newstring);
}
console.log(str.replaceAll('e', 'E')); // somE tExt
A cast to a class-oriented notation is a bit tricky:
function ExtendedString(value)
{
if(this instanceof arguments.callee) { throw new Error("Calling " + arguments.callee.name + " as a constructor function is not allowed."); }
if(this.constructor != String) { value = value == undefined || value == null || value.constructor != String ? "" : value; arguments.callee.bind(Object.getPrototypeOf(value))(); return value; }
this.replaceAll = function(substring, newstring)
{
return this.replace(new RegExp(substring, 'g'), newstring);
}
}
console.log(!!ExtendedString("str").replaceAll); // true
this.String = {
Get : function (val) {
return function() {
return val;
}
}
};
What is the ':' doing?
this.String = {} specifies an object. Get is a property of that object. In javascript, object properties and their values are separated by a colon ':'.
So, per the example, you would call the function like this
this.String.Get('some string');
More examples:
var foo = {
bar : 'foobar',
other : {
a : 'wowza'
}
}
alert(foo.bar); //alerts 'foobar'
alert(foo.other.a) //alerts 'wowza'
Others have already explained what this code does. It creates an object (called this.String) that contains a single function (called Get). I'd like to explain when you could use this function.
This function can be useful in cases where you need a higher order function (that is a function that expects another function as its argument).
Say you have a function that does something to each element of an Array, lets call it map. You could use this function like so:
function inc (x)
{
return x + 1;
}
var arr = [1, 2, 3];
var newArr = arr.map(inc);
What the map function will do, is create a new array containing the values [2, 3, 4]. It will do this by calling the function inc with each element of the array.
Now, if you use this method a lot, you might continuously be calling map with all sorts of arguments:
arr.map(inc); // to increase each element
arr.map(even); // to create a list of booleans (even or odd)
arr.map(toString); // to create a list of strings
If for some reason you'd want to replace the entire array with the same string (but keeping the array of the same size), you could call it like so:
arr.map(this.String.Get("my String"));
This will create a new array of the same size as arr, but just containing the string "my String" over and over again.
Note that in some languages, this function is predefined and called const or constant (since it will always return the same value, each time you call it, no matter what its arguments are).
Now, if you think that this example isn't very useful, I would agree with you. But there are cases, when programming with higher order functions, when this technique is used.
For example, it can be useful if you have a tree you want to 'clear' of its values but keep the structure of the tree. You could do tree.map(this.String.Get("default value")) and get a whole new tree is created that has the exact same shape as the original, but none of its values.
It assigns an object that has a property "Get" to this.String. "Get" is assigned an anonymous function, which will return a function that just returns the argument that was given to the first returning function. Sounds strange, but here is how it can be used:
var ten = this.String["Get"](10)();
ten will then contain a 10. Instead, you could have written the equivalent
var ten = this.String.Get(10)();
// saving the returned function can have more use:
var generatingFunction = this.String.Get("something");
alert(generatingFunction()); // displays "something"
That is, : just assigns some value to a property.
This answer may be a bit superflous since Tom's is a good answer but just to boil it down and be complete:-
this.String = {};
Adds an object to the current object with the property name of String.
var fn = function(val) {
return function() { return(val); }
}
Returns a function from a closure which in turn returns the parameter used in creating the closure. Hence:-
var fnInner = fn("Hello World!");
alert(fnInner()); // Displays Hello World!
In combination then:-
this.String = { Get: function(val) {
return function() { return(val); }
}
Adds an object to the current object with the property name of String that has a method called Get that returns a function from a closure which in turn returns the parameter used in creating the closure.
var fnInner = this.String.Get("Yasso!");
alert(fnInner()); //displays Yasso!