How does one reassign variables in an object? - javascript-objects

For a class I'm taking, I need to reassign an object's properties to "Unknown". Here is the sample problem:
//In the function below, "person" will be passed in as an object that represents a person with properties such as name, age, gender, etc.
//Loop through all the properties of the object and set each value equal to "Unknown"
//For example, if "person" is {name: "Dolph L.", age: 33} then the function would return {name: "Unknown", age: "Unknown"}
function describePerson(person) {
//code here
}
If I'm understanding is correctly, it says that I must loop through the object and reassign it's properties to "Unknown". What I've been trying to do is this:
function describePerson(person) {
for (var prop in person) {
if (prop) {
return "Unknown";
}
}
}
I'm very new to Javascript and could really use some help here.
Thanks

for (var prop in person) {
if (person.hasOwnProperty(prop)) {
person[prop] = "Unknown";
}
}
return person;
You don't want to return in the loop, you want to modify each property of the object using the = operator.
The reason behind hasOwnProperty is the following.
person is an object. The object inherits properties from its Prototype. You don't care about those inherited properties, so you call hasOwnProperty which returns true if the property is one you gave it.

Related

Why class method in typescript "is not a function" when class is not initialized directly by "new" [duplicate]

I read a JSON object from a remote REST server. This JSON object has all the properties of a typescript class (by design). How do I cast that received JSON object to a type var?
I don't want to populate a typescript var (ie have a constructor that takes this JSON object). It's large and copying everything across sub-object by sub-object & property by property would take a lot of time.
Update: You can however cast it to a typescript interface!
You can't simple cast a plain-old-JavaScript result from an Ajax request into a prototypical JavaScript/TypeScript class instance. There are a number of techniques for doing it, and generally involve copying data. Unless you create an instance of the class, it won't have any methods or properties. It will remain a simple JavaScript object.
While if you only were dealing with data, you could just do a cast to an interface (as it's purely a compile time structure), this would require that you use a TypeScript class which uses the data instance and performs operations with that data.
Some examples of copying the data:
Copying AJAX JSON object into existing Object
Parse JSON String into a Particular Object Prototype in JavaScript
In essence, you'd just :
var d = new MyRichObject();
d.copyInto(jsonResult);
I had the same issue and I have found a library that does the job : https://github.com/pleerock/class-transformer.
It works like this :
let jsonObject = response.json() as Object;
let fooInstance = plainToClass(Models.Foo, jsonObject);
return fooInstance;
It supports nested children but you have to decorate your class's member.
In TypeScript you can do a type assertion using an interface and generics like so:
var json = Utilities.JSONLoader.loadFromFile("../docs/location_map.json");
var locations: Array<ILocationMap> = JSON.parse(json).location;
Where ILocationMap describes the shape of your data. The advantage of this method is that your JSON could contain more properties but the shape satisfies the conditions of the interface.
However, this does NOT add class instance methods.
If you are using ES6, try this:
class Client{
name: string
displayName(){
console.log(this.name)
}
}
service.getClientFromAPI().then(clientData => {
// Here the client data from API only have the "name" field
// If we want to use the Client class methods on this data object we need to:
let clientWithType = Object.assign(new Client(), clientData)
clientWithType.displayName()
})
But this method will not work on nested objects, sadly.
I found a very interesting article on generic casting of JSON to a Typescript Class:
http://cloudmark.github.io/Json-Mapping/
You end up with following code:
let example = {
"name": "Mark",
"surname": "Galea",
"age": 30,
"address": {
"first-line": "Some where",
"second-line": "Over Here",
"city": "In This City"
}
};
MapUtils.deserialize(Person, example); // custom class
There is nothing yet to automatically check if the JSON object you received from the server has the expected (read is conform to the) typescript's interface properties. But you can use User-Defined Type Guards
Considering the following interface and a silly json object (it could have been any type):
interface MyInterface {
key: string;
}
const json: object = { "key": "value" }
Three possible ways:
A. Type Assertion or simple static cast placed after the variable
const myObject: MyInterface = json as MyInterface;
B. Simple static cast, before the variable and between diamonds
const myObject: MyInterface = <MyInterface>json;
C. Advanced dynamic cast, you check yourself the structure of the object
function isMyInterface(json: any): json is MyInterface {
// silly condition to consider json as conform for MyInterface
return typeof json.key === "string";
}
if (isMyInterface(json)) {
console.log(json.key)
}
else {
throw new Error(`Expected MyInterface, got '${json}'.`);
}
You can play with this example here
Note that the difficulty here is to write the isMyInterface function. I hope TS will add a decorator sooner or later to export complex typing to the runtime and let the runtime check the object's structure when needed. For now, you could either use a json schema validator which purpose is approximately the same OR this runtime type check function generator
TLDR: One liner
// This assumes your constructor method will assign properties from the arg.
.map((instanceData: MyClass) => new MyClass(instanceData));
The Detailed Answer
I would not recommend the Object.assign approach, as it can inappropriately litter your class instance with irrelevant properties (as well as defined closures) that were not declared within the class itself.
In the class you are trying to deserialize into, I would ensure any properties you want deserialized are defined (null, empty array, etc). By defining your properties with initial values you expose their visibility when trying to iterate class members to assign values to (see deserialize method below).
export class Person {
public name: string = null;
public favoriteSites: string[] = [];
private age: number = null;
private id: number = null;
private active: boolean;
constructor(instanceData?: Person) {
if (instanceData) {
this.deserialize(instanceData);
}
}
private deserialize(instanceData: Person) {
// Note this.active will not be listed in keys since it's declared, but not defined
const keys = Object.keys(this);
for (const key of keys) {
if (instanceData.hasOwnProperty(key)) {
this[key] = instanceData[key];
}
}
}
}
In the example above, I simply created a deserialize method. In a real world example, I would have it centralized in a reusable base class or service method.
Here is how to utilize this in something like an http resp...
this.http.get(ENDPOINT_URL)
.map(res => res.json())
.map((resp: Person) => new Person(resp) ) );
If tslint/ide complains about argument type being incompatible, just cast the argument into the same type using angular brackets <YourClassName>, example:
const person = new Person(<Person> { name: 'John', age: 35, id: 1 });
If you have class members that are of a specific type (aka: instance of another class), then you can have them casted into typed instances through getter/setter methods.
export class Person {
private _acct: UserAcct = null;
private _tasks: Task[] = [];
// ctor & deserialize methods...
public get acct(): UserAcct {
return this.acct;
}
public set acct(acctData: UserAcct) {
this._acct = new UserAcct(acctData);
}
public get tasks(): Task[] {
return this._tasks;
}
public set tasks(taskData: Task[]) {
this._tasks = taskData.map(task => new Task(task));
}
}
The above example will deserialize both acct and the list of tasks into their respective class instances.
Assuming the json has the same properties as your typescript class, you don't have to copy your Json properties to your typescript object. You will just have to construct your Typescript object passing the json data in the constructor.
In your ajax callback, you receive a company:
onReceiveCompany( jsonCompany : any )
{
let newCompany = new Company( jsonCompany );
// call the methods on your newCompany object ...
}
In in order to to make that work:
1) Add a constructor in your Typescript class that takes the json data as parameter. In that constructor you extend your json object with jQuery, like this: $.extend( this, jsonData). $.extend allows keeping the javascript prototypes while adding the json object's properties.
2) Note you will have to do the same for linked objects. In the case of Employees in the example, you also create a constructor taking the portion of the json data for employees. You call $.map to translate json employees to typescript Employee objects.
export class Company
{
Employees : Employee[];
constructor( jsonData: any )
{
$.extend( this, jsonData);
if ( jsonData.Employees )
this.Employees = $.map( jsonData.Employees , (emp) => {
return new Employee ( emp ); });
}
}
export class Employee
{
name: string;
salary: number;
constructor( jsonData: any )
{
$.extend( this, jsonData);
}
}
This is the best solution I found when dealing with Typescript classes and json objects.
In my case it works. I used functions
Object.assign (target, sources ...).
First, the creation of the correct object, then copies the data from json object to the target.Example :
let u:User = new User();
Object.assign(u , jsonUsers);
And a more advanced example of use. An example using the array.
this.someService.getUsers().then((users: User[]) => {
this.users = [];
for (let i in users) {
let u:User = new User();
Object.assign(u , users[i]);
this.users[i] = u;
console.log("user:" + this.users[i].id);
console.log("user id from function(test it work) :" + this.users[i].getId());
}
});
export class User {
id:number;
name:string;
fullname:string;
email:string;
public getId(){
return this.id;
}
}
While it is not casting per se; I have found https://github.com/JohnWhiteTB/TypedJSON to be a useful alternative.
#JsonObject
class Person {
#JsonMember
firstName: string;
#JsonMember
lastName: string;
public getFullname() {
return this.firstName + " " + this.lastName;
}
}
var person = TypedJSON.parse('{ "firstName": "John", "lastName": "Doe" }', Person);
person instanceof Person; // true
person.getFullname(); // "John Doe"
Personally I find it appalling that typescript does not allow an endpoint definition to specify
the type of the object being received. As it appears that this is indeed the case,
I would do what I have done with other languages, and that is that I would separate the JSON object from the class definition,
and have the class definition use the JSON object as its only data member.
I despise boilerplate code, so for me it is usually a matter of getting to the desired result with the least amount of code while preserving type.
Consider the following JSON object structure definitions - these would be what you would receive at an endpoint, they are structure definitions only, no methods.
interface IAddress {
street: string;
city: string;
state: string;
zip: string;
}
interface IPerson {
name: string;
address: IAddress;
}
If we think of the above in object oriented terms, the above interfaces are not classes because they only define a data structure.
A class in OO terms defines data and the code that operates on it.
So we now define a class that specifies data and the code that operates on it...
class Person {
person: IPerson;
constructor(person: IPerson) {
this.person = person;
}
// accessors
getName(): string {
return person.name;
}
getAddress(): IAddress {
return person.address;
}
// You could write a generic getter for any value in person,
// no matter how deep, by accepting a variable number of string params
// methods
distanceFrom(address: IAddress): float {
// Calculate distance from the passed address to this persons IAddress
return 0.0;
}
}
And now we can simply pass in any object conforming to the IPerson structure and be on our way...
Person person = new Person({
name: "persons name",
address: {
street: "A street address",
city: "a city",
state: "a state",
zip: "A zipcode"
}
});
In the same fashion we can now process the object received at your endpoint with something along the lines of...
Person person = new Person(req.body); // As in an object received via a POST call
person.distanceFrom({ street: "Some street address", etc.});
This is much more performant and uses half the memory of copying the data, while significantly reducing the amount of boilerplate code you must write for each entity type.
It simply relies on the type safety provided by TypeScript.
Use a class extended from an interface.
Then:
Object.assign(
new ToWhat(),
what
)
And best:
Object.assign(
new ToWhat(),
<IDataInterface>what
)
ToWhat becomes a controller of DataInterface
If you need to cast your json object to a typescript class and have its instance methods available in the resulting object you need to use Object.setPrototypeOf, like I did in the code snippet bellow:
Object.setPrototypeOf(jsonObject, YourTypescriptClass.prototype)
Use 'as' declaration:
const data = JSON.parse(response.data) as MyClass;
An old question with mostly correct, but not very efficient answers. This what I propose:
Create a base class that contains init() method and static cast methods (for a single object and an array). The static methods could be anywhere; the version with the base class and init() allows easy extensions afterwards.
export class ContentItem {
// parameters: doc - plain JS object, proto - class we want to cast to (subclass of ContentItem)
static castAs<T extends ContentItem>(doc: T, proto: typeof ContentItem): T {
// if we already have the correct class skip the cast
if (doc instanceof proto) { return doc; }
// create a new object (create), and copy over all properties (assign)
const d: T = Object.create(proto.prototype);
Object.assign(d, doc);
// reason to extend the base class - we want to be able to call init() after cast
d.init();
return d;
}
// another method casts an array
static castAllAs<T extends ContentItem>(docs: T[], proto: typeof ContentItem): T[] {
return docs.map(d => ContentItem.castAs(d, proto));
}
init() { }
}
Similar mechanics (with assign()) have been mentioned in #Adam111p post. Just another (more complete) way to do it. #Timothy Perez is critical of assign(), but imho it is fully appropriate here.
Implement a derived (the real) class:
import { ContentItem } from './content-item';
export class SubjectArea extends ContentItem {
id: number;
title: string;
areas: SubjectArea[]; // contains embedded objects
depth: number;
// method will be unavailable unless we use cast
lead(): string {
return '. '.repeat(this.depth);
}
// in case we have embedded objects, call cast on them here
init() {
if (this.areas) {
this.areas = ContentItem.castAllAs(this.areas, SubjectArea);
}
}
}
Now we can cast an object retrieved from service:
const area = ContentItem.castAs<SubjectArea>(docFromREST, SubjectArea);
All hierarchy of SubjectArea objects will have correct class.
A use case/example; create an Angular service (abstract base class again):
export abstract class BaseService<T extends ContentItem> {
BASE_URL = 'http://host:port/';
protected abstract http: Http;
abstract path: string;
abstract subClass: typeof ContentItem;
cast(source: T): T {
return ContentItem.castAs(source, this.subClass);
}
castAll(source: T[]): T[] {
return ContentItem.castAllAs(source, this.subClass);
}
constructor() { }
get(): Promise<T[]> {
const value = this.http.get(`${this.BASE_URL}${this.path}`)
.toPromise()
.then(response => {
const items: T[] = this.castAll(response.json());
return items;
});
return value;
}
}
The usage becomes very simple; create an Area service:
#Injectable()
export class SubjectAreaService extends BaseService<SubjectArea> {
path = 'area';
subClass = SubjectArea;
constructor(protected http: Http) { super(); }
}
get() method of the service will return a Promise of an array already cast as SubjectArea objects (whole hierarchy)
Now say, we have another class:
export class OtherItem extends ContentItem {...}
Creating a service that retrieves data and casts to the correct class is as simple as:
#Injectable()
export class OtherItemService extends BaseService<OtherItem> {
path = 'other';
subClass = OtherItem;
constructor(protected http: Http) { super(); }
}
You can create an interface of your type (SomeType) and cast the object in that.
const typedObject: SomeType = <SomeType> responseObject;
FOR JAVA LOVERS
Make POJO class
export default class TransactionDTO{
constructor() {
}
}
create empty object by class
let dto = new TransactionDto() // ts object
let json = {name:"Kamal",age:40} // js object
let transaction: TransactionDto = Object.assign(dto,JSON.parse(JSON.stringify(json)));//conversion
https://jvilk.com/MakeTypes/
you can use this site to generate a proxy for you. it generates a class and can parse and validate your input JSON object.
I used this library here: https://github.com/pleerock/class-transformer
<script lang="ts">
import { plainToClass } from 'class-transformer';
</script>
Implementation:
private async getClassTypeValue() {
const value = await plainToClass(ProductNewsItem, JSON.parse(response.data));
}
Sometimes you will have to parse the JSON values for plainToClass to understand that it is a JSON formatted data
In the lates TS you can do like this:
const isMyInterface = (val: any): val is MyInterface => {
if (!val) { return false; }
if (!val.myProp) { return false; }
return true;
};
And than user like this:
if (isMyInterface(data)) {
// now data will be type of MyInterface
}
I ran into a similar need.
I wanted something that will give me easy transformation from/to JSON
that is coming from a REST api call to/from specific class definition.
The solutions that I've found were insufficient or meant to rewrite my
classes' code and adding annotations or similars.
I wanted something like GSON is used in Java to serialize/deserialize classes to/from JSON objects.
Combined with a later need, that the converter will function in JS as well, I ended writing my own package.
It has though, a little bit of overhead. But when started it is very convenient in adding and editing.
You initialize the module with :
conversion schema - allowing to map between fields and determine
how the conversion will be done
Classes map array
Conversion functions map - for special conversions.
Then in your code, you use the initialized module like :
const convertedNewClassesArray : MyClass[] = this.converter.convert<MyClass>(jsonObjArray, 'MyClass');
const convertedNewClass : MyClass = this.converter.convertOneObject<MyClass>(jsonObj, 'MyClass');
or , to JSON :
const jsonObject = this.converter.convertToJson(myClassInstance);
Use this link to the npm package and also a detailed explanation to how to work with the module: json-class-converter
Also wrapped it for
Angular use in :
angular-json-class-converter
Pass the object as is to the class constructor; No conventions or checks
interface iPerson {
name: string;
age: number;
}
class Person {
constructor(private person: iPerson) { }
toString(): string {
return this.person.name + ' is ' + this.person.age;
}
}
// runs this as //
const object1 = { name: 'Watson1', age: 64 };
const object2 = { name: 'Watson2' }; // age is missing
const person1 = new Person(object1);
const person2 = new Person(object2 as iPerson); // now matches constructor
console.log(person1.toString()) // Watson1 is 64
console.log(person2.toString()) // Watson2 is undefined
You can use this npm package. https://www.npmjs.com/package/class-converter
It is easy to use, for example:
class UserModel {
#property('i')
id: number;
#property('n')
name: string;
}
const userRaw = {
i: 1234,
n: 'name',
};
// use toClass to convert plain object to class
const userModel = toClass(userRaw, UserModel);
// you will get a class, just like below one
// const userModel = {
// id: 1234,
// name: 'name',
// }
You can with a single tapi.js!
It's a lightweight automapper that works in both ways.
npm i -D tapi.js
Then you can simply do
let typedObject = new YourClass().fromJSON(jsonData)
or with promises
axios.get(...).as(YourClass).then(typedObject => { ... })
You can read more about it on the docs.
There are several ways to do it, lets examine a some options:
class Person {
id: number | undefined;
firstName: string | undefined;
//? mark for note not required attribute.
lastName?: string;
}
// Option 1: Fill any attribute and it would be accepted.
const person1= { firstName: 'Cassio' } as Person ;
console.log(person1);
// Option 2. All attributes must assign data.
const person2: Person = { id: 1, firstName: 'Cassio', lastName:'Seffrin' };
console.log(person2);
// Option 3. Use partial interface if all attribute not required.
const person3: Partial<Person> = { firstName: 'Cassio' };
console.log(person3);
// Option 4. As lastName is optional it will work
const person4: Person = { id:2, firstName: 'Cassio' };
console.log(person4);
// Option 5. Fill any attribute and it would be accepted.
const person5 = <Person> {firstName: 'Cassio'};
console.log(person5 );
Result:
[LOG]: {
"firstName": "Cassio"
}
[LOG]: {
"id": 1,
"firstName": "Cassio",
"lastName": "Seffrin"
}
[LOG]: {
"firstName": "Cassio"
}
[LOG]: {
"id": 2,
"firstName": "Cassio"
}
[LOG]: {
"firstName": "Cassio"
}
It will also work if you have an interface instead a Typescript class.
interface PersonInterface {
id: number;
firstName: string;
lastName?: string;
}
Play this code
I think that json2typescript is a good alternative
https://www.npmjs.com/package/json2typescript
You can convert json to Class model with a simple model class with annotations
Used in project
You can cast json to property like this
class Jobs {
constructor(JSONdata) {
this.HEAT = JSONdata.HEAT;
this.HEAT_EAF = JSONdata.HEAT_EAF;
}
}
var job = new Jobs({HEAT:'123',HEAT_EAF:'456'});
This is a simple and a really good option
let person = "{"name":"Sam","Age":"30"}";
const jsonParse: ((key: string, value: any) => any) | undefined = undefined;
let objectConverted = JSON.parse(textValue, jsonParse);
And then you'll have
objectConverted.name

