A strange problems about javascript prototype :
(function(w){
if(!w)
return;
var TestJS = function(){
};
TestJS.prototype = {
data:{},
initData:function(){
this.data={
val_name_1 : 1,
val_name_2 : 2,
val_name_3 : "hello-3"
};
console.log(this.data);
return this;
},
TestChildJS:{
initChild:function(){
console.log(TestJS);
console.log(TestJS.data);
console.log(new TestJS().data.val_name_1);
console.log(TestJS.data.val_name_1);
}
}
};
window.TestJS = new TestJS();
})(window);
why 'TestChildJS' can not get 'val_name_1'?
TestJS.initData();
console.log(TestJS.TestChildJS.initChild());
console pic
so I have to write my code like that:
(function(w){
if(!w)
return;
var TestJS = function(){
};
TestJS.prototype = {
data:{},
initData:function(){
this.data={
val_name_1 : 1,
val_name_2 : 2,
val_name_3 : "hello-3"
};
console.log(this.data);
this.TestChildJS.initParentData(this);
return this;
},
TestChildJS:{
parentData:{},
initParentData:function(parent){
this.parentData = parent.data;
return this;
},
initChild:function(){
console.log(this.parentData);
}
}
};
window.TestJS = new TestJS();
})(window);
How to use the first way can get the content of the second way?
why 'TestChildJS' can not get 'val_name_1'?
when:
TestJS.initData();
is run, it adds a data property to the TestJS object (the one assigned by window.TestJS = new TestJS()). That property isn't inherited by any other object.
When:
console.log(new TestJS().data.val_name_1);
is run, the object returned by new TestJS() has not had it's initData method called yet, so it doesn't have a data property and it doesn't inherit it from the constructor (because the property is directly on the constructor itself, not its prototype).
Note also that assigning a new object to this.data creates a property directly on the instance, so adding to this.data is modifying the instance's data object, not the one on the constructor's prototype.
The patterns in your code (especially the second one) seem unnecessarily convoluted.
It has to do with the scope of the IIFE. A variable declared inside a closure shadows any outer variable with the same name. Since after the IIFE executes you no longer have access to its scope, TempJS inside it will always be a function constructor -- not an instantiated object.
Consider this example:
var i;
var func = (function(){
i = 1;
return function() {
console.log(i)
};
})();
func(i); // 1
i = 2;
func(i); // 2
If I re-declare the i variable inside the closure, look what happens:
var i = 1;
var func = (function(){
var i = 1;
return function() {
console.log(i)
};
})();
func(i); // 1
i = 2;
func(i); // 1
So one solution to your problem would be to declare TestJS once before the IIFE.
var TestJS;
(function(w){
if(!w)
return;
TestJS = function(){
};
// ...
TestChildJS:{
initChild:function(){
console.log(TestJS.data.val_name_1);
}
// ...
window.TestJS = new TestJS();
})(window);
TestJS.initData();
console.log(TestJS.TestChildJS.initChild()); // 1
Notice that I removed console.log(new TestJS().data.val_name_1);. TestJS is no longer a constructor function, so that line will throw.
Another solution is to assign the empty function expression to window.TestJS inside the closure, instead of var TestJS. Doing so will not create a local TestJS name and will therefore prevent the ambiguity.
Related
Regarding private variable encapsulation - achieved by defining the private variable as var instead of it being a property of this ( not private this.private ). This is the most direct way of making a member private.
But, the above rule makes sense if the instance is being returned and used. In case we return a new object with get/set methods being exposed. Now, does storing the private variable in the this param still achieve private variable encapsulation
Is there any way that I can get access to the instance param when it's not being returned?
function X(init) {
this.private = init;
var that = this
function get() {
return that.private;
}
function set (tmp) {
that.private = tmp;
}
return {
get: get,
set: set
}
}
var tmp = new X(1);
console.log(tmp.get()) // 1
console.log(tmp instanceof X) // false
tmp.private = 20 // doesnt work as tmp isnt an instance object
console.log(x.get()) //1
x.set(20)
console.log(x.get()) //20
Do I have access to private as it's a property of this when the this isnt being returned?
No, we can not have access to private property on this as you are explicitly returning new object.
Consider this example when using a constructor:
function X()
{
// Scope of X()
var i = 1; // this variable is accessible only in this scope
this.get = function() { return i; };
}
a = new X(); // call the constructor
console.log(a.i); // this will be undefined
a.i = 2; // even if you try to set `i` on `a`
console.log(a.get()); // the actual `i` of `a` will still be 1
console.log(a.i); // this will be two.
Now for the example which uses a function which returns an object.
function Y()
{
// Scope of Y()
var i = 1; // this variable is accessible only in this scope
function get() { return i; };
return { // now you return an object which exposes a function that returns the value of `i` of this SCOPE.
get: get
}
}
b = Y();
console.log(b.i); // this will be undefined
console.log(b.get()); // this will be 1
b.i = 3; // this will work
console.log(b.get()); // this will still be 1
console.log(b.i); // this will be 3
I'm sorry I don't have enough time to continue the explanation, I'll return to this later.
You can hide(make private) member variables by hiding it in the scope.
Also, between these two approaches, only the first approach where you will be able to make use of the this keyword. You can't use this keyword in the second approach as its value will be the Window
You can achieve true privacy using a WeakMap.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap
WeakMaps allow you to create an affiliated object on which you can append any values.
You can't access them if you can't access the scope in which they were defined.
Using ES5 in a browser
"use strict";
{
const privacy = new WeakMap();
function X(init)
{
privacy.set(this, {});
const props = privacy.get(this);
props.init = init;
}
X.prototype.setInit = function(tmp)
{
privacy.get(this).init = tmp;
};
X.prototype.getInit = function()
{
return privacy.get(this).init;
};
window.X = X;
}
Using ES6 class notation in NodeJS
const privacy = new WeakMap();
class X
{
constructor(init)
{
privacy.set(this, {});
const props = privacy.get(this);
props.init = init;
}
setInit(tmp)
{
privacy.get(this).init = tmp;
}
getInit()
{
return privacy.get(this).init;
}
}
module.exports = X;
I have 2 objects: myObject1 and myObject2.
I am trying to call a private variable from myObject1 using my0bject2 method called increment, but console.log says NaN.
Is there any way to call myObject1 variable directly from myObject2 method? Maybe extend it somehow?
var myObject1 = function() {
var x = 0;
return{}
}();
var myObject2 = function() {
return{
increment: function() {
myObject1.x +=1;
console.log(myObject1.x);
}
}
}();
myObject2.increment();
When you specify var x within myObject1 you are declaring that variable as private. The only thing that has access to that variable are methods within myObject1. As you noted, this variable is private.
You didn't make it clear if you wanted to keep it private or not so I will assume you just want to access it. You could do a couple of things here.
You could attach the variable as an object property of myObject1 via this. this refers to the current scope (myObject1) so by saying this.x within myObject1 you are saying myObject1.x. From that function, you will need to return this so that instances of it can access all of its public properties.
var myObject1 = function() {
this.x = 0;
return this;
}();
var myObject2 = function() {
return {
increment: function() {
myObject1.x +=1;
console.log(myObject1.x);
}
}
}();
myObject2.increment();
You could also just return the properties that you want, in this case x. You have more control over what is returned in this case and you get the same result as above.
var myObject1 = function() {
var x = 0;
return {
x: x,
};
}();
var myObject2 = function() {
return {
increment: function() {
myObject1.x +=1;
console.log(myObject1.x);
}
}
}();
myObject2.increment();
Either way, these methods both expose the x property of myObject1 making it public.
Its because you have to create an instance of the function myObject before you can access its properties,
/* Create an object called myObject which can be instantiated using the new keyword */
var myObject = function() {
/* use 'this.x' to define an internal variable for x */
this.x = 0
};
/* this is where you create a new instance of myObject which will have the 'x' property */
var myObject1 = new myObject();
var myObject2 = function() {
return {
increment: function() {
myObject1.x += 1;
console.log(myObject1.x);
}
}
}();
myObject2.increment();
EDIT: Added comments to the code to explain the important modifications
myObject1 returns an object in which x is its own property. So now you're incrementing one of the property of the object. Earlier, with var x you were assuming to increment property of myObject1 but in fact it was just a local variable for that function which returned your object, i.e. declaring var x in function has no effect whatsoever.
var myObject1 = function() {
return{
x: 0
}
}();
var myObject2 = function() {
return{
increment: function() {
myObject1.x +=1;
console.log(myObject1.x);
}
}
}();
myObject2.increment();
In javascript, there is function scope, that is anything you define with var keyword is accessible within that function body.
Here you are implementing the module pattern. Your functions behave as constructors (or a factory) in your case (returning the object constructed). myObject2 cannot access x as it is defined in another scope and it is not included in the object generated by your first function.
You can return the x variable inside the return object;
var myObject1 = function() {
return{
x: 0
}
}();
That makes it a property on the returned object (that is assigned to myObject1) and you can access it.
You should study "function scope" and "module pattern" in more detail.
You're defining a variable in your functions scope. Instead, you should assign a variable to your current object (this) and of course return that object instead of an empty one.
var myObject1 = function() {
this.x = 0;
return this;
}();
I have read this answer and IIFE but I can't seem to find the correct solution to my problem.
I have a simple class here:
define(['jquery'], function($) {
// Need 'self' because someCallback() is being called with .call() and 'this' changes
var self;
function Foo(number) {
self = this;
this.someNumber = number;
}
Foo.prototype = {
someCallback: function () {
//Use self because 'this' changes to a DOM element
var num = self.someNumber;
//Do something with the num
return num * 2;
}
};
return Foo;
});
and someCallBack() is being called by a jQuery plugin using .call(). Because of this, the context changed, hence the use of the self variable.
However, this is wrong because:
define(['foo'], function(Foo) {
describe('context question', function () {
var foo1 = new Foo(1);
var foo2 = new Foo(2);
it('"this" should work', function () {
var call1 = foo1.someCallback.call(this); // 4
var call2 = foo2.someCallback.call(this); // 4
expect(call2).toBe(4); // Only works because it is 'new' last
expect(call1).toBe(2); // Fails because 'self' is taken from foo2
});
});
});
How exactly should I wrap the self variable to make this code work?
You could probably just use the revealing module pattern and declare it as a "global" variable (local to the module):
define(['jquery'], function($) {
var someNumber;
function Foo(number) {
someNumber = number;
}
Foo.prototype = {
someCallback: function () {
return someNumber * 2;
}
};
return Foo;
});
Two ways of calling an object method which stores its own this value include
Define the method as a nested function which references its this value in a closure which stores this value in a variable. The function defined could be anonymous or declared with a name but must be evaluated each time a class instance is created, so as to create a new Function object capturing different values of self in function scope.
Take a statically defined function object and bind its this value using bind. Bind creates a new wrapper function object each time it is called.
The first method looks like (without Jquery or Jasmine):
function Foo(number)
{ var self = this;
this.num = number;
this.someCallback = function() // method with new Foo object stored as self in function scope
{ // something with num:
return self.num * 2;
}
}
and the second method could look like
function Foo(number)
{ this.num = number
this.someCallback = this.someCallback.bind(this); // bind prototypical method as local method.
}
Foo.prototype = {
someCallback: function () {
// this value is bound by constructor;
//Do something with the num
return this.num * 2;
}
};
When I use var keyword to declare any variable it gets declared inside the enclosing scope. However in the code below, I have declared function c (inside an object method a.b) with var keyword and still this inside the function c is bound to the global object window. Why is this?
var a = {
b: function () {
var c = function () {
return this;
};
return c();
}
};
document.write(a.b()); //prints: [object Window]
The value of this is determined by context, not scope.
When you call a function without any context (context.func()) as you do there (c()), the default context is the default object (which is window in browsers) unless you are in strict mode (in which case it is undefined instead).
(There are exceptions to this rule, such as apply, call, bind, and new but none of them apply here).
Many people get confused by this. The value this depends on one of 4 methods of invocation.
However, functional invocation and method-invocation cause most of the confusion.
If a function is a member of an object, this is the object itself.
obj.someFunction(); //method invocation
If a function is called without context this is the global object (in 'strict mode' this is undefined.)
someFunction(); //functional invocation
The confusion occurs when a function is called within an object, but not as a member of the object as in anObject.testWithHelper(..);
var testForThis = function(isThis, message) {
//this can be confusing
if(this === isThis)
console.log("this is " + message);
else
console.log("this is NOT " + message);
};
//functional invocation
testForThis(this, "global"); //this is global
var anObject = {
test: testForThis, //I am a method
testWithHelper: function(isThis, message) {
//functional invocation
testForThis(isThis, message + " from helper");
}
};
//method invocation
anObject.test(anObject, "anObject"); //this is anObject
//method invocation followed by functional invocation
anObject.testWithHelper(anObject, "an object"); //this is NOT anObject from helper
Here is my JSFIDDLE
If you would like c to return a, you can use closure:
var a = {
b: function () {
var that = this;
var c = function () {
return that;
};
return c();
}
};
Or avoid this all together:
var getNewA = function() {
var newA = {};
newA.b = function() {
var c = function() {
return newA;
};
return c();
};
return newA;
};
var newA = getNewA();
I am having a JavaScript namespace say
A={
CA: function() {
this.B();
},
B: function() {
var test='test';
var result='t1';
C: function() {
this.test='test1';
.....
.....
return 'test1';
}
result=this.C();
return result;
}
}
Now when I am executing such code it is giving that TypeError: this.C is not a function. Can somebody tell me why it is so. I know it is something related with lexical scoping but am unable to understand this.
You have to be careful when you use this to identify anything in Javascript because each time you change scope "this" changes.
Assigning the 'this' reference to it's own variable helps get around this.
var a = new function() {
var self = this;
self.method = function() { alert('hiya'); };
var b = function() {
this.method(); // this isn't 'a' anymore?
self.method(); // but 'self' is still referring to 'a'
};
};
I think the problem is that when this.C() is executed inside the function referred to by B, this refers to the object that contains B, that is, object A. (This assumes B() is called within the context of A)
The problem is, C does not exist on the object A, since it's defined within B. If you want to call a local function C() within B, just use C().
EDIT:
Also, I'm not sure what you've posted is valid JavaScript. Specifically, B should be defined this way, since you can't use the object:property syntax within a function.
B: function()
{
var test='test';
var result='t1';
var C = function()
{
this.test='test1';
return 'test1';
}
result=C();
return result;
}
I am actually surprised that your code doesn't give you error on the 'C:' line.
Anyway, your syntax to define a function is not correct. Define it using the var keyword. Also, notice that I created the 'closure' so that the function C can access 'this'. See the code below:
A={
CA: function()
{
this.B();
},
B: function()
{
var test='test';
var result='t1';
var self = this;
var C = function()
{
self.test='test1';
.....
.....
return 'test1';
}
result=C();
return result;
}
}
If you want to assign C to 'this' object, you can also do:
A={
CA: function()
{
this.B();
},
B: function()
{
var test='test';
var result='t1';
var self = this;
this.C = function()
{
self.test='test1';
.....
.....
return 'test1';
};
result= this.C();
return result;
}
}
Solution for calling methods from another method. (Essentially the pointer "this" must be assigned to a variable and the new variable used in place of this.)
function myfunction(){
var me = this;
me.method1 = function(msg){ alert(msg); }
me.method2 = function(){
me.method1("method1 called from method2");
}
}
var f as new myfunction();
f.method2();
This example shows how one can call a method from within another method or from outside using an instance of the function.