Access Object inside property call()? - javascript

Having a strange issue - for some reason, accessing the parent object inside a property using the call() method only works when it is returned inside a function.
Why am I not able to access MyObject in the second example, but I can in the first?
I need to get the second working as I don't want to constantly be calling functions inside loops as it's slow and looks bad.
What I have right now:
var MyObject = {
"selectorArray": ['[id*="example"]','[class*="example"]'],
"all": function() {
return Array.prototype.slice.call(document.querySelectorAll(MyObject.selectorArray.join()));
},
"somemethod": function () {
for (var i = 0; i < MyObject.all().length; i++) {
MyObject.all()[i] // do something etc
}
}
.. I need to use all() in loops other methods also
}
What I want (faster and better looking)
var MyObject = {
"selectorArray": ['[id*="example"]','[class*="example"]'],
"all": Array.prototype.slice.call(document.querySelectorAll(MyObject.selectorArray.join())),
"somemethod": function () {
for (var i = 0; i < MyObject.all.length; i++) {
MyObject.all[i] // do something etc
}
}
.. I need to use all in loops other methods also
}

MyObject doesn't exist while the object literal is being evaluated. You can't use MyObject.anything to define the value of MyObject.all in the literal, because you're referring to a property of an object that hasn't yet been created. Instead, you can pull the value you need out of the literal:
var selectorArray = ['[id*="example"]','[class*="example"]'];
var MyObject = {
"selectorArray": selectorArray,
"all": Array.prototype.slice.call(document.querySelectorAll(selectorArray.join())),
"somemethod": function () {
for (var i = 0; i < MyObject.all.length; i++) {
MyObject.all[i] // do something etc
}
}
...
};

Related

Referencing an object through a variable in JavaScript

