Why is 'this' different than obj.var - javascript

I am currently writing an extensible plugin for jQuery. You can see the plugin here.
My question is specific to lines 37 and 96:
// Creates a Namespace
hautD = {};
// A Basic Dropdown Constructor function
hautD.HautForms = function(el, options) {
// Overwrite default options
// with user provided ones
// and merge them into "options".
var options = $.extend({}, defaults, options);
if (el) {
this.init(el, options);
}
}
switch(options.cornerType){
default:
// default = 'rounded'
if(this.borderRadius){
alert('woo hoo');
}
break
}
(source) where I have this.borderRadius. When I set a breakpoint for this.borderRadius, it is undefined. So in the Chrome Inspector I set a watch expression for 'this' and it returns hautD.HautForms. If I set a watch inspection for hautD.HautForms.borderRadius the returned value is 'true'.
In a nutshell: why is this.borderRadius == undefined while hautD.HautForms.borderRadius == true given this == hautD.HautForms.
Sorry if this question is convoluted, this is my first 'real' plugin.

this is a special keyword that is used in methods to refer to the
object on which a method is being invoked
this is used inside the class object to access member variables.
Otherwise, you access your object from outside by public methods/variables.
In your code, you use this correctly here:
this.init(el, options);
because it is inside hautD.HautForms operation
Your code should be like this:
hautD.HautForms = function(el, options) {
// Overwrite default options
// with user provided ones
// and merge them into "options".
var options = $.extend({}, defaults, options);
if (el) {
this.init(el, options);
}
switch(options.cornerType) {
// here you can use this.borderRadius instead of hautD.HautForms.borderRadius from outside
}
}

Related

Override getter/setters created with Object.defineProperty

I am running into an issue where I am getting an error for properties that I've added to an object via Object.defineProperty.
The error in question.
Exception: RangeError: Maximum call stack size exceeded
Maybe (likely) my design is incorrect and I should be doing something differently. This is what I intend to do with the code below:
Create an object P via a factory function.
Pass a config object C to the factory to customise P.
Store C within P as a private object and get/set the values of C by attaching its properties to P via Object.defineProperty. C may be different for any given P.
The problem comes when I want to override the default get/set methods for some C.a
I do that as follows:
// Create P with a custom (non-default) get method.
let C = { a: 1, b: 2, c: 3 };
let P = factory.createObject(C);
const customGetA = function(object, property) {
return function() {
if(!object[property])
object[property] = ' ';
return object[property];
};
};
P.customgGetMethod('a', customGetA);
// Looking at object in the console reveals the error mentioned above.
let factory = (function() {
'use strict';
this.createObject = function(config) {
const product = {};
let C = config;
// Add default getters/setters to the product, referencing the properties of C.
for (const property in config) {
Object.defineProperty(product, property, {
get: function() {
return C[property];
},
set: function(value) {
C[property] = value;
},
configurable: true,
enumerable: true
});
}
product.customGetMethod = function(property, callback) {
// Get the property description.
let descriptor = Object.getOwnPropertyDescriptor(this, property);
// Assign the custom get method within the callback, binding its values via closure.
descriptor.get = callback(this, property);
// Redefine the property with the new get method.
Object.defineProperty(this, property, descriptor);
};
return product;
};
})();
In the end, I want a to be able to pass a custom data object into P and have it remain private, and dynamically generate get/set methods based off of that data so I don't have to get/set boiler plate for N-properites * M-products. This may not be the best design or implementation, but I am at a loss for how to do it another way.
Any alternatives or insight would be appreciated.
The getter function that customGetA creates in P.customgGetMethod('a', customGetA); is essentially
function() {
if(!product.a)
product.a = ' ';
return product.a;
}
When we compare that to the default getter created in the factory
function() {
return C.a;
}
we can see that the new one looks up the value in product, not the configuration C. And looking up a property in product evaluates its getter, which is the function we already are in, which recurses until it eventually overflows the stack...
I think you are looking for
// Assign the custom get method within the callback, binding its values via closure.
descriptor.get = callback(C, property);
// ^
to close over the internal configuration object.

