I have the code snippet below:
var ret_ = function(x){
return x;
}
var make_cps=function(x,c_){
return c_(x);
}
var pred = {
_position: 0,
setPosition: function (i) {
_position = i
},
getPosition: function () {
return _position
},
_size: 0,
setSize: function (i) {
_size = i
},
getSize: function () {
return _size
},
_context: null,
setContext: function (x) {
_context = x
},
run: function () {
return function (c_) {
return make_cps(_position, c_);
}(ret_) == 2;
}
}
When I run it like below, it runs correctly:
pred.setPosition(2)
pred.setSize(10)
pred.setContext(null)
var res = pred.run()
console.log(res) // Output: true
but if I replace the _position to getPosition() an error occurs as getPosition() is not defined. Also if I change to this.getPosition() it says this doesn't have a member called getPosition()
var pred = {
_position: 0,
setPosition: function (i) {
_position = i
},
getPosition: function () {
return _position
},
_size: 0,
setSize: function (i) {
_size = i
},
getSize: function () {
return _size
},
_context: null,
setContext: function (x) {
_context = x
},
run: function () {
return function (c_) {
return make_cps(this.getPosition(), c_); // gives Error here
}(ret_) == 2;
}
}
Please someone throw light on this issue.
You've lost your context. Where you've put this.getPosition(), this will return as the window object.
If you alter the line to read
return make_cps(pred.getPosition(), c_);
It will work successfully.
Alternately, you can change the run function to read
run: function () {
var that = this;
return function (c_) {
return make_cps(that.getPosition(), c_);
}(ret_) == 2;
}
Edit: Clarification
The reason that _position is still working rather than suffering from the same issue is that you're not actually setting prev._position at all in your current code.
setPosition: function (i) {
_position = i
},
getPosition: function () {
return _position
}
What it's actually doing there is creating a new global variable called _position and using that instead.
This code should actually read:
setPosition: function (i) {
this._position = i
},
getPosition: function () {
return this._position
},
This is setting a global variable _position:
setPosition: function (i) {
_position = i
},
It is not the variable on your pred object.
When you access it here:
run: function () {
return function (c_) {
return make_cps(_position, c_);
}(ret_) == 2;
}
you're using that global variable.
If you want to use instance properties, you're going to need to start using this:
setPosition: function (i) {
this._position = i
},
run: function () {
var pred = this;
return function (c_) {
return make_cps(pred._position, c_);
}(ret_) == 2;
}
Related
I am building a simple validation library and I need to use Proxy because I want to accept custom validation rules as chain object. I build something and its works properly on modern browsers but not works on IE11, I tried with proxy-polyfill but its also not work properly. My proxy code is below.
function contextProxy(context) {
return new Proxy(context, {
get(obj, prop) {
if (prop in obj) {
return obj[prop];
}
const newContext = contextProxy(context._clone());
if (definedRules.hasOwnProperty(prop)) {
return newContext._takeRule(definedRules[prop]);
}
if (customRules.hasOwnProperty(prop)) {
return newContext._takeRule(customRules[prop]);
}
},
});
}
And I use that proxy;
function validationL() {
return contextProxy(new ValidationLContext());
}
And I have definedRules object;
const definedRules = {
numeric: function () {
return function (text) {
return /^\d+$/.test(text);
};
},
lowercase: function () {
return function (text) {
return /^([a-z]+\s*)+$/.test(text);
};
},
uppercase: function () {
return function (text) {
return /^([A-Z]+\s*)+$/.test(text);
};
},
minLength: function (min) {
return function (text) {
return text.length >= min;
};
},
maxLength: function (max) {
return function (text) {
return text.length <= max;
};
},
alphaNumeric: function () {
return function (text) {
return /^([a-zA-Z0-9 _-]+)$/i.test(text);
};
},
specialChars: function () {
return function (text) {
return !/^([a-zA-Z0-9 _-]+)$/i.test(text);
};
},
email: function () {
return function (text) {
return /^([a-zA-Z0-9_.+-])+\#(([a-zA-Z0-9-])+\.)+([a-zA-Z0-9]{2,4})+$/.test(
text
);
};
}
};
ValidationLContext.js
function ValidationLContext(isNot = false, chain = []) {
this.chain = chain;
this.isNot = isNot;
}
ValidationLContext.prototype.not = function () {
this.isNot = true;
return this;
};
ValidationLContext.prototype._takeRule = function (ruleFn) {
return (...args) => {
this.chain.push({ fn: ruleFn.apply(this, args), isNot: this.isNot });
if (this.isNot) {
this.isNot = false;
}
return this;
};
};
ValidationLContext.prototype.validate = function (text) {
return this.chain.every((c) =>
c.isNot ? !c.fn.call(this, text) : c.fn.call(this, text)
);
};
ValidationLContext.prototype._clone = function () {
return new ValidationLContext(this.isNot, this.chain);
};
export default ValidationLContext;
So library usage like this;
validationL().numeric().minLength(3).validate("123");
validationL().not().numeric().minLength(3).validate("123");
I can use like above on modern browsers like Chrome but when I try on IE11 only not() function works so only objects functions can work.
Can anyone help me with this.
I use Angular 1.5 and I made a factory function which is return a literal object like this:
return {
item: null,
get: function() {
return item;
},
create: function() {
if (this.get()){
this.remove();
}
this.item = {};
},
remove: function() {
var item = this.get();
if (item) {
this.item = null;
}
},
add: function() {
if (!this.get()) {
this.create();
}
this.item.newprop = 'value';
}
}
please do not ask me to change to function declaration. I want a object with his own actions(functions) and properties that is working on.
This pattern (like get inside create so on..) I didn't copied from anywhere. so I'm wonder if has a name? It is best way to deal with function-black boxes?
What is the best way to put Promise inside? so every function should return a promise
every then function I need to use bind???
todo like this:
create: function () {
this.get()
.then(remove)
.then(function () {
this.item = {}; // BUT this === undefined!!
});
}
You have to use bind in every then callback function:
var myModule = {
item: null,
get: function() {
return Promise.resolve(this.item);
},
create: function() {
return this.remove().then(function() {
this.item = {};
}.bind(this));
},
remove: function() {
return this.get().then(function(item) {
if (item) {
this.item = null;
}
}.bind(this));
},
add: function() {
return this.get().then(function(item) {
return item || this.create();
}.bind(this)).then(function() {
this.item.newprop = 'value';
}.bind(this));
}
}
// Let see it working:
myModule.create().then(function() {
return myModule.get();
}).then(function(item) {
console.log("After create: ", item);
return myModule.remove();
}).then(function() {
return myModule.get();
}).then(function(item) {
console.log("After remove: ", item);
return myModule.add();
}).then(function() {
return myModule.get();
}).then(function(item) {
console.log("After add: ", item);
});
I am trying to create a flux store for a React app I am building. I am using an object-assign polyfill npm package and Facebook's Flux library.
Initially I was getting the error "Cannot read property '_data' of null' error in the console which was refering to var currIds = this._data.map(function(m){return m.id;});. That method is currently the only one being called directly. I then did console.log(this) which returned "null".
I find this strange. What is going on?
My code:
var Assign = require('object-assign');
var EventEmitterProto = require('events').EventEmitter.prototype;
var CHANGE_EVENT = 'CHANGE';
var StoreMethods = {
init: function() {},
set: function (arr) {
console.log(this);
var currIds = this._data.map(function(m){return m.id;});
arr.filter(function (item){
return currIds.indexOf(item.id) === -1;
}).forEach(this.add.bind(this));
},
add: function(item){
console.log(this);
this._data.push(item);
},
all: function() {
return this._data;
},
get: function(id){
return this._data.filter(function(item){
return item.cid === id;
})[0];
},
addChangeListener: function(fn) {
this.on(CHANGE_EVENT, fn);
},
removeChangeListener: function(fn) {
this.removeListener(CHANGE_EVENT, fn);
},
emitChange: function() {
this.emit(CHANGE_EVENT);
},
bind: function(actionType, actionFn) {
if(this.actions[actionType]){
this.actions[actionType].push(actionFn);
} else {
this.actions[actionType] = [actionFn];
}
}
};
exports.extend = function(methods) {
var store = {
_data: [],
actions: {}
};
Assign(store, EventEmitterProto, StoreMethods, methods);
store.init();
require('../dispatcher').register(function(action){
if(store.actions[action.actionType]){
store.actions[action.actionType].forEach(function(fn){
fn.call(null, action.data);
})
}
});
return store;
};
I can't see where set is called, however your this can be null if the function is invoked through call (see here) or apply, and your first argument is null.
This also happens in your require.register callback:
fn.call(null, action.data) //First parameter is your 'this'.
My issue is I have 2 inner objects in my js class and I'm trying to use the methods from one of those objects in my other object (examples of what I'm trying to do below). I understand why this doesn't work because of a the scope. I'm just wondering if there is a way to get it to work.
var Class1 = {
self : this,
Obj1 : {
Obj1Method : function () {
alert("Do something");
},
Obj1Method2 : function () {
alert("Do something else");
},
InnerObj1 : {
InnerNestObj1Method : function (val) {
alert(val + 2);
}
}
},
Class1Method2 : function () {
this.Obj1.Obj1Method2();
},
Obj2 : {
Obj2Method : function (val2) {
self.Obj1.InnerObj1.InnerNestObj1Method(val2);
},
Obj2Method2 : function () {
self.Class1Method2();
}
}
};
Class1.Obj1.InnerObj1.InnerNestObj1Method(3); //works
Class1.Class1Method2(); //works
Class1.Obj2.Obj2Method2(); //No bueno
Class1.Obj2.Obj2Method(5); //No bueno
You can fix your example by replacing self with Class1.
The line self : this, is setting Class1.self to point to the global object (this when that line is evaluated).
var Class1 = {
self : this,
Obj1 : {
Obj1Method : function () {
alert("Do something");
},
Obj1Method2 : function () {
alert("Do something else");
},
InnerObj1 : {
InnerNestObj1Method : function (val) {
alert(val + 2);
}
}
},
Class1Method2 : function () {
this.Obj1.Obj1Method2();
},
Obj2 : {
Obj2Method : function (val2) {
Class1.Obj1.InnerObj1.InnerNestObj1Method(val2);
},
Obj2Method2 : function () {
Class1.Class1Method2();
}
}
};
Class1.Obj1.InnerObj1.InnerNestObj1Method(3); //works
Class1.Class1Method2(); //works
Class1.Obj2.Obj2Method2(); //bueno
Class1.Obj2.Obj2Method(5); //bueno
What happens when you do self: this
// If this is running in non strict mode, from the global scope, `this` points
// To the global object because there was no function call setting `this`
var Class1 = {
self : this,
};
What you need to understand is that this is set by whoever called the function using this. In the example above, there is no caller, so the runtime sets this to point to the global object.
Here's how you could you could make your object a bit more reusable and give yourself a reference to the outer object:
function createClass() {
var self = {
Obj1: {
Obj1Method: function() {
alert("Do something");
},
Obj1Method2: function() {
alert("Do something else");
},
InnerObj1: {
InnerNestObj1Method: function(val) {
alert(val + 2);
}
}
},
Class1Method2: function() {
self.Obj1.Obj1Method2();
},
Obj2: {
Obj2Method: function(val2) {
self.Obj1.InnerObj1.InnerNestObj1Method(val2);
},
Obj2Method2: function() {
self.Class1Method2();
}
}
};
return self;
}
var Class1 = createClass();
Class1.Obj1.InnerObj1.InnerNestObj1Method(3); //works
Class1.Class1Method2(); //works
Class1.Obj2.Obj2Method2(); //works
Class1.Obj2.Obj2Method(5); //works
You can do it with Classes:
"use strict"
class Class1 {
constructor() {
this.Obj1 = {
Obj1Method: function() {
alert("Do something");
},
Obj1Method2: function() {
alert("Do something else");
},
InnerObj1: {
InnerNestObj1Method: function(val) {
alert(val + 2);
}
}
};
var self = this;
this.Obj2 = {
Obj2Method: function(val2) {
self.Obj1.InnerObj1.InnerNestObj1Method(val2);
},
Obj2Method2: function() {
self.Class1Method2();
}
};
}
Class1Method2() {
this.Obj1.Obj1Method2();
}
};
var c1 = new Class1();
c1.Obj1.InnerObj1.InnerNestObj1Method(3); //works
c1.Class1Method2(); //works
c1.Obj2.Obj2Method(3); //works
c1.Obj2.Obj2Method2(); //works
I have this code (also shown below) that is giving me an error in IE8 but is fine in Chrome and PhantomJS.
The error is "Object doesn't support this property or method knockout-2.2.1.debug.js, line 2319 character 35", which is called from currentPage(pages[pages.indexOf(current) + steps]);
I have no clue why it's not working, so any help would be greatly appreciated!
var Page = (function () {
function Page(index, name, canNavigateToPage, navigatedToThisPage) {
this.index = index;
this.name = name;
this.canNavigateToPage = canNavigateToPage;
this.navigatedToThisPage = navigatedToThisPage;
}
Page.prototype.navigateToPage = function () {
if (this.canNavigateToPage()) {
this.navigatedToThisPage(this);
}
};
return Page;
})();
var AccountSearchParameters = (function () {
function AccountSearchParameters() {
this.reference = ko.observable();
this.scheme = ko.observable();
this.lastName = ko.observable();
this.sufficientInputToSearchForAccount = ko.computed(function () {
return this.reference() && this.scheme() && this.lastName();
}, this);
}
return AccountSearchParameters;
})();
function viewModel() {
var self = this,
currentPage = ko.observable(),
accountSearchParameters = new AccountSearchParameters(),
forwardPageProgressionGuards = {
'1': function canMoveToPage2() {
return accountSearchParameters.sufficientInputToSearchForAccount();
},
'2': function canMoveToPage3() {
return true;
},
'3': function canMoveToPage4() {
return true;
}
},
canMoveToNextPage = function (currentlyOnPage) {
function disallowPageMovementNotExplicitlyDefined() {
return false;
}
return (forwardPageProgressionGuards[currentlyOnPage] || disallowPageMovementNotExplicitlyDefined)();
},
canMoveToPreviousPage = function (currentlyOnPage) {
return currentlyOnPage > 1;
},
pages = [
new Page(1, 'Customer details', function () {
return true;
}, function (page) {
currentPage(page);
}),
new Page(2, 'Bank details', forwardPageProgressionGuards['1'], currentPage),
new Page(3, 'Payment details', forwardPageProgressionGuards['2'], currentPage),
new Page(4, 'Confirmation', function () {
return true;
}, currentPage)],
pageNavigator = function (canNavigate, steps) {
current = currentPage();
console.log(canNavigate(current.index));
if (canNavigate(current.index)) {
currentPage(pages[pages.indexOf(current) + steps]);
}
};
currentPage(pages[0]);
self.page = ko.computed(function () {
return currentPage();
});
self.accountSearchParameters = accountSearchParameters;
self.nextPage = function () {
pageNavigator(canMoveToNextPage, 1);
};
self.previousPage = function () {
pageNavigator(canMoveToPreviousPage, -1);
};
self.canMoveToNext = ko.computed(function () {
return canMoveToNextPage(currentPage().index);
});
return self;
}
$(function () {
ko.applyBindings(viewModel());
});
indexOf in IE8 does not supported, use $.inArray