Code A, this is OK and logs [ 1, 2, 3 ]
function fn() {
console.log(...arguments);
}
fn([1, 2, 3]);
Code B, this fails with SyntaxError: Unexpected token ...
Also, wrapping ...arguments in () won't help either.
function fn() {
var a = ...arguments;
}
fn([1, 2, 3]);
In both cases, I assume, we have a RHS lookup. Why is it OK to pass on ...arguments to console.log (or other function), but not to assign it to a variable?
PS. I know that this would never stand in real life code or there are other, better solutions to this. My question is purely theoretical/conceptual and limited to understanding why the different behavior occurs.
The two applications of the spread syntax are:
turn items of an iterable value into arguments of a function call
turn items of an iterable into elements of an Array
So this works:
function fn() {
var a = [...arguments];
}
It doesn't work as in CODE B... Invalid syntax
You can spread in fn params like
function fn(...params)
{
...
}
or Array and Object
var a = [1,2,3,4,5],
b = [6,...a];
var x = {a:1,b:1},
y = {c:1,...x}
Related
I am new about programming. I have some difficulties to undurstand function.
So I done practice. But, I don't know why use return.
My code work.
In this exemple :
the function name is reverse.
I return in my function an array [1, 2, 5, 8].
Console.log a new array named reversed.
function reverse(array) {
const reversed = [];
for (i = array.length - 1; i > -1; i--) {
reversed.push(array[i]);
}
console.log(reversed);
}
return reverse([1, 2, 5, 8]);
**
But why after validate exercice, it's say me false and send me this solution :**
function reverse(array) {
const reversed = [];
for (let i = array.length - 1; i > -1; i--) {
reversed.push(array[i]);
}
return reversed;
}
Thanks : )
I googling and use chat GPT to teach me why use return in my case. But, nothing about I can understand.. I need your help.
Thank all.
The variable reversed is defined inside the function body so it can only be accessed from inside of the function. In your code you tried to access the variable from outside of the function. return keyword is used inside the function to return a value, using return outside the function has no effect.
Some remarks:
The names reverse and reversed are very similar which can lead to confusion. I see it happening in another answer and in your comments. reverse is defined as a function, and reversed is a local variable that references an array. They are completely different objects.
In your code you have placed a return statement where it is not valid. A return statement can only occur inside a function body.
console.log(reversed) will output the result in the console window, but that is not what the purpose is of the exercise. Although console.log is very useful while you are debugging your code, it is not equivalent to a return value. The caller of your function will get as return value what the function has returned with a return statement, not what console.log has output.
Your code does not define i with let (or var) and so it is implicitly declared as a global variable. This is not good practice. Always define variables explicitly.
Your code calls the reverse function. This is what you typically would do to test your function, but in code challenges, this call is typically made by the person or framework that tests your implementation. And those tests will call it more than once, with different arrays as argument, often including boundary cases, like an empty array, or a very large array.
So when you debug code, you would call the function and print the value that it returns, like so:
function reverse(array) {
const reversed = [];
for (let i = array.length - 1; i > -1; i--) {
reversed.push(array[i]);
}
return reversed;
}
let result = reverse([1,2,3,4]);
console.log(result); // Should be [4,3,2,1]
result = reverse([1]);
console.log(result); // Should be [1]
result = reverse([]);
console.log(result); // Should be []
result = reverse(reverse([1,2,3,4])); // Reversing the result!
console.log(result); // Should be [1,2,3,4]
Maybe I should also mention that reverse is also a native method available for arrays. It will reverse the array itself instead of creating a new array (like above). If the purpose is to always create a new array, you can still make use of it: first create a copy of the given array, reverse the copy and return that:
function reverse(array) {
// The spread syntax creates a copy, and reverse is applied on the copy
return [...array].reverse();
}
let result = reverse([1,2,3,4]);
console.log(result); // Should be [4,3,2,1]
result = reverse([1]);
console.log(result); // Should be [1]
result = reverse([]);
console.log(result); // Should be []
result = reverse(reverse([1,2,3,4])); // Reversing the result!
console.log(result); // Should be [1,2,3,4]
TypeScript function chaining, but I want to programmatically chain them.
Example class: chain.ts
class MyChain {
value: number = 0;
constructor() {
this.value = 0;
}
sum(args: number[]) {
this.value = args.reduce((s, c) => s + c, 0);
return this;
}
add(v: number) {
this.value = this.value + v;
return this;
}
subtract(v: number) {
this.value = this.value - v;
return this;
}
}
const mc = new MyChain();
console.log(mc.sum([1, 2, 3, 4]).subtract(5).value);
I see the number 5 on the console.
Now, I'm still fairly new to JavaScript and TypeScript, so I figured out that the function within this class is actually an element of an array of the instance of the class. Hence, I can do this:
console.log(mc["sum"]([1, 2, 3, 4]).value);
This indeed returns the number 10.
Now, I'm confused as to how I'd chain this programmatically. For example (this is obviously not what I would want to do anyway and shows my boneheaded lack of understanding of JavaScript:
console.log(mc["sum"]([1, 2, 3, 4]).mc["subtract"](5).value);
Error:
Property 'mc' does not exist on type 'MyChain'.ts(2339)
Okay, in all honesty, I kind of intuitively knew that wasn't going to work. However, thinking about it, how would I go about accessing the elements of a multidimensional array in just about any reasonable language?
console.log(mc["sum"]([1, 2, 3, 4])["subtract"](5).value);
Bingo. This does the trick. But, this isn't really the solution I need. What I need is something like this:
interface IChainObject {
action: string;
operand: number | number[];
}
const chainObj: IChainObject[] = [
{ action: "sum", operand: [1, 2, 3, 4] },
{ action: "subtract", operand: 5 },
];
And, to start, I'd like to try this:
console.log(mc[chainObj[0].action](chainObj[0].operand).value);
And consequently, generating a mechanism that would ultimately build something like this:
console.log(
mc[chainObj[0].action](chainObj[0].operand)[chainObj[1].action](
chainObj[1].operand
).value
);
Hence, it seems to me that what I want is some way to generate this:
[chainObj[0].action](chainObj[0].operand)[chainObj[1].action](chainObj[1].operand)
from this, with my chain object having one or many action/operand object sets:
const chainObj: IChainObject[] = [
{ action: "sum", operand: [1, 2, 3, 4] },
{ action: "subtract", operand: 5 },
];
Now, this is where my brain more or less shuts down. I am thinking that I need to generate a chain of string values, but they'll just be strings and won't really work as array indexes into the function as I want.
Why do I want to do this? Ultimately, I want to build a complex Yup schema object from a JSON object. I found this excellent post, but my core issue is I don't really understand how this code works.
At this point, I am able to parse out the way Vijay was able to solve his issue and mimic it, in a way. Here's working code for my example:
const mc = new MyChain();
interface IChainObject {
action: string;
operand: number | number[];
}
const chainObj: IChainObject[] = [
{ action: "sum", operand: [1, 2, 3, 4, 5] },
{ action: "subtract", operand: 5 },
];
let myChain = {};
chainObj.forEach((o) => {
myChain = mc[o.action](o.operand);
});
console.log("myChain is", myChain["value"]);
Results in: myChain is 10
You're probably asking yourself, "What's your problem Dan?. You seem to have a solution in hand now." Yes, I guess I do, but I don't understand it. I'm basically copying and pasting code, marginally understanding it, and making changes that make it work.
My basic issue is I don't understand how this line of code works: myChain = mc[o.action](o.operand);
I get the general gist that it's calling the function based on the action and providing the data to the function via the operand. I'm a copy and paste code monkey. I want to be more than a monkey. Maybe a baboon or even ape. Hence, I want to understand what I've done. What doesn't make sense to me is how it's chaining it.
I thought maybe the secret was in the forEach function, but that doesn't seem to be it. Here is a simple test:
let p = 0;
const x = [1, 2, 3, 4];
x.forEach((y) => {
p = y;
});
console.log("p is", p); p is 4
What is the secret JavaScript magic that is happening under the hood that makes the myChain = mc[o.action](o.operand); code actually chain my functions together rather than simply work one and the work the other. I'm just not seeing it.
Let's start from the first misunderstanding I can find:
Now, I'm still fairly new to JavaScript and TypeScript, so I figured out that the function within this class is actually an element of an array of the instance of the class.
This is not the case. Square brackets in Javascript are used for all property lookups, not just array indexing. x.foo is actually equivalent to x["foo"], and the same syntax works for arrays since arrays are just objects. Classes in Javascript are just objects that have a prototype property, which is itself an object. It contains a list of default attributes, and if you instantiate a class and look up a property that isn't in the object, it'll search for it in the prototype. So, looking at the code:
mc["sum"]([1, 2, 3])
It searches for a "sum" property in mc, and can't find any since you haven't defined one, so it searches in the prototype of MyChain, and finds the mc method. Thus, mc["sum"] is the sum method of mc. Now, this code:
console.log(mc["sum"]([1, 2, 3, 4]).mc["subtract"](5).value);
doesn't work, and it looks very off for a reason. mc["sum"]([1, 2, 3, 4]) returns mc, so why would you have to access the mc property (not that the mc property even exists)? That's why your second example, the one that calls subtract directly, works:
console.log(mc["sum"]([1, 2, 3, 4])["subtract"](5).value);
Now, let's look at the working code:
const mc = new MyChain();
interface IChainObject {
action: string;
operand: number | number[];
}
const chainObj: IChainObject[] = [
{ action: "sum", operand: [1, 2, 3, 4, 5] },
{ action: "subtract", operand: 5 },
];
let myChain = {};
chainObj.forEach((o) => {
myChain = mc[o.action](o.operand);
});
console.log("myChain is", myChain["value"]);
You actually don't need a lot of this code. It can be simplified down to:
const mc = new MyChain();
interface IChainObject {
action: keyof MyChain;
operand: number | number[];
}
const chainObj: IChainObject[] = [
{ action: "sum", operand: [1, 2, 3, 4, 5] },
{ action: "subtract", operand: 5 },
];
chainObj.forEach((o) => {
// bypass typescript type checking with cast
(mc[o.action] as Function)(o.operand);
});
console.log("myChain is", mc.value);
Essentially, the forEach loops through the elements in chainObj in order. The element's value is stored in the variable o. mc[o.action] takes the method name stored in o.action, and accesses it using square brackets. This is basically looking up the method. Then, the method is called with (o.operand) (in Javascript functions are just values, and you can call any value like a function, but if it's not a function it'll error). mc then modifies itself, and you move on to the next loop. If we insert a debugger statement in the function then break on the first loop, we can inspect the variables:
As you can see, the value starts off at 0, o.action is "sum", and mc[o.action] is the sum method. We can then call the sum method with o.operand, which adds the elements up and sets the value to 15. Then, in the second loop:
mc[o.action] is the subtract method, and we call it with o.operand, which is 5, lowering the value to 10.
Most things in Javascript, like classes are basically just objects.1
What that means is that attributes, or in this case - functions, can be accessed via the dot notation or bracket notation.
Lets look at an example that might assist the explanation:
class MyClass {
myFunction(x) {
console.log(x);
}
}
const x = new MyClass();
// attribute accessed via the dot notation
x.myFunction("Hello World!");
// attribute accessed via the bracket notation and a string
x['myFunction']("Hello World, again!");
// attribute accessed via a variable that is a string
const functionName = 'myFunction';
x[functionName]("Well uh, Hello World again?");
// attribute accessed via a variable that is a string, and passing in an argument
const argument = "This is " + "an argument";
x[functionName](argument);
To illustrate the point further:
class MyClass {
myFunction(x) {
console.log(x);
}
}
const x = new MyClass();
console.log(x.myFunction) // returns a function
console.log(x["myFunction"]) // returns a function
// executing the function
x.myFunction("Method One");
x["myFunction"]("Method Two")
We can see that the returned function can be called.
So let's get back to your example
chainObj.forEach((o) => {
myChain = mc[o.action](o.operand);
});
o.action is the function name
o.operand is the argument
Therefore, what is roughly translates to is:
chainObj.forEach((o) => {
myChain = mc[functionName](arugment);
});
just like our previous examples.
1 "classes are basically just objects"
There are so many pieces of this; I'm just going to focus on "what's the secret that makes the forEach() code work?"
The "secret" is that instances of MyChain have a property named value that gets updated after each method is called. The code with forEach() is not really chaining calls together; it just operates on the original MyChain variable named mc each time.
Since all the methods of MyChain that update this.value also return this, it happens not to matter whether you really chain calls (operate on the return value of each method call):
const chaining = new MyChain();
console.log(chaining.add(3).subtract(1).value); // 2
or if you just call methods on the original object in succession:
const notChaining = new MyChain();
notChaining.add(3);
notChaining.subtract(1);
console.log(notChaining.value) // 2
If you want there to be a difference between those, you can show it by making two versions of MyChain; one that only works via chaining, and one that only works in succession.
The following requires chaining because it never updates the original object and method calls return new objects with the results of the method call:
class RealChain {
constructor(public value: number = 0) { }
sum(args: number[]) {
return new RealChain(args.reduce((s, c) => s + c, 0));
}
add(v: number) {
return new RealChain(this.value + v);
}
subtract(v: number) {
return new RealChain(this.value - v);
}
}
const realChaining = new RealChain();
console.log(realChaining.add(3).subtract(1).value); // 2
const notRealChaining = new RealChain();
notRealChaining.add(3);
notRealChaining.subtract(1);
console.log(notRealChaining.value) // 0
and the following prohibits chaining, because it only updates the original object and its methods don't return anything:
class NotChain {
value: number = 0;
constructor() {
this.value = 0;
}
sum(args: number[]) {
this.value = args.reduce((s, c) => s + c, 0);
}
add(v: number) {
this.value = this.value + v;
}
subtract(v: number) {
this.value = this.value - v;
}
}
const realNotChaining = new NotChain();
realNotChaining.add(3);
realNotChaining.subtract(1);
console.log(realNotChaining.value) // 2
const badNotChaining = new NotChain();
console.log(badNotChaining.add(3).subtract(1).value); // error!
// badNotChaining.add(3) is undefined so you can't call subtract() on it
The code with forEach() would only work with NotChain instances and not with RealChain instances.
If you want a programmatic loop-like thing that actually works with chaining and not calling methods on an original object, you should probably use reduce() instead of forEach():
const realChainReduced = chainObj.reduce(
(mc, o) => mc[o.action](o.operand),
new RealChain() // or MyChain, doesn't matter
);
console.log("realChainReduced is", realChainReduced.value); // 10
Note that I didn't cover any of the other parts, including TypeScript specifics (the typings used here give some compiler errors), so be warned.
Playground link to code
I have a a function that looks like this:
function strip(o) {
for (var i in o) {
var test = basic.arr.some(function(x) {return x === i} );
//JSlint -- don't make functions within loop
if (test)
delete o[i];
}
return o;
}
the code works and JSlint complains.
I understand that defining multiple functions with a loop' s repeating expression will end up in creating multiple functions each retaining the last repeating expression's value (because each loop doesn't have a distinct scope);
but in this case , since I'm evaluating the function right away, I can't think of a scenario where the actual value of i would change;
I've already resolved using a forEach method but would like to understand why this can eventually lead to problems.
fiddle
By enclosing that anonymous function declaration in for (var i in o) {, you're redefining it each time you iterate, not just once, regardless of whether or not it's "invoked immediately" -- and I'd point out your oversight of that redefinition as the reason you want to remove that construct from your code.
So you want to either get rid of for or you need to define the function earlier. I'm going to take "more" of JSLint's advice and get rid of the for as well.
This lints on JSLint.com as is:
/*jslint white:true, devel:true */
var obj = {
a: true,
b: true,
c: true
};
var basic ={
arr: ['a', 'b']
};
function strip(o) {
"use strict";
var test;
Object.keys(o).forEach(function (i) {
test = basic.arr.some(function (x) { return x === i; });
if (test) {
delete o[i];
}
});
return o;
}
strip(obj);
console.log(obj);
Consider the following two ways of creating the instance of an object:
function f() { return { k: 'v'} }
var inst1 = f();
function F() { this.k = 'v'; }
var inst2 = new F();
The behavior of inst1 and inst2 is the same, the only difference is that a different constructor is saved to the object:
inst1.constructor; // Object()
inst2.constructor; // F()
What's the constructor of an array? See:
var xs = [];
xs.constructor; // Array()
Until this Point I understand the logic. But I bumped into the following:
var tags = document.getElementsByTagName("*");
typeof tags; // object
tags.constructor; // HTMLCollection()
So far it's similar to the inst2 example. But when I console.log with Firebug, I receive something like an Array with a 'named' constructor:
console.log(tags); // HTMLCollection[tag1, tag2, ...]
The block brackets confuse me, I would have expected curly one here. There must be an explanation to this, anybody knows the answer?
It sounds like the crux of your question lies in determining how Firebug displays your object. Firebug examines objects you passed to it, and chooses how it will display objects as a string based on what properties they have.
HTMLCollection is "array-like" so it has a length property, and stores its contents in properties named 0, 1, 2, etc. This makes it array-like and Firebug recognizes this, outputting the string representation of your object like it would an array.
In the case of Firebug, if it sees a length and a splice property, it will treat the object as array-like:
var MyArrayLike = function(){
this[0] = 1;
this.length = 1;
this.splice = function(){};
}
Firebug output:
-> new MyArrayLike();
<- [1]
I am reading the book "JavaScript-The Good Parts", in chapter 4.14 Curry, the book gives the following example:
Function.method('curry', function(){
var slice = Array.prototype.slice,
args = slice.apply(arguments), //1st-arguments
that=this;
return function(){
return that.apply(null, args.concat(slice.apply(arguments))); //2nd-arguments
}
})
var add1=add.curry(1);
document.writeln(add1(6)); // 7
I have two questions on this code:
There are two places using 'arguments'. In the method invoking in the last two lines code, is it so that '1' goes to the 1st-arguments and '6' goes to the 2nd-arguments?
There is a line of code apply(null, args.concat(slice.apply(arguments))), why does it apply(null,...) here?, what is the sense to apply a argument to a null object?
There are two places using 'arguments'. In the method invoking in the last two lines code, is it so that '1' goes to the 1st-arguments and '6' goes to the 2nd-arguments?
Yes, 1 is part of arguments of the outer function, while 6 goes the arguments in the inner function. The inner function can capture all other variables in a closure except for arguments and this, which have a special meaning inside a function, hence are not part of that closure. So that, and args are captured, but not arguments.
There is a line of code apply(null, args.concat(slice.apply(arguments))), why does it apply(null,...) here?, what is the sense to apply a argument to a null object?
Invoking a method with a null context will simply set the this value to the global object. In the case it seems the author does not care what that context is.
That example was kind of tricky for me too, because I'm not experienced in JavaScript, so I will try to explain it as I would have wished to be explained to me. Due to that reason it might be too explicit!
Also, before you start, I would recommend you to review the 'closure' part (for me to understand closure, was very helpful this quote):
"A closure is a special kind of object that combines two things: a function, and the environment in which that function was created. The environment consists of any local variables that were in-scope at the time that the closure was created." (from Mozilla developer network) - Closures: Line by Line explanation of "Javascript: Good Parts" example?
And now about the curry example:
apply(this, arguments) - this method can be applied to a function to call it and sends the 'this' variable and the parameters variables as an array (arguments).
For example:
// JSON objects
var ob1 = { // first object
a : 0,
b : 0
};
var ob2 = { // the second one
a : 0,
b : 0
};
// a function that updates 'a' and 'b'
function update(newA, newB) {
this.a = newA;
this.b = newB;
}
// 'this' is equal with ob1 and arguments array is [10, 20]
update.apply(ob1, [10, 20]);
// the same as above, but 'this' is here ob2
update.apply(ob2, [30, 40]);
/* after update:
ob1 {
a: 10,
b: 20
}
ob2 {
a: 30,
b: 40
}
*/
Now, that you have understood apply, you might ask why is in this example is called only with a single parameter.
args = slice.apply(arguments);
The function 'slice' takes an array and some parameters that represent indexes and forms a new array only with those.For example:
var anArray = ['red', 'green', 'black'];
var otherArray = anArray.slice(0); // after this line 'otherArray' will be ['red']
otherArray = anArray.slice(1,2); // 'otherArray' will be ['green','black']
// but what happens id you don't specify any indexes?
otherArray = anArray.slice();
// otherArray will be equal with ['red', 'green', 'black'], the same as 'anArray'
// it just makes a copy of it
So, when it does:
args = slice.apply(arguments)
is the same as:
args = slice.apply(/*this*/ arguments, /*arguments*/ undefined);
which is it 'almost' like saying:
args = arguments.slice(); // args will have all the elements the same as arguments
The reason why it does that is because args will be an array with all the methods of an array, but 'arguments' doesn't have any methods.
"Because of a design error, arguments is not really an array. It is an array-like object. Arguments has a length property, but it lacks all of the array methods." (from the book)
Let's recap, we need it these two lines:
var slice = Array.prototype.slice;
args = slice.apply(arguments);
just to make 'arguments' to have all the methods that are specific to the an array.
In our case we will need the 'concat' method that takes two array and joins them.
Function.method('curry', function(){
var slice = Array.prototype.slice;
args = slice.apply(arguments);
that=this; // saves this (which will be a function)
return function(){
// 'that' will be the same as this from above
// and is a function that we call it with the apply
return that.apply(/*this*/null, /*arguments*/ args.concat(slice.apply(arguments)));
// 'this' was null, an arguments was a join between 'args' and 'arguments'
}
});
Finally, you just have to understand how will be used:
function add(a, b) {
return a + b;
}
var add1 = add.curry(1); // add is function, and 'add.curry(1)' returns a function
// that will have 'a' set as 1(one) and 'b' as undefined(not being set)
var sum = add1(6); // 7
// when you call add1(6), you actually call that function that was returned by
// the line add.curry(1), with 'b' equal with 6