I have a simple object that I am mocking, assume something like this:
function MyObject() {
this.SomeVariable = "some-value";
this.SomeFunction = function() { ... };
}
Now I want to mock the variable, which I was doing:
var mockedObject = mock(MyObject);
when(mockedObject).SomeVariable.thenReturn("some-other-value");
However it tells me that I cannot use thenReturn() on this... cant remember the exact error but does anyone know a way around this? Or should I just do:
var mockedObject = mock(MyObject);
mockedObject.SomeVariable = "some-other-value";
You can only stub or verify interactions (function calls). Because "SomeVariable" is an attribute in the object, not a function in the object you cannot stub or verify uses of it.
So your conclusion is correct; you should simply provide the value you want as an attribute of the object:
var mockedObject = mock(MyObject);
mockedObject.SomeVariable = "some-other-value";
An alternative, if it's your own code, is to change the original object to use a "getter" style function instead, e.g.:
function MyObject() {
this.SomeVariable = function() { return "some-value"; };
this.SomeFunction = function() { ... };
}
For true "objects" this is often a better approach than using attributes.
Related
I am looking forward about a concrete question in javascript.
I would like to use a function expression as a class method, but I don´t know if it is possible. I try to look any documentation about it but I didn´t find... This is an example:
class TourIdentifier extends HttpClient {
constructor(baseURL, langCode, ...attr) {
super({
baseURL,
headers: {
lang: langCode
}
});
this.model.init(...attr);
}
model = function init(...attr) {
this.tours_identifiers_identifier = attr[0];
this.tours_id = attr[1];
this.lang_code = attr[2];
this.tours_id_ = attr[3];
this.tours_identifiers_id = attr[4];
}
...
I know there are many ways to do this, but I just have this question, and I hope that anyone with more experience than me in JS can share they knowledge.
I appreciate your comments!
Best regards!
This is everything you need to know about method definition in classes MDN, but here is some of my explanation:
Basically you are trying to define a method in a class(which requires giving this method a name) and then assign a named function to it.
This will work, but you won't be able to call the method of the class using the function's name, but you will be able to do it using the method's name.
Imagine you have a object and a separate function:
function func() {return 'Hello'}
const obj = {};
And you want to assign the function to one of the object's properties you will do:
obj.myFunction = func;
And from now on you are going to call the function from the object like this:
obj.myFunction();
But of course we can assign the function at the object's declaration:
function func() {return 'Hello'};
const obj = {
myFunction: func
}
And you are still going to call the function form the object like this:
obj.myFunction();
And of corpse you can declare the named functioning in the object's declaration:
const obj = {
myFunction: function func() {return 'Hello'}
}
And you are still going to call the function form the object like this:
obj.myFunction();
So I hope you see where I'm going.... the same thing can be applied to classes, as we know they are just sugar syntax :)
In short this is possible, but the name of the function won't be useful.
class foo {
constructor() {
//something
}
method = function init() {
console.log('init 1');
}
}
const fooInstance = new foo();
fooInstance.method();
Is there a way for a function to get the object value before the function name? Below is an example of what I am kinda trying to achieve. I want to be able to read the myElement variable within the function but not pass it as a parameter but pass it before the function with the dot.
Any simple examples or explanations would be most helpful.
var myElement = document.getElementById('myID');
myElement.myFunction();
function myFunction() {
alert(myElement);
}
The only way you could do this is to add myFunction to HTMLElements prototype (which is what gets returned by document.getElementById(). That's usually frowned upon, but if it's your own project and you know what you do, you could do that.
var myElement = document.getElementById('myID');
HTMLElement.prototype.myFunction = function() {
console.log(this);
}
myElement.myFunction();
<div id="myID"></div>
With this prototype in place, you can call myFunction on every HTMLElement in your code.
In regards to your last comment, the function could be
HTMLElement.prototype.myFunction = function() {
alert(this.id);
}
I don't see why you should do it, as it's much easier to just do
alert(myElement.id);
In regards to the comments, here's what I'd do. Instead of extending anything, create your own class (or function), that takes a HTMLElement. Now on this class, you can add whatever method you want, manipulate your element and then return the plain HTMLElement from a getter. You can obviously change that to whatever return you want.
class MyHtmlElement {
constructor(htmlElement) {
this._htmlElement = htmlElement;
}
alertId() {
alert(this._htmlElement.id);
// optional
return this;
}
logId() {
console.log(this._htmlElement.id);
// optional
return this;
}
setId(newId) {
this.htmlElement.id = newId;
// optional
return this;
}
setStyle(prop, val) {
this._htmlElement.style[prop] = val;
// optional
return this;
}
get htmlElement() {
return this._htmlElement;
}
set htmlElement(value) {
this._htmlElement = value;
}
}
const el = new MyHtmlElement(document.getElementById('foo'));
el
.setId('bar')
.logId()
.alertId()
.setStyle('background-color', 'red')
.setStyle('width', '100vw')
.setStyle('height', '100vh');
// If you need the plain element, return it
const plainHTMLElement = el.htmlElement;
console.log(plainHTMLElement);
<div id="foo"></div>
When a function is stored in an object and then called with theObject.theFunction(), the value of this within the function will be theObject.
function sayHello() {
alert('Hello, my name is ' + this.name);
}
let myObject = { name: 'Bob', speak: sayHello };
myObject.speak(); // shows the message 'Hello, my name is Bob'
Now if you want to be able to create your own function and let it be used by an Element, you either need to store the function in the Element instance first or to add it to the Element prototype, both of which I highly discourage. If you feel like you have to do this, there's a flaw in your design.
Still, if you have a good reason to add a custom method to an existing object, I recommend you look up lessons about prototype inheritance in JavaScript, or read my old answer about it here if you're not sure how it works. You could say, make a function which adds methods to an object when it is called, like this:
function addMethods(elem) {
elem.speak = sayHello;
}
let myElement = document.getElementById('myID');
addMethods(myElement);
myElement.speak(); // Hello, my name is <value of the element's name attribute>
Or you could add the method to the prototype of all elements:
Element.prototype.speak = sayHello;
let myElement = document.getElementById('myID');
myElement.speak();
While browsers have let people do this since forever ago, there is technically no guarantee that Element is publicly available, or that its prototype is modifiable, or that you can add methods to Element instances. The Prototype framework (an inconveniently named third party library) has been using these techniques for a long time, but it did cause them a couple issues. jQuery prefers using a different approach, wrapping elements in another object on which custom methods are put.
The below code snippet I found on one blogs to avoid if-else statement. This code is very modular and can be easily extended. But I am not able to get this to work.
CatModel.prototype.makeWords = function () {
console.log('inside catmodel')
this.setWord('meow')
this.sayIt()
}
DogModel.prototype.makeWords = function () {
console.log('inside dogmodel')
this.setWord('bark')
this.saySomething()
}
// somewhere else
var makeWords = function (type) {
var model = namespace[type + 'Model']
model.makeWords()
}
makeWords('cat')
Presumably the CatModel and DogModel functions are declared somewhere and setWord and sayIt are also set up on their prototype object.
You'd need to put CatModel and DogModel in an object and refer to it from namespace (which I'd recommend not calling namespace):
var namespace = {
CatModel: CatModel,
DogModel: DogModel
};
Then when creating an instance, use new (you always use new with constructor functions). I'd also put the () on the call even though strictly speaking they're optional if you don't have parameters to pass:
var makeWords = function (type) {
var model = new namespace[type + 'Model']()
// ---------^^^--------------------------^^
model.makeWords()
}
When creating an object to use JS in an OO manner, is there any difference within a JS engine between (with the exception of being able to define a constructor:
var Uploader = Uploader || {};
and
var Uploader = function() {
}
and
function Uploader() {
}
Especially when later, you wish to do something along the lines of
Uploader.DOM = {
Create: function(file) {
}
};
Is it all down to personal preference? Or is there a real difference?
Objects:
var MyObj = {
myArr: [1,2,3],
find: function(/*some arguments*/) {
//some logic that finds something in this.myArr
}
}
In MyObj.find function this keyword will point to MyObj (which somewhat resembles how this works in languages those have classes). You can use this functionality to do mix-ins:
var MyObj2 = {
myArr: [4,2,6]
}
MyObj2.find = MyObj.find;
In MyObj2.find function this keyword will point to MyObj2.
Also objects support getters and setters (works on IE9+ and all good browsers):
var MyObj = {
myArr: [1,2,3],
find: function(/*some arguments*/) {
//some logic that finds something in this.myArr
},
get maxValue() {
return Math.max.apply(null, this.myArr);// maxValue is calculated on the fly
},
a_: null,
get a () {
return this.a_;
},
set a (val) {
//fire a change event, do input validation
this.a_ = val;
}
}
Now max value in the array can be accessed like this: MyObj.maxValue. I also added a property a_. It can't be named the same as its getter and setter so appended an underscore. Appending or prepending underscores is a naming convention for private variables which should not be accessed directly.
var qwe = MyObj.a // get a_
MyObj.a = 'something'; //set a_
Functions:
var u = new Uploader(); // will throw an exception
var Uploader = function() { }
Uploader is defined in runtime here. It does not exist yet when I try to instantiate it.
var u = new Uploader(); //will work
function Uploader() {}
Uploader is defined in compilation time here so it will work.
Functions can be used with revealing pattern to conceal some members. Functions don't support getters and setters but you can put objects inside functions.
function myFunc() {
function privateFunc() {};
function publicFunc() {};
var obj = {
//members, getters, setters
};
return {
publicFunc: publicFunc,
obj: obj
}
}
You can call muFunc.publicFunc() outside of myFunc because it is returned. But you can not use privateFunc outside because it is not returned.
Revealing pattern functions are not meant to be instantiated usually. This is because when you instantiate it everything inside will be copied to a new instance. So it will use up more memory than if you would add functions using prototype.
myFunc.prototype.someFunc = function() {};
Like this all instances of myFunc will share the same instance of someFunc.
Conclusion: with functions you can simulate a private access modifier but in objects the this keyword acts somewhat similar of what you'd expect in a language that have classes. But you always can use call, apply and bind to change the context (i.e. what 'this' keyword will be) of the function.
This is my first stab at OOP, so please bear with me:
(function(){
var Ship = function(){
this.passengers = [];
this.hasAliens = function() {
return this.passengers.some(function(passenger){
return passenger.isAlien()
});
}
};
var Passenger = function(){};
Passenger.prototype.isAlien = function(){
return this instanceof Alien;
};
Passenger.prototype.board = function(ship) {
ship.passengers.push(this)
}
var Alien = function() { Passenger.call(this); }
var Human = function() { Passenger.call(this); }
Alien.prototype = Object.create(Passenger.prototype);
Human.prototype = Object.create(Passenger.prototype);
Alien.prototype.constructor = Alien.constructor;
Human.prototype.constructor = Human.constructor;
var ship = new Ship();
var john = new Human();
var zorg = new Alien();
//simple testing
john.board(ship);
console.log("Ship does not have aliens ", ship.hasAliens()===false);
zorg.board(ship);
console.log("Ship has aliens ", ship.hasAliens()===true);
})();
This works fine. However, I'd like to know how to pass the Passenger.isAlien() method to save me that nasty nested anonymous function. I'm trying to do it like this:
var Ship = function(){
this.passengers = [];
this.hasAliens = function(){
return this.passengers.some(Passenger.isAlien);
};
};
But that gives me "undefined is not a function"
http://jsfiddle.net/WYyxY/
As I said, isAlien is a property of the prototype, i.e. an instance of the constructor function, and not the constructor function itself. Passenger.isAlien is indeed undefined (nowhere in your code is Passenger.isAlien = function....).
There is not really a more concise way to do this. Think about what a callback passed to .some is doing: It has to take an element of the array as argument and then do something with it. In your case you want to execute a method of that element.
One way to call a method and pass the object it should be called on as parameter is to use .call [MDN]. Unfortunately, as with all functions in JavaScript, you cannot just get a reference to Passenger.prototype.isAlien.call, because .call looses its context (it does not know which function it refers to). You'd have to bind it to Passenger.prototype.isAlien first
this.passengers.some(
Passenger.prototype.isAlien.call.bind(Passenger.prototype.isAlien)
);
and personally I find that not more readable.
Stick with the anonymous function, your intend is much clearer. Or if you want to, you can let another function create that function:
function callOn(funcName) {
return function(obj) {
return obj[funcName]();
};
}
this.passengers.some(callOn('isAlien'));
For doing OOP with javascript, I strongly recommend checking out prototypeJS. Your code becomes much more readable, and it also supports inheritance!
Here's a quick look at it