I am having a little trouble with my code. I understand the concept of method chaining and constructor chaining but can't get it to work right. Here is what I have so far:
function Set() {
this.values = [];
}
Set.prototype.add = function() {
for (i = 0; i < arguments.length; i++) {
this.values.push(arguments[i]);
}
}
var regSet = new Set();
regSet.add(10, 11, null);
console.log(regSet.values); // → [10, 11, null];
function NonNullSet() {
Set.apply(this, arguments);
}
NonNullSet.prototype = Object.create(Set.prototype);
NonNullSet.prototype.constructor = NonNullSet;
NonNullSet.prototype.add = function () {
for (var i = 0; i < arguments.length; i++) {
if (arguments[i] == null || arguments[i] == undefined) {
throw new Error("Can't add null or undefined");
}
}
return Set.prototype.add.apply(this, arguments);
}
var nonNull = new NonNullSet();
nonNull.add(10, 12, null);
console.log(nonNull.values); // → undefined
As you can see from the code above, nonNullSet is a subclass of Set and I am trying to augment the add method by checking for null or undefined values. If they exist, just continuing looping. If they are valid values, call the add method of the Set super class, rather than rewriting it.
To me, this looks right and I am not getting the results I want, so something isn't right. What am I doing wrong here?
I see two problems:
continue skips to the next loop iteration, but the loop doesn't do anything anyway, so continue-ing inside it doesn't change anything. Instead, you should actually call Set.add inside your loop, giving it one element at a time — but only if the element is non-null.
console.log(nonNullSet.values) should be console.log(nonNull.values): nonNullSet is your constructor, nonNull is your instance.
Also, not exactly problems, but other things you should improve:
You should capitalize NonNullSet.
Inside Set.prototype.add, you should make i a local variable, by using the var keyword.
Your variable-names contain null, but instead of checking for nullity, you check for falsiness. (null is falsy, of course, but so are some other things.) I think this is a bit confusing.
Related
Code line in question:
callbackFn ? callbackFn(currentNode) : levelOrderList.push(currentNode.value);
I am having trouble of a way to think of this in psuedo-code terms since 'callbackFn' is used like a function but not defined like a function.
I know this code works and have ran it myself. I have also solved this without using the callbackFn, but I would really like to understand why this works.
My guess for psuedo cod would be:
if callbackFn exists (not null or undefined), then return callbackFn(currentNode).
else push currentNode.value to the levelOrderList.
Full code for context:
function levelOrder(callbackFn) {
const queue = [this.root];
const levelOrderList = [];
while (queue.length > 0) {
const currentNode = queue.shift();
callbackFn ? callbackFn(currentNode) : levelOrderList.push(currentNode.value);
const enqueueList = [
currentNode?.leftChild,
currentNode?.rightChild
].filter((value) => value);
queue.push(...enqueueList);
}
if (levelOrderList.length > 0) return levelOrderList;
}
Your guess for pseudo code is correct.
The author of that could should better have used an if...else structure like your pseudo code does. The conditional operator (? :) is used here as an unnecessary short-cut. Normally you would use the conditional operator to use the value that it evaluates to, like x = condition ? a : b;. But here that value is ignored. There is really no good reason to avoid if...else here.
The author added support for a callback mechanism as an alternative to returning an array. This doesn't look like best practice to me either. For two reasons:
This "polymorphism" can be confusing for the user of such an API. It is easier to understand when the two functionalities are offered by two different functions, one that returns the result in an array, another that calls the callback. The caller will choose the function based on how they want to deal with the traversed nodes.
A callback mechanism is rather "old style". It makes more sense to turn this function into a generator function. The caller can then easily decide what to do with the nodes that the returned iterator yields: collect those nodes in an array or just process them one by one.
This is how that generator function would look like:
class Node {
constructor(value) {
this.value = value;
this.leftChild = this.rightChild = null;
}
}
class Tree {
constructor(...values) {
this.root = null;
for (let value of values) this.add(value);
}
add(value) {
function addTo(node) {
if (!node) return new Node(value);
if (value < node.value) {
node.leftChild = addTo(node.leftChild);
} else {
node.rightChild = addTo(node.rightChild);
}
return node;
}
this.root = addTo(this.root);
}
*levelOrder() {
if (!this.root) return;
const queue = [this.root];
while (queue.length > 0) {
const currentNode = queue.shift();
yield currentNode.value;
if (currentNode.leftChild) queue.push(currentNode.leftChild);
if (currentNode.rightChild) queue.push(currentNode.rightChild);
}
}
}
// Demo
const tree = new Tree(4, 6, 7, 2, 1, 5, 3);
// Several ways to use the levelOrder generator function:
console.log(...tree.levelOrder());
console.log(Array.from(tree.levelOrder()));
for (let value of tree.levelOrder()) console.log(value);
Please go through the below code.
I am not able to exactly what's calllback.call is doing.
Also, I am not able to know what is the difference between this and this[i] and how if(callback.call(this, this[i])) is evaluated to true or false.
Array.prototype.each = function(callback) {
var i = 0;
while (i < this.length) {
callback.call(this, this[i]);
i++;
}
return this;
};
Array.prototype.map = function(callback) {
var i = this.length;
var found = []
while (i--) {
if(callback.call(this, this[i])) {
found.push(this[i]);
}
}
return found;
};
The functions are called below:
Array.each(function(value){
...
})
Array.map(function(value){
...
})
I am not able to exactly what's calllback.call is doing.
Function.prototype.call lets you invoke a function with an explicit value for this
callback.call(this, this[i]);
is the same as
callback(this[i])
except that inside the callback the value of this is the set to be the same as it was in the current context.
Also am not able to know what is the difference between this and this[i].
In this context, this is the current array. So this means the whole array, this[i] gets the i'th element of the array.
The .each function you have will loop through an array and call a function for each of them. This is similar to javascript's built in Array.prototype.forEach.
The .map is named as though it were a polyfill for Array.protoype.map but is actually doing a .filter operation. Strange.
This question already has answers here:
JS checking deep object property existence [duplicate]
(2 answers)
Closed 6 years ago.
After spending many hours today searching for a specific condition functionality and testing my modified finds. I thought I share my coding snippets and knowledge here. I'm not too good at answering other questions so I thought it would do to post my finds and hope it would benefit others. Since this site has been really helpful with my projects.
Anyway. At some point when you get to a stage scripting more complicated scripts. You'll most likely bump into a problem were you'll need to check deep in objects if a certain property exists. This has occurred mostly with JSON API from my experience.
Example Issue
if (someObject && someObject.nextObject && someObject.nextObject.andAnother) {
// do something if all of this exists
}
Trying to check the deepest property, when one property in the middle doesn't exist, will throw a reference error.
ReferenceError: something is not defined
At some point these can get really long and messy to work with. Which is pretty much silly.
Trying to Tidy Like That Makes JSLint Complain & Can Be Harder for Another Scripter to Read
JSLint doesn't like when code ends up like this when scripters try to reduce the amount of characters on one line. Even I find it more difficult to read with other nested conditions together:
if (someObject
&& someObject.nextObject
&& someObject.nextObject.andAnother) {
// do something if all of this exists
}
There's multiple ways of making this less clutter. Below is an example of some different versions of snippet functions for this object.
var object = {
nextObject: {
anArray: [{
value: true
}]
}
};
Boolean Function with String Perimeter
var hasDeepProperty = function (obj, pathString) {
var i, properties = pathString.split("."), l = properties.length;
for (i = 0; i < l; i++) {
if (obj.hasOwnProperty(properties[i])) {
obj = obj[properties[i]];
} else {
return false;
}
}
return true;
};
console.log(hasDeepProperty(object, "nextObject.0.value")); // returns true
console.log(hasDeepProperty(object, "nextObject.doesNotExist.value")); // returns false
Boolean Function with Array Perimeter
var hasDeepProperty = function (obj, pathArray) {
var i, properties = pathArray, l = properties.length;
for (i = 0; i < l; i++) {
if (obj.hasOwnProperty(properties[i])) {
obj = obj[properties[i]];
} else {
return false;
}
}
return true;
};
console.log(hasDeepProperty(object, ["nextObject", 0, "value"])); // returns true
console.log(hasDeepProperty(object, ["nextObject", "doesNotExist", "value"])); // returns false
Prototype Boolean Function with String Perimeter
Object.prototype.hasDeepProperty = function (pathString) {
var i, properties = new String(pathString).split("."), l = properties.length, obj = new Object(this);
for (i = 0; i < l; i++) {
if (obj.hasOwnProperty(properties[i])) {
obj = obj[properties[i]];
} else {
return false;
}
}
return true;
};
console.log(object.hasDeepProperty("nextObject.0.value")); // returns true
console.log(object.hasDeepProperty("nextObject.doesNotExist.value")); // returns false
Prototype Boolean Function with Array Perimeter
Object.prototype.hasDeepProperty = function (pathArray) {
var i, properties = pathArray, l = properties.length, obj = new Object(this);
for (i = 0; i < l; i++) {
if (obj.hasOwnProperty(properties[i])) {
obj = obj[properties[i]];
} else {
return false;
}
}
return true;
};
console.log(object.hasDeepProperty(["nextObject", 0, "value"])); // returns true
console.log(object.hasDeepProperty(["nextObject", "doesNotExist", "value"])); // returns false
I'm aware that declaring with the new keyword that isn't a function simulated as a class is discouraged. But for some reason Prototype causes slice method not to exist for string and same for hasOwnProperty for the object.
I hope others find this useful and there be something like this to be added on the next version of JavaScript.
This my first: post my own QnA. And Happy New Year everyone.
I looked at this:
Calling a JavaScript function named in a variable
But it doesn't answer my question.
This normally works:
window['class']['sub_class']['function_name'](data);
But now I'm trying to make a general function that can handle any depth:
function callbackFunction(callback, data){
//callback = a.b.c, or a.b, or a
callback = explode(callback);
//I need to be able to call callbackFunction and somehow callback and form their proper form below
window[callback.a](data);
//or
window[callback.a][callback.b](data);
//or
window[callback.a][callback.b][callback.c](data);
}
I believe the duplicate suggested by Bergi will only solve half of your problem. Since your final value will be a function, and since that function is a member of an object, you'll end up executing it in the wrong context (i.e., with the wrong this value).
I suggest you use something like this:
function getCallback(path) {
var arr = path.split('.');
var k;
var fn = window;
while(k = arr.shift()) {
if(typeof fn[k] === "function") {
fn = fn[k].bind(fn);
} else {
fn = fn[k];
}
}
if(typeof fn === "function") return fn;
return function(){};
}
http://jsfiddle.net/7CEd5/
Compare the value of this in the callback with what you get by using the answers to Convert string in dot notation to get the object reference.
You can chain references to objects/sub-objects/etc for however long you want. If you have a point-delimited string (e.g. "document.blah.blah2.method"), then you need to split it to individual tokens (e.g. ["document", "blah", "blah2", "method"]).
Then it's simply a matter of looping through the chain:
var c = window;
for (var i = 0; i < chain.length - 1; i++) {
c = c[chain[i]];
}
c[chain[chain.length-1]](some_arguments);
I have a weird quirk in ActionScript. I need to pass the index to a callback function.
Here is my code
for (var i:Number = 0; ((i < arrayQueue.length) && uploading); i++)
{
var lid:ListItemData=ListItemData(arrayQueue[i]);
var localI:Number= new Number(i); // to copy?
var errorCallback:Function = function():void { OnUploadError(localI); };
var progressCallback:Function = function(e:ProgressEvent):void { lid.progress = e; OnUploadProgress(localI); };
var completeCallback:Function = function():void { Alert.show('callback'+localI.toString()); OnUploadComplete(localI); }; // localI == arrayQueue.length - 1 (when called)
Alert.show(localI.toString()); // shows current i as expected
lid.fileRef.addEventListener(Event.COMPLETE, completeCallback);
lid.fileRef.addEventListener(ProgressEvent.PROGRESS, progressCallback);
lid.fileRef.addEventListener(HTTPStatusEvent.HTTP_STATUS, errorCallback);
lid.fileRef.addEventListener(IOErrorEvent.IO_ERROR, errorCallback);
lid.fileRef.addEventListener(SecurityErrorEvent.SECURITY_ERROR, errorCallback);
lid.fileRef.upload(url, 'File');
}
Any idea on how to pass in the index to my callbacks? .upload does not block.
Passing additional parameters for your callbacks is possible via some kind of delegate function or closure. However it is often considered a bad practice. You may use event target property instead to determine your index based on FileReference.
Edit: here is a sample of using closures:
function getTimerClosure(ind : int) : Function {
return function(event : TimerEvent) {
trace(ind);
};
}
for (var i = 0; i < 10; i++) {
var tm : Timer = new Timer(100*i+1, 1);
tm.addEventListener(TimerEvent.TIMER, getTimerClosure(i));
tm.start();
}
This will continuously trace numbers from 0 to 9.
Edit2: here is a sample of creating a delegate based on a function closure:
function timerHandler(event : Event, ...rest) : void {
trace(event, rest);
}
function Delegate(scope : Object, func : Function, ...rest) : Function {
return function(...args) : void {
func.apply(scope, args.concat(rest));
}
}
var tm : Timer = new Timer(1000, 1);
tm.addEventListener(TimerEvent.TIMER, Delegate(this, this.timerHandler, 1, 2, 3));
tm.start();
However this is a bad approach since unsubscribing for such a listener is a hell pain. This in turn will probably cause some memory leakages, which will decrease overall performance of your application. So, use with caution!
Bottom line: if you know how to work with closures, use them - it is a wonderful thing! If you don't care about your application performance in a long perspective, use closures - it's simple!
But if you are unsure about closures, use a more conventional approach. E.g. in your case you could create a Dictionary that matches your FileReference objects to appropriate indices. Something like that:
var frToInd : Dictionary = new Dictionary(false);
// false here wouldn't prevent garbage collection of FileReference objects
for (var i : int = 0; i < 10; i++) {
// blah-blah stuff with `lib` objects
frToInd[lib.fileRef] = i;
// another weird stuff and subscription
}
function eventListener(event : Event) : void {
// in the event listener just look up target in the dictionary
if (frToInd[event.target]) {
var ind : int = frToInd[event.target];
} else {
// Shouldn't happen since all FileReferences should be in
// the Dictionary. But if this happens - it's an error.
}
}
-- Happy coding!
I have a weird quirk in ActionScript
It's not a quirk, it's variable scope. You should read this article: http://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/WS5b3ccc516d4fbf351e63e3d118a9b90204-7f9d.html#WS5b3ccc516d4fbf351e63e3d118a9b90204-7f8c
And you really shouldn't use anonymous, it just makes everything more confusing. You're actually making multiple copies of the same object.
If the arrayQueue is in scope, you can use this code to get the index:
GetArrayIndex(e.currentTarget);
function GetArrayIndex(object:Object):Number
{
for(var i:Number = 0; 0 < arrayQueue.length; i++)
{
if(object === arrayQueue[i])
return i;
}
}
You should consider using an uint for the index.