typescript getter / setter on array property

Is there a good way to use getter / setter pattern for array properties?
For example:
export class User {
private _name: string;
set name(value: string) {
this._name = value;
}
get name(): string {
return this._name;
}
private _roles = new Array<string>();
set roles(value: Array<string>) {
this._roles = value;
}
get roles(): Array<string> {
return this._roles;
}
constructor() {
}
}
While changing user.name fires the setter method, adding or removing items from roles does not.
Now i think i understand why it does not fire the setter, because adding items to the array does not change the pointer but merely adds to the already allocated space (correct me if i'm wrong).
How can we get the desired getter / setter behaviour on array properties?
As you said doing something like user.roles.push('my-role') will merely mutate the existing array. Instead of giving direct access to the array through the roles-setter, you could add methods like addRole and removeRole. Then you can implement whatever logic you need when adding or removing to the roles array, keeping it totally private.

what is purpose of bind in reactJS [duplicate]

What is the use of bind() in JavaScript?
Bind creates a new function that will force the this inside the function to be the parameter passed to bind().
Here's an example that shows how to use bind to pass a member method around that has the correct this:
var myButton = {
content: 'OK',
click() {
console.log(this.content + ' clicked');
}
};
myButton.click();
var looseClick = myButton.click;
looseClick(); // not bound, 'this' is not myButton - it is the globalThis
var boundClick = myButton.click.bind(myButton);
boundClick(); // bound, 'this' is myButton
Which prints out:
OK clicked
undefined clicked
OK clicked
You can also add extra parameters after the 1st (this) parameter and bind will pass in those values to the original function. Any additional parameters you later pass to the bound function will be passed in after the bound parameters:
// Example showing binding some parameters
var sum = function(a, b) {
return a + b;
};
var add5 = sum.bind(null, 5);
console.log(add5(10));
Which prints out:
15
Check out JavaScript Function bind for more info and interactive examples.
Update: ECMAScript 2015 adds support for => functions. => functions are more compact and do not change the this pointer from their defining scope, so you may not need to use bind() as often. For example, if you wanted a function on Button from the first example to hook up the click callback to a DOM event, the following are all valid ways of doing that:
var myButton = {
... // As above
hookEvent(element) {
// Use bind() to ensure 'this' is the 'this' inside click()
element.addEventListener('click', this.click.bind(this));
}
};
Or:
var myButton = {
... // As above
hookEvent(element) {
// Use a new variable for 'this' since 'this' inside the function
// will not be the 'this' inside hookEvent()
var me = this;
element.addEventListener('click', function() { me.click() });
}
};
Or:
var myButton = {
... // As above
hookEvent(element) {
// => functions do not change 'this', so you can use it directly
element.addEventListener('click', () => this.click());
}
};
The simplest use of bind() is to make a function that, no matter
how it is called, is called with a particular this value.
x = 9;
var module = {
x: 81,
getX: function () {
return this.x;
}
};
module.getX(); // 81
var getX = module.getX;
getX(); // 9, because in this case, "this" refers to the global object
// create a new function with 'this' bound to module
var boundGetX = getX.bind(module);
boundGetX(); // 81
Please refer to this link on MDN Web Docs for more information:
Function.prototype.bind()
bind allows-
set the value of "this" to an specific object. This becomes very helpful as sometimes this is not what is intended.
reuse methods
curry a function
For example, you have a function to deduct monthly club fees
function getMonthlyFee(fee){
var remaining = this.total - fee;
this.total = remaining;
return this.name +' remaining balance:'+remaining;
}
Now you want to reuse this function for a different club member. Note that the monthly fee will vary from member to member.
Let's imagine Rachel has a balance of 500, and a monthly membership fee of 90.
var rachel = {name:'Rachel Green', total:500};
Now, create a function that can be used again and again to deduct the fee from her account every month
//bind
var getRachelFee = getMonthlyFee.bind(rachel, 90);
//deduct
getRachelFee();//Rachel Green remaining balance:410
getRachelFee();//Rachel Green remaining balance:320
Now, the same getMonthlyFee function could be used for another member with a different membership fee. For Example, Ross Geller has a 250 balance and a monthly fee of 25
var ross = {name:'Ross Geller', total:250};
//bind
var getRossFee = getMonthlyFee.bind(ross, 25);
//deduct
getRossFee(); //Ross Geller remaining balance:225
getRossFee(); //Ross Geller remaining balance:200
From the MDN docs on Function.prototype.bind() :
The bind() method creates a new function that, when called, has its
this keyword set to the provided value, with a given sequence of
arguments preceding any provided when the new function is called.
So, what does that mean?!
Well, let's take a function that looks like this :
var logProp = function(prop) {
console.log(this[prop]);
};
Now, let's take an object that looks like this :
var Obj = {
x : 5,
y : 10
};
We can bind our function to our object like this :
Obj.log = logProp.bind(Obj);
Now, we can run Obj.log anywhere in our code :
Obj.log('x'); // Output : 5
Obj.log('y'); // Output : 10
This works, because we bound the value of this to our object Obj.
Where it really gets interesting, is when you not only bind a value for this, but also for its argument prop :
Obj.logX = logProp.bind(Obj, 'x');
Obj.logY = logProp.bind(Obj, 'y');
We can now do this :
Obj.logX(); // Output : 5
Obj.logY(); // Output : 10
Unlike with Obj.log, we do not have to pass x or y, because we passed those values when we did our binding.
Variables has local and global scopes. Let's suppose that we have two variables with the same name. One is globally defined and the other is defined inside a function closure and we want to get the variable value which is inside the function closure. In that case we use this bind() method. Please see the simple example below:
var x = 9; // this refers to global "window" object here in the browser
var person = {
x: 81,
getX: function() {
return this.x;
}
};
var y = person.getX; // It will return 9, because it will call global value of x(var x=9).
var x2 = y.bind(person); // It will return 81, because it will call local value of x, which is defined in the object called person(x=81).
document.getElementById("demo1").innerHTML = y();
document.getElementById("demo2").innerHTML = x2();
<p id="demo1">0</p>
<p id="demo2">0</p>
Summary:
The bind() method takes an object as an first argument and creates a new function. When the function is invoked the value of this in the function body will be the object which was passed in as an argument in the bind() function.
How does this work in JS anyway
The value of this in javascript is dependent always depends on what Object the function is called. The value of this always refers to the object left of the dot from where is the function is called. In case of the global scope this is window (or global in nodeJS). Only call, apply and bind can alter the this binding differently. Here is an example to show how the this keyword works:
let obj = {
prop1: 1,
func: function () { console.log(this); }
}
obj.func(); // obj left of the dot so this refers to obj
const customFunc = obj.func; // we store the function in the customFunc obj
customFunc(); // now the object left of the dot is window,
// customFunc() is shorthand for window.customFunc()
// Therefore window will be logged
How is bind used?
Bind can help in overcoming difficulties with the this keyword by having a fixed object where this will refer to. For example:
var name = 'globalName';
const obj = {
name: 'myName',
sayName: function () { console.log(this.name);}
}
const say = obj.sayName; // we are merely storing the function the value of this isn't magically transferred
say(); // now because this function is executed in global scope this will refer to the global var
const boundSay = obj.sayName.bind(obj); // now the value of this is bound to the obj object
boundSay(); // Now this will refer to the name in the obj object: 'myName'
Once the function is bound to a particular this value we can pass it around and even put it on properties on other objects. The value of this will remain the same.
The bind() method creates a new function instance whose this value is bound to the value that was passed into bind().
For example:
window.color = "red";
var o = { color: "blue" };
function sayColor(){
alert(this.color);
}
var objectSayColor = sayColor.bind(o);
objectSayColor(); //blue
Here, a new function called objectSayColor() is created from sayColor() by calling bind() and passing in the object o. The objectSayColor() function has a this value equivalent to o, so calling the function, even as a global call, results in the string “blue” being displayed.
Reference : Nicholas C. Zakas - PROFESSIONAL JAVASCRIPT® FOR WEB DEVELOPERS
I will explain bind theoretically as well as practically
bind in javascript is a method -- Function.prototype.bind . bind is a method. It is called on function prototype. This method creates a function whose body is similar to the function on which it is called but the 'this' refers to the first parameter passed to the bind method. Its syntax is
var bindedFunc = Func.bind(thisObj,optionsArg1,optionalArg2,optionalArg3,...);
Example:--
var checkRange = function(value){
if(typeof value !== "number"){
return false;
}
else {
return value >= this.minimum && value <= this.maximum;
}
}
var range = {minimum:10,maximum:20};
var boundedFunc = checkRange.bind(range); //bounded Function. this refers to range
var result = boundedFunc(15); //passing value
console.log(result) // will give true;
Creating a new Function by Binding Arguments to Values
The bind method creates a new function from another function with one or more arguments bound to specific values, including the implicit this argument.
Partial Application
This is an example of partial application. Normally we supply a function with all of its arguments which yields a value. This is known as function application. We are applying the function to its arguments.
A Higher Order Function (HOF)
Partial application is an example of a higher order function (HOF) because it yields a new function with a fewer number of argument.
Binding Multiple Arguments
You can use bind to transform functions with multiple arguments into new functions.
function multiply(x, y) {
return x * y;
}
let multiplyBy10 = multiply.bind(null, 10);
console.log(multiplyBy10(5));
Converting from Instance Method to Static Function
In the most common use case, when called with one argument the bind method will create a new function that has the this value bound to a specific value. In effect this transforms an instance method to a static method.
function Multiplier(factor) {
this.factor = factor;
}
Multiplier.prototype.multiply = function(x) {
return this.factor * x;
}
function ApplyFunction(func, value) {
return func(value);
}
var mul = new Multiplier(5);
// Produces garbage (NaN) because multiplying "undefined" by 10
console.log(ApplyFunction(mul.multiply, 10));
// Produces expected result: 50
console.log(ApplyFunction(mul.multiply.bind(mul), 10));
Implementing a Stateful CallBack
The following example shows how using binding of this can enable an object method to act as a callback that can easily update the state of an object.
function ButtonPressedLogger()
{
this.count = 0;
this.onPressed = function() {
this.count++;
console.log("pressed a button " + this.count + " times");
}
for (let d of document.getElementsByTagName("button"))
d.onclick = this.onPressed.bind(this);
}
new ButtonPressedLogger();
<button>press me</button>
<button>no press me</button>
As mentioned, Function.bind() lets you specify the context that the function will execute in (that is, it lets you pass in what object the this keyword will resolve to in the body of the function.
A couple of analogous toolkit API methods that perform a similar service:
jQuery.proxy()
Dojo.hitch()
Bind Method
A bind implementation might look something like so:
Function.prototype.bind = function () {
const self = this;
const args = [...arguments];
const context = args.shift();
return function () {
return self.apply(context, args.concat([...arguments]));
};
};
The bind function can take any number of arguments and return a new function.
The new function will call the original function using the JS Function.prototype.apply method.The apply method will use the first argument passed to the target function as its context (this), and the second array argument of the apply method will be a combination of the rest of the arguments from the target function, concat with the arguments used to call the return function (in that order).
An example can look something like so:
function Fruit(emoji) {
this.emoji = emoji;
}
Fruit.prototype.show = function () {
console.log(this.emoji);
};
const apple = new Fruit('🍎');
const orange = new Fruit('🍊');
apple.show(); // 🍎
orange.show(); // 🍊
const fruit1 = apple.show;
const fruit2 = apple.show.bind();
const fruit3 = apple.show.bind(apple);
const fruit4 = apple.show.bind(orange);
fruit1(); // undefined
fruit2(); // undefined
fruit3(); // 🍎
fruit4(); // 🍊
/**
* Bind is a method inherited from Function.prototype same like call and apply
* It basically helps to bind a function to an object's context during initialisation
*
* */
window.myname = "Jineesh";
var foo = function(){
return this.myname;
};
//IE < 8 has issues with this, supported in ecmascript 5
var obj = {
myname : "John",
fn:foo.bind(window)// binds to window object
};
console.log( obj.fn() ); // Returns Jineesh
Consider the Simple Program listed below,
//we create object user
let User = { name: 'Justin' };
//a Hello Function is created to Alert the object User
function Hello() {
alert(this.name);
}
//since there the value of this is lost we need to bind user to use this keyword
let user = Hello.bind(User);
user();
//we create an instance to refer the this keyword (this.name);
Simple Explanation:
bind() create a new function, a new reference at a function it returns to you.
In parameter after this keyword, you pass in the parameter you want to preconfigure. Actually it does not execute immediately, just prepares for execution.
You can preconfigure as many parameters as you want.
Simple Example to understand bind:
function calculate(operation) {
if (operation === 'ADD') {
alert('The Operation is Addition');
} else if (operation === 'SUBTRACT') {
alert('The Operation is Subtraction');
}
}
addBtn.addEventListener('click', calculate.bind(this, 'ADD'));
subtractBtn.addEventListener('click', calculate.bind(this, 'SUBTRACT'));
The bind function creates a new function with the same function body as the function it is calling .It is called with the this argument .why we use bind fun. : when every time a new instance is created and we have to use first initial instance then we use bind fun.We can't override the bind fun.simply it stores the initial object of the class.
setInterval(this.animate_to.bind(this), 1000/this.difference);
function.prototype.bind() accepts an Object.
It binds the calling function to the passed Object and the returns
the same.
When an object is bound to a function, it means you will be able to
access the values of that object from within the function using
'this' keyword.
It can also be said as,
function.prototype.bind() is used to provide/change the context of a
function.
let powerOfNumber = function(number) {
let product = 1;
for(let i=1; i<= this.power; i++) {
product*=number;
}
return product;
}
let powerOfTwo = powerOfNumber.bind({power:2});
alert(powerOfTwo(2));
let powerOfThree = powerOfNumber.bind({power:3});
alert(powerOfThree(2));
let powerOfFour = powerOfNumber.bind({power:4});
alert(powerOfFour(2));
Let us try to understand this.
let powerOfNumber = function(number) {
let product = 1;
for (let i = 1; i <= this.power; i++) {
product *= number;
}
return product;
}
Here, in this function, this corresponds to the object bound to the function powerOfNumber. Currently we don't have any function that is bound to this function.
Let us create a function powerOfTwo which will find the second power of a number using the above function.
let powerOfTwo = powerOfNumber.bind({power:2});
alert(powerOfTwo(2));
Here the object {power : 2} is passed to powerOfNumber function using bind.
The bind function binds this object to the powerOfNumber() and returns the below function to powerOfTwo. Now, powerOfTwo looks like,
let powerOfNumber = function(number) {
let product = 1;
for(let i=1; i<=2; i++) {
product*=number;
}
return product;
}
Hence, powerOfTwo will find the second power.
Feel free to check this out.
bind() function in Javascript
The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.
An example for the first part
grabbed from react package useSt8
import { useState } from "react"
function st8() {
switch(arguments.length) {
case 0: return this[0]
case 1: return void this[1](arguments[0])
default: throw new Error("Expected 0 or 1 arguments")
}
}
function useSt8(initial) {
// this in st8 will be something like [state, setSatate]
return st8.bind(useState(initial))
}
// usage
function Counter() {
const count = useSt8(0);
return (
<>
Count: {count()}
<button onClick={() => count(0)}>Reset</button>
<button onClick={() => count(prevCount => prevCount + 1)}>inc</button>
</>
);
}
An example for the second part
const add = (a, b) => a+b
someThis = this
// new function with this value equal to someThis
add5 = add.bind(someThis, 5)
add5(10) // 15
// we don't use this in add decelartion so this will work too.
add10 = add.bind(null, 10)
add10(5) // 15
Here's the simplest possible explanation:
Say you have a function
function _loop(n) { console.log("so: " + n) }
obviously you can call it like _loop(69) as usual.
Rewrite like this:
var _loop = function() { console.log("so: " + this.n) }
Notice there are now
no arguments as such
you use "this. " to get to the named arguments
You can now call the function like this:
_loop.bind( {"n": 420} )
That's it.
Most typical use case:
A really typical use is when you need to add an argument to a callback.
Callbacks can't have arguments.
So just "rewrite" the callback as above.
Simple example
function lol(second, third) {
console.log(this.first, second, third);
}
lol(); // undefined, undefined, undefined
lol('1'); // undefined, "1", undefined
lol('1', '2'); // undefined, "1", "2"
lol.call({first: '1'}); // "1", undefined, undefined
lol.call({first: '1'}, '2'); // "1", "2", undefined
lol.call({first: '1'}, '2', '3'); // "1", "2", "3"
lol.apply({first: '1'}); // "1", undefined, undefined
lol.apply({first: '1'}, ['2', '3']); // "1", "2", "3"
const newLol = lol.bind({first: '1'});
newLol(); // "1", undefined, undefined
newLol('2'); // "1", "2", undefined
newLol('2', '3'); // "1", "2", "3"
const newOmg = lol.bind({first: '1'}, '2');
newOmg(); // "1", "2", undefined
newOmg('3'); // "1", "2", "3"
const newWtf = lol.bind({first: '1'}, '2', '3');
newWtf(); // "1", "2", "3"
Another usage is that you can pass binded function as an argument to another function which is operating under another execution context.
var name = "sample";
function sample(){
console.log(this.name);
}
var cb = sample.bind(this);
function somefunction(cb){
//other code
cb();
}
somefunction.call({}, cb);
In addition to what have been said, the bind() method allows an object to borrow a method from another object without making a copy of that method. This is known as function borrowing in JavaScript.
i did not read above code but i learn something in simple so want to share here about bind method after bind method we can use it as any normal method.
<pre> note: do not use arrow function it will show error undefined </pre>
let solarSystem = {
sun: 'red',
moon : 'white',
sunmoon : function(){
let dayNight = this.sun + ' is the sun color and present in day and '+this.moon + ' is the moon color and prenet in night';
return dayNight;
}
}
let work = function(work,sleep){
console.log(this.sunmoon()); // accessing the solatSystem it show error undefine sunmmon untill now because we can't access directly for that we use .bind()
console.log('i work in '+ work +' and sleep in '+sleep);
}
let outPut = work.bind(solarSystem);
outPut('day','night')
bind is a function which is available in java script prototype, as the name suggest bind is used to bind your function call to the context whichever you are dealing with for eg:
var rateOfInterest='4%';
var axisBank=
{
rateOfInterest:'10%',
getRateOfInterest:function()
{
return this.rateOfInterest;
}
}
axisBank.getRateOfInterest() //'10%'
let knowAxisBankInterest=axisBank.getRateOfInterest // when you want to assign the function call to a varaible we use this syntax
knowAxisBankInterest(); // you will get output as '4%' here by default the function is called wrt global context
let knowExactAxisBankInterest=knowAxisBankInterest.bind(axisBank); //so here we need bind function call to its local context
knowExactAxisBankInterest() // '10%'

How to pass the ng-model into a function instead of ng-model's value?

I want to create a single function that will be used for total project.
It will work based on the ng-model passed into it.
For ex:-
$scope.checkBoxValueChanged=function(model) {
if($scope.model=="AA") {
$scope.model="BB";
}
else {
$scope.model="BB";
}
};
});
If i have the passed model's value as "AA" then i need to assign the passed model's value as "BB"
But what i am getting is the model value instead of model name.
Can anyone tell me how to get the model instead of model value.
Any help will be highly appreciated.
You should pass the property name (what you refer to as "model") as a string parameter.
Then you can access it using the object[key] syntax.
$scope.checkBoxValueChanged = function(propName) {
if($scope[propName] === 'AA') {
$scope[propName] = 'BB';
} else {
$scope[propName] = 'AA';
}
};
BTW, in JS, scope.model is equivalent to scope['model'], so the dot syntax won't work as you want it to.