JavaScript & Default Values on a Prototype

I am very new to JavaScript. I've done quite a bit of Google searching, but have not been able to find the answer I seek -- likely because I am using the wrong terminology.
The other day, I was reading a basic tutorial about creating JavaScript components: http://callmenick.com/post/javascript-objects-building-javascript-component-part-2
Part of the tutorial involves a set of default options, which are assigned to a type's prototype chain:
SimpleAlert.prototype.options = {
wrapper : document.body,
type : "default",
message : "Default message."
}
The idea being that the SimpleAlert type can be created with different parameters if the user provides them during construction - otherwise the default options are used.
The SimpleAlert constructor function looks like this:
function SimpleAlert( options ) {
this.options = extend( {}, this.options ); // Why?
extend( this.options, options );
// start the functionality...
}
And the extend function is defined as:
function extend( a, b ) {
for( var key in b ) {
if( b.hasOwnProperty( key ) ) {
a[key] = b[key];
}
}
return a;
}
My understanding of this code is:
Creating a new SimpleAlert object involves the user passing in their own options object. Let's say I pass in { test : "Testing" } for the options parameter. The extend function then accesses this.options (which I believe refers to the prototype's instance of options since there is no other options variable defined in the scope) and essentially copies everything from the shared instance to the empty {} object, which is then assigned to the shared this.options instance.
Then, the user supplied options (in my case { test : "Testing" } is extended int the this.options shared instance (basically copying my extra test field into the shared instance)
But in reality, it seems that the shared instance is not affected in this example -- meaning the SimpleAlert.prototype.options declaration remains unchanged so that each new instance of SimpleAlert has access to it (which is what I want)
However, in my experimentation, I find that changing this.options results in the prototype value being modified... I'm sure whatever I'm doing wrong is simple to fix, but I cannot see it. Here is an example:
var Animal = function(ovr) {
if (arguments.length > 0)
{
this.options.mode = ovr;
}
};
Animal.prototype.options = { mode: "Test" }; / I want this to be default for all instances created
var test = new Animal("Override"); // set this test obj to use "Overide"
console.log(test.options.mode); // outputs Override as expected
var again = new Animal();
console.log(again.options.mode); // also outputs Override (I want it to say "Test")
Thank you for your help!
As you've already suggested in your code, it's missing the "copy" operation so you're modifying the shared object on the prototype.
var Animal = function(ovr) {
if (arguments.length > 0) {
// Copy whatever is on this.options to a blank object,
// then assign this.options to be that newly copied object, so
// the prototype object is masked by the instance object.
this.options = Object.assign({}, this.options);
this.options.mode = ovr;
}
};
This might help to understand how the instance property "masks" the prototype:
var Foo = function() {
console.log('a', this.x);
this.x = 100;
console.log('b', this.x);
delete this.x;
console.log('c', this.x);
};
Foo.prototype.x = 1;
new Foo();

How can I create an object of fixed structure?

I have the following code inside my revealing module, but I am uncertain with how to declare/define imageListItem, which is strictly a DTO and doesn't really require any information hiding. Am I correctly defining this object?
var imageListItem = function() {
var _title;
Object.defineProperty(this, "title", {
get: function () { return _title; },
set: function (value) { _title = value; }
}
);
};
var imageList = (function () {
var buffer = new CBuffer();
return {
populate: function (listItems) {
buffer.push(listItems);
},
rotate: function() {
buffer.rotateLeft();
}
}
})();
With imageListItem, I want to declare an object structure for later use. That declaration should not logically be dependent on how that object will later be used. That is, I don't want to find myself dynamically assigning new properties to, or deleting properties from, imageListItem by accident. Any assignment to properties should strictly be only to properties that have already been declared on the object.
Object.freeze() almost accomplihses this, by preventing properties being added or removed, but it also prevents properties being changed.
E.g. I want this:
var obj = {
prop: function() {},
foo: 'bar'
};
// New properties may be added, existing properties may be changed or removed
obj.foo = 'baz';
obj.lumpy = 'woof';
var o = Object.freeze(obj);
// Now any changes will fail
function fail(){
'use strict';
obj.delete(foo); // throws a TypeError
obj.quaxxor = 'the friendly duck'; // throws a TypeError
}
I dont' want this:
// Now any changes will fail
function fail(){
'use strict';
obj.foo = 'sparky'; // throws a TypeError
}
You see? I want freeze to prevent quaxxor being added to obj, but I don't want it to prevent me changing the value of foo.
What you are looking for may be either Object.preventExtensions() or Object.seal().
Similarly to Object.freeze(), both methods prevent new properties from being added to the object, nevertheless allow changing values of existing properties.
The difference between seal and preventExtensions is that seal strictly disallows deletion and conversion of properties from/to data accessors, while preventExtensions doesn't actually prevent existing properties from being deleted: this behavior depends on the JS engine you're using (some engines may let you delete the property, other ones may not).
So basically, quoting from the MDN Documentation:
The Object.preventExtensions() method prevents new properties from ever being added to an object (i.e. prevents future extensions to the object). [...] Note that the properties of a non-extensible object, in general, may still be deleted.
The Object.seal() method seals an object, preventing new properties from being added to it and marking all existing properties as non-configurable. Values of present properties can still be changed as long as they are writable. [...] Attempting to delete or add properties to a sealed object, or to convert a data property to accessor or vice versa, will fail.
Here's an example to demonstrate the behavior of both methods:
var myFirstObj = { foo: 1 },
mySecondObj = { bar: "baz" };
Object.preventExtensions(myFirstObj);
Object.seal(mySecondObj);
myFirstObj.foo = false; // Works fine
mySecondObj.baz = "hello"; // Works fine
delete myFirstObj.foo; // May work fine depending on your JS engine
(function() {
'use strict';
myFirstObj.qux = 'something'; // Throws a TypeError
mySecondObj.qux = 'something'; // Throws a TypeError
delete mySecondObj.foo; // Throws a TypeError
})();
Now, talking about your ImageListItem Object, you can achieve what you want simply adding a line of code:
var ImageListItem = function() {
var _title;
Object.defineProperty(this, "title", {
get: function () { return _title; },
set: function (value) { _title = value; }
});
// Choose the one which fits your needs
Object.preventExtensions(this);
// or
Object.seal(this);
};

Conventional jquery plugin vs AMD jquery plugin

I still can't get my head around with this AMD jquery plugin.
// UMD dance - https://github.com/umdjs/umd
!function(root, factory) {
if (typeof define === 'function' && define.amd) {
define(['jquery'], factory);
} else {
factory(root.jQuery);
}
}(this, function($) {
'use strict';
// Default options
var defaults = {
};
// Constructor, initialise everything you need here
var Plugin = function(element, options) {
this.element = element;
this.options = options;
};
// Plugin methods and shared properties
Plugin.prototype = {
// Reset constructor - http://goo.gl/EcWdiy
constructor: Plugin,
someMethod: function(options) {
return options;
}
};
// Create the jQuery plugin
$.fn.plugin = function(options) {
// Do a deep copy of the options - http://goo.gl/gOSSrg
options = $.extend(true, {}, defaults, options);
return this.each(function() {
var $this = $(this);
// Create a new instance for each element in the matched jQuery set
// Also save the instance so it can be accessed later to use methods/properties etc
// e.g.
// var instance = $('.element').data('plugin');
// instance.someMethod();
$this.data('plugin', new Plugin($this, options));
});
};
// Expose defaults and Constructor (allowing overriding of prototype methods for example)
$.fn.plugin.defaults = defaults;
$.fn.plugin.Plugin = Plugin;
});
Some tests,
console.log($('.some-element').plugin({
test: 'option1',
test2: 'option2'
}));
I always get this empty object,
Object[]
So how can I use this empty object?
And I want to access the method inside the plugin,
var plugin = $('.element').plugin();
var instance = $('.element').data('plugin',plugin);
console.log(instance); // an empty object again!
console.log(instance.someMethod("hello world"));
TypeError: instance.someMethod is not a function
console.log(instance.someMethod("hello world"));
So how should I do to run the method inside the plugin then?
It is so different from the conventional jquery plugin. AMD's is so difficult to understand. Any idea how I can get this AMD works like the conventional!??
EDIT:
Finally I got something,
var plugin = $('.element').plugin();
var instance = $('.element').data('plugin');
console.log(instance);
console.log(instance.someMethod("hello world"));
result,
Object { element={...}, options={...}, constructor=function(), more...}
hello world
Why was it so difficult for the those who comment and answer to point this out! Sigh!
In your test with .some-element you would get an empty jQuery object if .some-element matches nothing since $.fn.plugin returns what this.each(...) returns and the jQuery.each function returns exactly the same jQuery object as what it was called on.
As to how to get the instance, let's go through each step:
var plugin = $('.element').plugin();
The line above won't create a plugin if $('.element') matches nothing. One thing for sure, the value you assign to plugin is equal to $('.element').
var instance = $('.element').data('plugin',plugin);
In context, the line above is equivalent to var instance = $('.element').data('plugin', $('.element')); And the return value of jQuery.data(key, value) is the jQuery object itself, so that here instance will be equal to $('.element').
console.log(instance); // an empty object again!
That's what you'd get if $('.element') matches nothing.
console.log(instance.someMethod("hello world"));
This is definitely not going to work.
How to access the plugin instance is detailed in a comment in your code:
// var instance = $('.element').data('plugin');
// instance.someMethod();
Of course for this to work, $('.element') has to match a non-empty set of elements.
AMD is not a factor at all in your problems. All of the issues here have to do with how to use jQuery and how to write jQuery plugins.
var plugin = $('.element').plugin();
var instance = $('.element').data('plugin');
console.log(instance);
console.log(instance.someMethod("hello world"));
result,
Object { element={...}, options={...}, constructor=function(), more...}
hello world

Is there a tidy way to create 'internal' properties in JavaScript?

In JavaScript, I may begin writing a 'library' or collection of functionality using a top level object like this:
window.Lib = (function()
{
return {
// Define Lib here.
//
};
})();
I may also add some functions within Lib which serve to create objects related to it:
window.Lib = (function()
{
return {
ObjectA: function()
{
var _a = 5;
return {
getA: function(){ return _a; }
};
},
ObjectB: function()
{
var _b = 2;
var _c = 1;
return {
getB: function(){ return _b; }
};
}
};
})();
Which would be used like so:
var thing = Lib.ObjectA();
var thing2 = Lib.ObjectA();
var thing3 = Lib.ObjectB();
And I can use the methods within each of those created above to get the values of _a defined within ObjectA() or _b defined within ObjectB():
alert(thing.getA()); // 5
alert(thing3.getB()); // 2
What I want to achieve is this:
Say I want to access the property _c (defined within ObjectB()) but only within the scope of Lib. How could I go about that? By this I mean, I want to make the property readable within any function that I define within the object returned by Lib(), but I don't want to expose those values outside of that.
Code example:
window.Lib = (function()
{
return {
ObjectA: function(){ ... },
ObjectB: function(){ ... },
assess: function(obj)
{
// Somehow get _c here.
alert( obj.getInternalC() );
}
};
})();
Which would work like so:
var thing = Lib.ObjectB();
alert( thing.getInternalC() ) // error | null | no method named .getInternalC()
Lib.assess(thing); // 1
Hope this makes sense.
So you want per-instance protected properties? That is, properties on the instances created by ObjectA, ObjectB, etc., but which are only accessible to the code within your library, and not to code outside it?
You cannot currently do that properly in JavaScript, but you'll be able to in the next version using private name objects. (See "Almost doing it" below for something similar you can do now in ES5, though.)
It's easy to create data that's shared by all code within Lib, but not per-instance properties, like so:
window.Lib = (function()
{
var sharedData;
// ...
})();
All of the functions defined within there (your ObjectA, etc.) will have access to that one sharedData variable, which is completely inaccessible from outside. But it's not per-instance, each object created by ObjectA, ObjectB, etc. doesn't get its own copy.
Almost doing it
If your code will be running in an environment with ES5 (so, any modern browser, where "modern" does not include IE8 or earlier), you can have obscured but not actually private properties, via Object.defineProperty. This is similar to how private name objects will work in ES.next, but not genuinely private:
Live Example | Source
window.Lib = (function() {
// Get a random name for our "c" property
var c = "__c" + Math.round(Math.random() * 1000000);
// Return our library functions
return {
ObjectA: function() {
// Create an object with a couple of public proprties:
var obj = {
pub1: "I'm a public property",
pub2: "So am I"
};
// Add our obscured "c" property to it, make sure it's
// non-enumerable (doesn't show up in for-in loops)
Object.defineProperty(obj, c, {
enumerable: false, // false is actually the default value, just emphasizing
writable: true,
value: "I'm an obscured property"
});
// Return it
return obj;
},
ObjectB: function(){ /* ... */ },
assess: function(obj) {
// Here, we access the property using the `c` variable, which
// contains the property name. In JavaScript, you can access
// properties either using dotted notation and a literal
// (`foo.propName`), or using bracketed notation and a string
// (`foo["propName"]`). Here we're using bracketed notation,
// and our `c` string, which has the actual property name.
display( obj[c] );
},
alter: function(obj, value) {
// Similarly, we can change the value with code that has
// access to the `c` variable
obj[c] = value;
}
};
})();
And use it like this:
// Create our object
var o = Lib.ObjectA();
// Play with it
display("pub1: " + o.pub1); // displays "pub1: I'm a public property"
display("c: " + o.c); // displays "c: undefined" since `o` has no property called `c`
Lib.assess(o); // displays "I'm an obscured property"
// Note that our obscured property doesn't show up in for-in loops or Object.keys:
var propName, propNames = [];
for (propName in o) {
propNames.push(propName);
}
display("propNames: " + propNames.join(","));
display("Object.keys: " + Object.keys(o).join(","));
// Our Lib code can modify the property
Lib.alter(o, "Updated obscured property");
Lib.assess(o);
The object returned by Lib.ObjectA has a property whose name will change every time Lib is loaded, and which is not enumerable (doesn't show up in for-in loops). The only way to get at it is to know it's name (which, again, changes every time Lib is created — e.g., every page load). The code within Lib knows what the property name is, because it's in the c variable which is shared by all of the Lib code. Since you can access properties using bracketed notation and a string, we can use instance[c] to access the property.
You see how these are pretty well obscured. Code outside of Lib don't see the obscured property when enumerating the property in the object, and they don't know the semi-random name we assigned it, so can't find the property. Of course, you could find it via inspection using a debugger, but debuggers can do lots of things.
And in fact, this is how private properties will work in ES.next, except that c won't be a string, it'll be a private name object.
Well, you would "just" need to declare those variables within the Context of Lib
window.Lib = (function()
{
var _c = 42;
return {
};
});
Notice that I removed the automatic invocation of that pseudo constructor function. That means, you would need to create multiple calls to Lib() for multiple instances, each would have its own unique set of values.
var inst1 = Lib(),
inst2 = Lib();
If you only want to have shared access from all child-context's (functions), you can just use the same pattern you already do (only with moving the var declarations to the parent context like shown above).

Categories