I've defined the methods "checkThreshold" and "checkOtherObject" in my prototype. checkOtherObject iterates across objects listed in attribute "nextDev" and is supposed to invoke checkThreshold for each object, thus:
// Protoype
function foo(otherObject) {
// attributes
this.nextDev=otherObject; // comma-delimited list of objects
// Method that references another method
this.checkOtherObject= function() {
successorList=this.nextDev.split(",");
for (var i=0; i<successorList.length;i++) {
successorList[i]['checkThreshold']();
}
}
// Method referenced by checkOtherObject
this.checkThreshold = function () {
<Do Stuff>
}
//Instantiations
var A = new foo ("B");
var B = new foo ("");
Thus, expected behavior is that A.checkOtherObject would invoke B.checkThreshold, but when I get to that line, B.checkThreshold isn't invoked. What am I doing wrong?
The root problem: you're trying to assign value of instances in theirselves. Note: what you're doing is still wrong, otherObject is a object, but it's a String object and it's not referring to the current foo's instance (this).
this.nextDev = otherObject
Other problem is that you're calling String().checkThreshold, but not foo().checkThreshold. You can check that at your statements:
successorList = this.nextDev.split(",");
for (var i = 0; i < successorList.length; i++) {
successorList[i]['checkThreshold']();
}
As you can see you're iterating string literals. String().split returns a object with string literals back, not a list with foo()'s.
/* confirm that the successorList is a array object */
successorList instanceof Array; // true
/* confirm that the first successorList's item is a string object */
typeof successorList[0] === "string"; // true
I'm basically not sure about what's your objective. It looks like you've a extra-ordinary giant confusion between String objects and objects. Maybe you want to store nextDev out of foo(), and at nextDev you want to store a Array object containing instances of foo? Then try:
var devs = [];
function foo(string) {
devs.push(this);
}
foo.prototype = {
checkOtherObjects: function() {
var me = this;
var myIndex;
var i = 0, len = devs.length;
for (; i < len; ++i) {
if (devs[i] === me) {
myIndex = i;
break;
}
}
for (i = myIndex + 1; i < len; ++i)
devs[i].checkThresold()
}
};
To offer a bit of a better explanation - yes, you CAN use window[successorList[i]], however, that is highly, highly not recommended, and here's why:
This becomes an issue if the variables no longer in the global scope. For example, if you place the code inside of a function, including an IIFE or a document ready function, you can no longer reference your variables from the window object, unless you were to declare them with window.A, window.B, but declaring global variables like that can get so messy, especially when working with other libraries/plugins.
So...how can you work around these issues? Simple - pass the object itself as #Bergi mentioned instead of passing the string that contains the name of the variable.
Here's what the code would look like:
// Protoype
function foo(otherObject) {
this.nextDev = otherObject || [];
// Method that references another method
this.checkOtherObject = function() {
this.nextDev.forEach(function(obj, index) {
obj.checkThreshold();
});
};
// Method referenced by checkOtherObject
this.checkThreshold = function() {
// Do Stuff
console.log("Checking Threshold for: ", this);
};
}
//Instantiations
var B = new foo(null);
var A = new foo([B]); //or [B, C, D, E, ...]
A.checkOtherObject();

Storing data in an array using Javascript Prototypical inheritance

Doing some javascript prototypical inheritance, I would like to push the arguments in my Grades constructor and do the storage manipulation and push the data inside my this.students array using my storage method, and then use the values as I please within my other methods.
But the problem is that when I console log the constructor, it does what I need it to in terms of pushing the data in the this.students array but each object comes up as undefined.
This is weird because if I run the for loop inside the Grades constructor it will work perfectly. But I would like to have a separate method to do this, inside of within my Grades constructor
Any help in pointing me in the right direction would be great! Thanks!
function Grades(studentGrades) {
if(!Array.isArray(studentGrades)) return true;
this.students = [];
this.studentGrades = arguments.length;
this.numRows = 0;
this.numColumns = 0;
this.init();
}
/*
* Method to initialize functions
*/
Grades.prototype.init = function() {
this.storage();
};
/*
* Method to store a list of grades in an array object
*/
Grades.prototype.storage = function() {
for(var i=0; i < this.studentGrades; i++) {
this.students.push(this.studentGrades[i]);
}
};
/*
* Method to add grades
*/
Grades.prototype.addGrades = function(numRows, numColumns, initial) {
for(this.numRows; this.numRows < this.students.length; this.numRows++ ) {
}
};
/*
* Method to display the students average
*/
Grades.prototype.display = function() {
// body...
};
var inputGrades = new Grades( [89,78,93,78], [83,67,93,98], [93,99,73,88] );
console.log(inputGrades);
I think there are some problems with your code, especially with Grades constructor :
function Grades(studentGrades) {
if(!Array.isArray(studentGrades)) return true;
this.students = [];
this.studentGrades = arguments.length;
this.numRows = 0;
this.numColumns = 0;
this.init();
}
You are using an array as parameter to the function but you are passing thtree parameters (arrays), I think this line:
var inputGrades = new Grades( [89,78,93,78], [83,67,93,98], [93,99,73,88] );
Should be like this:
var inputGrades = new Grades( [[89,78,93,78], [83,67,93,98], [93,99,73,88] ]);
And the following line this.studentGrades = arguments.length; is useless in the constructor and may cause problems in your code, and should be replaced with :
this.studentGrades = arguments;
Or if you pass an array of arrays like I did you can use:
this.studentGrades = studentGrades;
Your problem is inside your storage function, originating from definition.
this.studentGrades is actually defined as the length of the array, not the array itself.
If you do not store the input array or pass it on through init(inputGrades) to storage(inputGrades), then you cannot access the original input from your storage prototype.
Better: change constructor bit to:
this.students = [];
this.studentGrades = studentGrades;
And your function inside storage to:
for(var i=0; i < this.studentGrades.length; i++) {
this.students.push(this.studentGrades[i]);
}
And you should be fine I think.
UPDATE: your original function call has a variable number of arguments.
Simplest way to get to complete answer is to change argument variable to:
var inputGrades = new Grades( [[89,78,93,78], [83,67,93,98], [93,99,73,88]]);
Now you send only one argument, an array of arrays.
Alternative: change the function to
function Grades() { // so no input argument
if(!Array.isArray(studentGrades)) return true;
this.students = [];
this.studentGrades = Array.prototype.slice.call(arguments);
this.numRows = 0;
this.numColumns = 0;
And then you should be able to send in multiple arguments.

Simulating Array Functionality

Good day! I have this code:
function MyArray() {}
MyArray.prototype.length = 0;
(function() {
var methods = ['push', 'pop', 'shift', 'unshift',
'slice', 'splice', 'join'];
for (var i = 0; i < methods.length; i++) (function(name) {
MyArray.prototype[ name ] = function() {
return Array.prototype[ name ].apply(this, arguments);
};
})(methods[i]);
})();
I need explanation. I understood that "methods" is array of real methods, which just "exported" to our new class. But, what is this: MyArray.prototype.length = 0; ? Author create new prototype property and assign it zero. And later use this new property!
var mine = new MyArray();
mine.push(1, 2, 3);
assert(mine.length == 3 ...
.....
How it is work? "length" have not instantiation in code above!
Its getting initialized at zero so that if you never call any of its functions, it will return zero (like a real array) and not undefined. Also it needs to start at zero so that the methods update it correctly. in your example, length its 3 because the push method did so.
You can't really subclass Array http://perfectionkills.com/how-ecmascript-5-still-does-not-allow-to-subclass-an-array/
So if you create an instance of MyArray you can't do: MyArr[0]=...
You can wrap an array inside MyArray and take advantage of the Array functions:
var MyArray=function() {
this.arr=[];
[].push.apply(this.arr,arguments);
//following doesn't work in older browsers
Object.defineProperty(this,"length",{
get:function(){return this.arr.length;},
enumerable:true,
configurable:true
});
}
MyArray.prototype.valueOf=function(){return this.arr;};
(function() {
var methods = ['push', 'pop', 'shift', 'unshift',
'slice', 'splice', 'join'],i=methods.length
while(--i!==-1){
;(function(name) {
MyArray.prototype[ name ] = function() {
console.log(arguments);
return Array.prototype[ name ].apply(this.arr, arguments);
};
}(methods[i]));
}
}());
var mArr1=new MyArray(1,2,3);
console.log(mArr1.slice(0,1));
//you cannot do this: myArr1[0]=22;

Adding methods in an object's prototype through a loop in JavaScript

I am trying to add several methods to an object at once by using a for loop.
What I have is an array which has names of several events like click, load, etc. in an array and as such it will be really easy for me to insert these events to my library's object. However, I am not able to add the methods through the loop to my object.
Here's my code:
function(something) myLibrary {
if(this === window) {return new myLibrary }
this.e = document.getElementById(something);
}
var eventsArr = ['click','load','drag','drop'];
var addEventToProto = function (method) {
if(!myLibrary.hasOwnProperty(method)) {
myLibrary.prototype[method] = function (fn) { addEventListener(this.e, method, fn); };
}
};
for (i = 0; i < eventsArr.length; i += 1) {
addEventToProto(eventsArr[i]);
};
If you need more information then please leave a comment.
You should use a constructor function and manipulate the prototype property of that function instead. Object don't have an exposed prototype property, only functions have. When you create and instance, using a constructor function, then the internal [[prototype]] property of the resulting object will be set to point to the exposed prototype property of the constructor function. You can manipulate the prototype property even after instanciating an object:
function myLibraryConstructor() {
this.e = document.getElementById('someElement!');
}
var myLibrary = new myLibraryConstructor();
var eventsArr = ['click','load','drag','drop'];
var addEventToProto = function (method) {
if(!myLibrary.hasOwnProperty(method)) {
myLibraryConstructor.prototype[method] = function (fn) { addEventListener(this.e, method, fn); };
}
};
for (i = 0; i < eventsArr.length; i += 1) {
addEventToProto(eventsArr[i]);
};

Attempt to create a each() type jQuery function using Javascript from scratch

My goal is to replicate the normal jQuery each type function from scratch using only Javascript. Here is my code so far:
// Created a jQuery like object reference
function $(object) {
return document.querySelectorAll(object);
this.each = function() {
for (var j = 0; j < object.length; j++) {
return object[j];
}
}
}
console.log($('.dd')); // returns NodeList[li.dd, li.dd]
$('.opened').each(function() {
console.log(this);
}); // Results in an error [TypeError: $(...).each is not a function]
As you can see, each is showing as a error. How should I go about fixing this?
A lightweight class that works like that would be:
function $(selector) {
// This function is a constructor, i.e. mean to be called like x = new $(...)
// We use the standard "forgot constructor" trick to provide the same
// results even if it's called without "new"
if (!(this instanceof $)) return new $(selector);
// Assign some public properties on the object
this.selector = selector;
this.nodes = document.querySelectorAll(selector);
}
// Provide an .each function on the object's prototype (helps a lot if you are
// going to be creating lots of these objects).
$.prototype.each = function(callback) {
for(var i = 0; i < this.nodes.length; ++i) {
callback.call(this.nodes[i], i);
}
return this; // to allow chaining like jQuery does
}
// You can also define any other helper methods you want on $.prototype
You can use it like this:
$("div").each(function(index) { console.log(this); });
The pattern I 've used here is widely known (in fact jQuery itself also uses it) and will serve you well in a great many cases.
something like this...??
function $(object) {
var obj = {
arr : document.querySelectorAll(object),
each : function(fun){
for (var i = 0; i < this.arr.length; i++) {
fun.call(this, this.arr[i]);
}
}
}
return obj;
}

Categories

Resources