Proper way to sort a backbone.js collection on the fly

I can successfully do this:
App.SomeCollection = Backbone.Collection.extend({
comparator: function( collection ){
return( collection.get( 'lastName' ) );
}
});
Which is nice if I want to have a collection that is only sorted by 'lastName'. But I need to have this sorting done dynamically. Sometimes, I'll need to sort by, say, 'firstName' instead.
My utter failures include:
I tried passing an extra variable specifying the variable to sort() on. That did not work. I also tried sortBy(), which did not work either. I tried passing my own function to sort(), but this did not work either. Passing a user-defined function to sortBy() only to have the result not have an each method, defeating the point of having a newly sorted backbone collection.
Can someone provide a practical example of sorting by a variable that is not hard coded into the comparator function? Or any hack you have that works? If not, a working sortBy() call?
Interesting question. I would try a variant on the strategy pattern here. You could create a hash of sorting functions, then set comparator based on the selected member of the hash:
App.SomeCollection = Backbone.Collection.extend({
comparator: strategies[selectedStrategy],
strategies: {
firstName: function () { /* first name sorting implementation here */ },
lastName: function () { /* last name sorting implementation here */ },
},
selectedStrategy: "firstName"
});
Then you could change your sorting strategy on the fly by updating the value of the selectedStrategy property.
EDIT: I realized after I went to bed :) that this wouldn't quite work as I wrote it above, because we're passing an object literal to Collection.extend. The comparator property will be evaluated once, when the object is created, so it won't change on the fly unless forced to do so. There is probably a cleaner way to do this, but this demonstrates switching the comparator functions on the fly:
var SomeCollection = Backbone.Collection.extend({
comparator: function (property) {
return selectedStrategy.apply(myModel.get(property));
},
strategies: {
firstName: function (person) { return person.get("firstName"); },
lastName: function (person) { return person.get("lastName"); },
},
changeSort: function (sortProperty) {
this.comparator = this.strategies[sortProperty];
},
initialize: function () {
this.changeSort("lastName");
console.log(this.comparator);
this.changeSort("firstName");
console.log(this.comparator);
}
});
var myCollection = new SomeCollection;
Here's a jsFiddle that demonstrates this.
The root of all of your problems, I think, is that properties on JavaScript object literals are evaluated immediately when the object is created, so you have to overwrite the property if you want to change it. If you try to write some kind of switching into the property itself it'll get set to an initial value and stay there.
Here's a good blog post that discusses this in a slightly different context.
Change to comparator function by assigning a new function to it and call sort.
// Following example above do in the view:
// Assign new comparator
this.collection.comparator = function( model ) {
return model.get( 'lastname' );
}
// Resort collection
this.collection.sort();
// Sort differently
this.collection.comparator = function( model ) {
return model.get( 'age' );
}
this.collection.sort();
So, this was my solution that actually worked.
App.Collection = Backbone.Collection.extend({
model:App.Model,
initialize: function(){
this.sortVar = 'firstName';
},
comparator: function( collection ){
var that = this;
return( collection.get( that.sortVar ) );
}
});
Then in the view, I have to M-I-C-K-E-Y M-O-U-S-E it like this:
this.collections.sortVar = 'lastVar'
this.collections.sort( this.comparator ).each( function(){
// All the stuff I want to do with the sorted collection...
});
Since Josh Earl was the only one to even attempt a solution and he did lead me in the right direction, I accept his answer. Thanks Josh :)
This is an old question but I recently had a similar need (sort a collection based on criteria to be supplied by a user click event) and thought I'd share my solution for others tackling this issue. Requires no hardcoded model.get('attribute').
I basically used Dave Newton's approach to extending native JavaScript arrays, and tailored it to Backbone:
MyCollection = Backbone.Collection.extend({
// Custom sorting function.
sortCollection : function(criteria) {
// Set your comparator function, pass the criteria.
this.comparator = this.criteriaComparator(criteria);
this.sort();
},
criteriaComparator : function(criteria, overloadParam) {
return function(a, b) {
var aSortVal = a.get(criteria);
var bSortVal = b.get(criteria);
// Whatever your sorting criteria.
if (aSortVal < bSortVal) {
return -1;
}
if (aSortVal > bSortVal) {
return 1;
}
else {
return 0;
}
};
}
});
Note the "overloadParam". Per the documentation, Backbone uses Underscore's "sortBy" if your comparator function has a single param, and a native JS-style sort if it has two params. We need the latter, hence the "overloadParam".
Looking at the source code, it seems there's a simple way to do it, setting comparator to string instead of function. This works, given Backbone.Collection mycollection:
mycollection.comparator = key;
mycollection.sort();
This is what I ended up doing for the app I'm currently working on. In my collection I have:
comparator: function(model) {
var methodName = applicationStateModel.get("comparatorMethod"),
method = this[methodName];
if (typeof(method === "function")) {
return method.call(null, model);
}
}
Now I can add few different methods to my collection: fooSort(), barSort(), and bazSort().
I want fooSort to be the default so I set that in my state model like so:
var ApplicationState = Backbone.Model.extend({
defaults: {
comparatorMethod: "fooSort"
}
});
Now all I have to do is write a function in my view that updates the value of "comparatorMethod" depending upon what the user clicks. I set the collection to listen to those changes and do sort(), and I set the view to listen for sort events and do render().
BAZINGA!!!!

Resources