Fun with JavaScript Arrays

The first thing I usually run into when trying new programming languages are some weird corner case behaviors, so I decided to run into those on purpose and write one of my first blog posts about that.

Couple of notes before we begin:

  • I've tried all those examples in Google Chrome Console - command+option+I on Mac and hopefully similar keys on Win/Linux
  • When reading this article you might need to stop and think. Stop and Think!
  • Also keep in mind that sometimes I'm playing stupid, but sometimes I'm actually stupid.

// Let's say we have an array with three elements, this can't hurt
var [123];
a
[123]

// Ok, looks good
// Let's add something interesting
a[-14;
// Has this array changed?
a
[123]

// No?
// What about that element
a[-1]
4

// Hmm, it's there
// So what happens when we try something crazy
a[NaN5;
a
[123]

// Whoaaa

// Let's try couple of others
a[-Infinity"P";
a[-Infinity+3"NP";
// No error yet, so let's do a little check
a[-Infinity=== a[-Infinity+3]
true

// Did I just prove that P=NP? Never-mind, we shouldn't get distracted
// by unimportant problems. So how could I see what's inside a? console.log!
console.log(a);
[123-14NaN5-Infinity"NP"]

// Hmm, this kinda looks like it's messed up array with object
// So let's check
typeof a
"object"

// Type of array is object? Really?
typeof []
"object"

// Indeed it is, I almost forgot about this ~'Good Part'
// Now that I have entertained myself... inverting string bitwise, heh...
// Actually, what is the result?
~'Good Part'
-1

// Yea... 
// But let's get back to adding wrong things to array
a[function]='err';
SyntaxErrorUnexpected token ]

// Ok, that is actually wrong, so something else
a[typeof console.log"duh";
a['~''<';
a[a[typeof a]='huh'void a'puh';
a[{do'break'}'thanks';
a.continue ["-",+"you"+"","+"][3,2,1][2];
a.return function(p)return p;};

// I'm starting to feel bad about abusing this little array a so much. 
// Are you ok little fella?
a
[123]

// Like nothing had happened
// So what about operators?
a[a|a3;
a[-~a&-~a2;
a[-~-~!a1
a
[321]

// Good, good, nicely reversed
a['!'-1]
5
// Whoaa? Dude?
'!'-1
NaN

// Oh yeah, I forgot about that I already have NaN in there
// console.log will reveal other interesting things for sure
console.log(a);
[  
  -Infinity"NP",
  03,
  12,
  21,
  -14,
  NaN5,
  [object Object]"thanks",
  continue"N",
  function"duh",
  length3,
  object"huh",
  returnfunction (p)return p;},
  undefined"puh",
  ~"<"
]

// Hmm, there are couple of nice ones 
a.length 2;
a
[32]

// Sorry for that
a[{}];
"thanks"
a['[object Object]'"you're welcome";

// Also, where did that continue: "N" come from? Was that from example with N=NP?
// Not really... that actually reminds me of one episode from my famous 
// "JavaScript sucks" series:
// You should try this one. Just put the following into your favorite JS console:
// Or are you afraid of JavaScript? :)
(({}-1)+"")[2]+({}+[])[1]+" "+((~-1<~[0])+"")[1/(1/0)]+((/./>/^/)+"")[4]+(""+!!(3^3))[1]+(""+!!(_="$"))[1]+"!"
// ... result stripped ... I won't make it that easy for you.

// Let's reveal one of the sneaky ones 
// (that has actually nothing to do with arrays):
a[100"Never gonna \
give you up,"
;
a[101"Never gonna \ 
let you down";

SyntaxErrorUnexpected token ILLEGAL

// Wow ... I thing I have been just Rickrolled by Chrome interpreter
// and I thought he would never gonna run around and desert me
// Do you know how this happened? BroTip: Try to copypaste it into your console.

// Talking about Syntax Errors, let's say we need a function that performs
// this importing-like functionality for us. Let's call it import.
function import({}
SyntaxErrorUnexpected token import



// One thing I haven't tried yet is doing a bit of recursion in array
a.me a;
console.log(a);
[..meArray[3...// striped other previously messed up things

console.log(a.me.me.me)
[..meArray[3...]

// If you would run the same thing in Node.JS you would see more explanatory
console.log(a.me.me.me)
me[Circular]

// Let's run simple one liner to try to iterate through recursive object
for(var i=0c=ac=c.meconsole.log(i)i++);
0
1
...
26012391
...

// Ok, Node.JS is still running, no simple way to kill it. 
// What about Chrome console?
for(var i=0c=ac=c.meconsole.log(i)i++);
0
1
...
314
...
2679
...

// Chrome died. Goodbye a. See you in Silicon Heaven.

// That reminds me of good old times when I tried to view 5MB XML without 
// line breakings in Chrome, Opera, IE, Firefox...

// Let's start chrome console again with clean array
var [];
a.me a;
// Hmm I need to add some elem to array, so I can just copypaste that loop again
a[12
a
[undefined × 12]

// That's interesting - array is 'filled up' with undefineds and Chrome
// prints that out neatly

// Let's test how long this loop take
console.time('evt')for(var i=0c=ac=c.me10000console.log(i)i++)console.timeEnd('evt');
evt1552.587ms

// Let's try the same thing with object to see if there's any difference
var {};
o.me o;
console.time('evt')for(var i=0c=oc=c.me10000console.log(i)i++)console.timeEnd('evt');
evt1610.678ms

// Not really - so what to take from this: Arrays are Objects, essentially.
// And yes, proving that by executing console.log multiple times is just
// plain stupid.

// One last interesting thing
i
10000

There are not many languages where you can access index from for loop after the loop ends. JavaScript is one of them, since it uses function scopes rather than block scopes and variable declarations are hoisted to the top of a function, but about that later.

Let's stop making fun of languages that suck and let's continue with that next time. We will have a look at comparing things. Also note that I really enjoy programming in JavaScript, but that doesn't mean I cannot rant about it's bad parts. And it's good parts. And my job and everything. 

Do you like what you have just read? Do you hate it? Leave me a comment!

131 comments :

  1. OK, it's look like fun, but you should get some basics about javascript types:

    Matthias reuters articles about java-script types at united-coders.com : part1, part2, part3 and part4 are a good start or the book good parts of javascript ...

    For really fun look at WAT - A lightning talk by Gary Bernhardt from CodeMash 2012

    ReplyDelete
    Replies
    1. java-script?

      Delete
    2. Christian, thanks for links! Those are indeed really nice and well written articles and I haven't seen them before.
      And yes, Gary's WAT makes me laugh every single time :)

      Delete
  2. Very entertaining reading. I learned some things, and I was amazed (not entirely positively) by a few of your examples.

    ReplyDelete
  3. So, yeah. Javascript doesn't have many types of things: strings, numbers (which are `double`s), objects, functions, and undefined. Objects map strings to things.

    Javascript 'arrays' are just objects, but their internal setters and getters are a little different. If you want to meditate upon such things, meditate upon this:

    > function Arr() { this.length = 0; }; Arr.prototype = [];
    []
    > x = Array(); y = Arr(); [x, y]
    > x = new Array(); y = new Arr(); [x, y]
    [ [], { length: 0 } ]
    > x[1] = 'abc'; y[1] = 'abc'; [x, x.length, y, y.length]
    [ [ , 'abc' ], 2, { '1': 'abc', length: 0 }, 0 ]
    > [x.toString(), y.toString(), ({1: 'abc', length: 0}).toString()]
    [ ',abc', '', '[object Object]' ]
    > [x instanceof Array, y instanceof Array]
    [ true, true ]
    > y.length = 2
    2
    > [x.toString(), y.toString()]
    [ ',abc', ',abc' ]

    So you see that the toString() is passed over from Array to Arr in the prototype, but the special setter which notices, "oh, you're passing in a positive numerical index, I should update the length" is not passed over from Array to Arr; and the environment does not immediately recognize an Arr object as an array even though it does satisfy `instanceof Array` due to prototypical inheritance.

    Anyway, the reason why a[-1] and a[NaN] are not crazy is because they get .toString()'ed before they go in. You may have noticed that any lightweight object declared with `{}` has a toString which gives '[object Object]', which means you can do this:

    > a = {}; a[{}] = 1000; a
    { '[object Object]': 1000 }

    That's what was happening when you said a[-1]; it set a["-1"] to be a special value.

    ReplyDelete
  4. None of this proves that JavaScript sucks. All it proves is that you had preconceptions about how it should work that were wrong.

    ReplyDelete
    Replies
    1. Arguably, a programming language which does not conform to common and helpful preconceptions is a programming language that sucks, because it adds an extra cognitive layer between "this is what I want the program to do" and "this is the code I have to write"

      That said, I love JavaScript :)

      Delete
    2. Where is it claimed that this proves that javascript sucks?

      Delete
    3. If a language is more powerful in certain areas, it also means it differentiates from the norm.

      Every time you learn a new language, you have to *learn* it. A lot of people coming from PHP, Java or C# think "it's just a scripting language" and underestimates the power of JavaScript and they hit a brick wall. And that's not the fault of JavaScript, but pure laziness.

      Delete
  5. Most of this matches with WAT - A lightning talk by Gary Bernhardt from CodeMash 2012. And rest are mostly playing with properties in objects on JavaScript.

    Just that certain obvious preconceptions about the language needs to be changed.

    ReplyDelete
  6. Most of what you did was adding new properties to `a`. That's all. As long as you use for loops (not for-in) or foreach you will never iterate over those. [1]

    An array is an object just like any other, I'll give you that typeof [] is odd but, Array.isArray is what you are looking for.

    [1]: http://codepen.io/seraphzz/pen/pAHgJ

    ReplyDelete
  7. Aaaa ... javascript. Enjoyed this - looking forward to more!

    ReplyDelete
  8. You didn't prove that "P" equals "NP", in fact you just showed that, in Javascript, -Infinity == -Infinity-3.

    So, when you did:
    a[-Infinity+3] ='NP';

    a[-Infinty] were just replaced by 'NP'.

    ReplyDelete
    Replies
    1. I don't think he literally thought he solved the P=NP problem.

      Delete
    2. Of couse, I just thought it could be relevant to point it out.

      Delete
  9. Entertaining. :-)
    Why are some people being so defensive?

    ReplyDelete
    Replies
    1. I'm still happy about those responses, because they often come up with interesting sources. :)
      Maybe next time I should point out clearly that I know exactly (well, there are still things to learn) why are all those things happening.

      Delete
  10. "…sometimes I'm playing stupid, but sometimes I'm actually stupid."

    :D

    ReplyDelete
  11. This post reads like an obfuscation contest. There is only one JS oddity in here that is very simple and straightforward: that an array can be assigned arbitrary property names. The rest of the post just uses increasingly obfuscated code to create the property name.

    ReplyDelete
  12. An array is an object: there's no reason you can't assign arbitrary properties to it.

    ReplyDelete
  13. Ctrl + Shift + J for chrome

    ReplyDelete
  14. Being new to Javascript, I found this tutorial very helpful. Thank you for such a great article!

    ReplyDelete
    Replies
    1. Thanks! But please don't take this as a tutorial. Use it rather as a list of things you should avoid ;)
      Learn the real deal from books, like from Douglas Crockford's: "JavaScript: The Good Parts".

      Delete
  15. Lets complain when the browser throws errors when we use reserved words as variables! https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Reserved_Words Look at that, import is on the list.

    ReplyDelete
    Replies
    1. And do you have a post where you complain that undefined can be redefined in some browsers? lol

      Delete
  16. You're silly and you'll realize that if you spend more time using JS! :)

    Quick tips:
    * Array is just an Object.
    * The setter will check if you pass a valid index (integer >= 0) and if you don't, it will just create a property with that name on the Objects instance.
    * console.log() will only log array keys + object properties that [].propertyIsEnumerable(prop). All properties set by you on Array Objects are enumerable.
    * Using reserved keywords like `function` or `import` as identifiers is forbidden. However, you may use them as Object properties.
    You can't set a[function] but you can set a['function'] and create the `function` property which you can read using `a.function`
    `a[typeof console.log]` is same as `a['function']`, not `a[function]` since `typeof` returns a String

    * `Infinity` + anything is still `Infinity`. That's why `a[Infinity]` === `a[Infinity + 3]` (`Infinity + 3` is evaluated as `Infinity`, so you basically accessed the same Object property)

    * You need to be extra careful with the variables' scope. JS constructs like `for`, `while`, `switch` don't have their own scope. However, the problem with that `i` is even bigger. Consider this:

    // in the global scope
    i = 'Free!';
    console.log('GLOBAL: ' + i);
    // GLOBAL: Free!

    function looping() {
    for (i = 0; i < 10; i++) {
    console.log('FUNCTION: ' + i);
    }
    }

    looping();
    // FUNCTION: 0
    // FUNCTION: 1
    // ...
    // FUNCTION: 9

    console.log('GLOBAL: ' + i);
    // GLOBAL: 10

    So, if you don't explicitly define the `i` variable inside the function using the `var` keyword, it will use the variable in the parent scope.

    One construct with it's own scope though is `with`:
    i = 'outside';
    a = {i : 'inside', j : 'extra'};

    with (a) {
    console.log(i); // inside
    console.log(j); // extra
    }

    console.log(i); // outside
    console.log(j); // ReferenceError: j is not defined


    Cheers! :)

    P.S.: Shame on me for not reading all the comments before writing all these... I missed the part with "Maybe next time I should point out clearly that I know exactly [...] why are all those things happening" :D

    ReplyDelete
  17. yes very funny and a good way to play with such a language

    ReplyDelete
  18. Similarly in JavaScript you can create anonymous self executing functions. This is a function that has no name and is executed immediately. It can be used to induce a block scope and can be useful when dealing with closures within loops.
    web design lessons

    ReplyDelete
  19. Nice post. Visit also this source to know how you can spy on whatsapp messages.

    ReplyDelete
  20. Great blog created by you. I read your blog, its best and useful information. You have done a great work. Super blogging and keep it up.php jobs in hyderabad.

    ReplyDelete
  21. Hi, Great.. Tutorial is just awesome..It is really helpful for a newbie like me.. I am a regular follower of your blog. Really very informative post you shared here. Kindly keep blogging. If anyone wants to become a Front end developer learn from Javascript Online Training from India . or learn thru JavaScript Online Training from India. Nowadays JavaScript has tons of job opportunities on various vertical industry. ES6 Training in Chennai

    ReplyDelete
  22. This is an awesome post. Really very informative and creative contents. This concept is a good way to enhance knowledge. I like it and help me to development very well. Thank you for this brief explanation and very nice information. Well, got good knowledge.
    WordPress website development Chennai

    ReplyDelete
  23. Nice tutorial. Thanks for sharing the valuable information. it’s really helpful. Who want to learn this blog most helpful. Keep sharing on updated tutorials…
    Best Devops online Training
    Online DevOps Certification Course - Gangboard

    ReplyDelete

  24. A universal message I suppose, not giving up is the formula for success I think. Some things take longer than others to accomplish, so people must understand that they should have their eyes on the goal, and that should keep them motivated to see it out til the end.


    Selenium training in Chennai
    Selenium training in Bangalore
    Selenium training in Pune
    Selenium Online training

    ReplyDelete
  25. Try to play popular games in the best online casino in history. great gambling slots Play a lot and get even more wins.

    ReplyDelete
  26. Well researched article and I appreciate this. The blog is subscribed and will see new topics soon.
    python training Course in chennai
    python training in Bangalore
    Python training institute in bangalore

    ReplyDelete
  27. Really useful information. Thank you so much for sharing.It will help everyone.Keep Post.
    Selenium Training in Chennai | SeleniumTraining Institute in Chennai

    ReplyDelete
  28. The article is so informative. This is more helpful for our
    software testing training online
    best selenium online training in chennai. Thanks for sharing

    ReplyDelete
  29. thanks for your details it's very useful and amazing.your article is very nice and excellentweb design company in velachery

    ReplyDelete
  30. You have provided a nice article, Thank you very much for this one. And I hope this will be useful for many people. And I am waiting for your next post keep on updating these kinds of knowledgeable things
    Java Training in Chennai
    Java Training in Coimbatore
    Java Training in Bangalore

    ReplyDelete
  31. It's really nice and meanful. it's really cool blog.you have really helped lots of people who visit blog and provide them useful information.web design company in velachery

    ReplyDelete
  32. This comment has been removed by the author.

    ReplyDelete
  33. Nice Post
    For Data Science training in Bangalore, Visit:
    Data Science training in Bangalore

    ReplyDelete
  34. Check out this page on hacking whatsapp online from Cocospy guide. The explanation by the writer is incredible, I am not that tech savvy but able to understand in one reading time!

    ReplyDelete
  35. Read this Spyic guide to learn hacking Whatsapp by phone number. I have read it and it is gold. You will need to download an app though from its official website, but it is all worth it as we do no need to know any script. All automatic.

    ReplyDelete
  36. Amazing write up, never read such an informative blog and enjoyed it. Thankyou. Keep up the good work. Looking forward to read more.

    Home Salon Dubai
    Salon at Home Dubai
    Beauty Services at home Dubai

    ReplyDelete
  37. I gathered a lot of information through this article.Every example is easy to undestandable and explaining the logic easily.selenium training in bangalore

    ReplyDelete
  38. It’s really great information Thanks for sharing.

    Best Manual Testing Training in Bangalore, BTM layout. My Class Training Academy training center for certified course, learning on Manual Testing Course by expert faculties, also provides job placement for fresher, experience job seekers.

    ReplyDelete
  39. It’s great blog to come across a every once in a while that isn’t the same out of date rehashed material. Fantastic read.dot net training in bangalore

    ReplyDelete
  40. It’s really great information for becoming a better Blogger. Keep sharing, Thanks...

    Bangalore Training Academy located in BTM - Bangalore, Best Informatica Training in Bangalore with expert real-time trainers who are working Professionals with min 8 + years of experience in Informatica Industry, we also provide 100% Placement Assistance with Live Projects on Informatica.

    ReplyDelete
  41. Very interesting, good job and thanks for sharing such a good blog. Thanks a lot…

    Tableau Training in Bangalore. BangaloreTrainingAcademy Provides Best Tableau Training in Bangalore with Industry Experts. Get Practical Knowledge on Tableau visualization Tool Step by Step easily with Tableau Certification guidance and 100% Placement.

    ReplyDelete
  42. Really very happy to say, your post is very interesting to read. I never stop myself to say something about it. You’re doing a great job. Keep it up...

    Upgrade your career Learn AWS Training from industry experts get Complete hands-on Training, Interview preparation, and Job Assistance at Softgen Infotech Located in BTM Layout.

    ReplyDelete
  43. Thank you so much for the great and very beneficial stuff that you have shared with the world.

    Become an Expert In Python Training! The most trusted and trending Programming Language. Learn from experienced Trainers and get the knowledge to crack a coding interview, @Softgen Infotech Located in BTM Layout.

    ReplyDelete
  44. Enjoyed reading the article above, really explains everything in detail,the article is very interesting and effective.Thank you and good luck…

    Start your journey with DevOps Course and get hands-on Experience with 100% Placement assistance from experts Trainers @Softgen Infotech Located in BTM Layout Bangalore.

    ReplyDelete
  45. Thank you so much for the great and very beneficial stuff that you have shared with the world.

    Start your journey withDatabase Developer Training in Bangalore and get hands-on Experience with 100% Placement assistance from experts Trainers @Bangalore Training Academy Located in BTM Layout Bangalore.

    ReplyDelete
  46. That was really a great Article. Thanks for sharing information. Continue doing this.

    Real Time Experts provides Best SAP PM Training in Bangalore with expert real-time trainers who are working Professionals with min 8+ years of experience in Java Training Industry, we also provide 100% Placement Assistance with Live Projects on Java Training.

    ReplyDelete
  47. Post is very useful. Thank you, this useful information.

    Start your journey with AWS Course and get hands-on Experience with 100% Placement assistance from Expert Trainers with 8+ Years of experience @eTechno Soft Solutions Located in BTM Layout Bangalore.

    ReplyDelete
  48. Thanks for sharing this fantastic blog, really very informative. Your writing skill is very good, you must keep writing this type of blogs

    Home Salon Dubai
    wedding car hire gurgaon
    wedding car hire banglore wedding car hire delhi
    wedding car hire dehradun
    wedding car hire noida

    ReplyDelete
  49. In case you want to know on how to do Whatsapp spy, read this article: https://ilounge.com/articles/whatsapp-spy. In there, the explanation is very thorough. Even I able to read the target Whatsapp messages easily overnight!

    ReplyDelete
  50. Visit this clickfree guide: https://www.clickfree.com/phone-spy/how-to-spy-whatsapp-messages-without-installing-on-target-phone/ to know how to spy Whatsapp messages without installing on target phone. This will save you all the hassle on getting in touch with the target phone and installing the software to spy the app yourself.

    ReplyDelete
  51. Cell phones have now made communication easier. Since their invention, they have evolved to incorporate more features than before. That is why they can store valuable personal data.

    Their continued use has brought the bad and good from all kinds of people. Talking of the bad, it’s now easier to send fake news or threaten people since they cannot see you physically.

    That brings in the need to spy someone’s cell phone without touching it to determine such truths. You may have a kid who is acting strange, and he or she doesn’t want you to touch their phone.

    Other times, you want to find out if a specific employee is up to something. Those are some of the things that prompt you to improvise a way to spy their phones without getting them. If you are reading this with such dilemmas, then we have a solution for you.

    The app we are going to introduce here can spy someone’s cell phone without touching it and without their knowledge. It will also deliver the spying results remotely without the target’s knowledge. Let’s see how it works and why you should go for it.

    ReplyDelete
  52. This comment has been removed by the author.

    ReplyDelete
  53. Snapdeal Winner List here came up with an Offer where you can win Snapdeal prize list 2020 by just playing a game & win prizes.
    Snapdeal winner name2020 also check the Snapdeal lucky draw2020

    ReplyDelete


  54. I am really happy to say it’s an interesting post to read . I learn new information from your article , you are doing a great job . Keep it up and a i also want to share some information regarding selenium testing course and selenium training videos

    ReplyDelete
  55. learn Ethical Hacking

    Methods of Facebook Hacking

    I am really happy to say it’s an interesting post to read . I learn new information from your article , you are doing a great job

    ReplyDelete
  56. I like your post, thanks for work!
    It will be nice, if you'll read my article about Facebook spy apps.

    ReplyDelete
  57. The world is going digital day by day. Internet service is improving and more and more people using internet and media as most part of the world internet is not costly.
    ExcelR Digital Marketing Courses In Bangalore

    ReplyDelete
  58. Everyone loves it whenever people come together and share thoughts. Great site, stick with it!
    Tech news

    ReplyDelete
  59. Snapdeal winner 2020 | Dear customer, you can complain here If you get to call and SMS regarding Snapdeal lucky draw, Mahindra xuv 500, lucky draw contest, contact us at to know the actual Snapdeal prize winners 2020.
    Snapdeal winner 2020
    Snapdeal lucky draw winner 2020
    Snapdeal lucky draw contest 2020
    snapdeal winner prizes 2020

    ReplyDelete


  60. This post is really nice and informative. The explanation given is really comprehensive and informative. I also want to say about the digital marketing course in india

    ReplyDelete
  61. Thank you so much for posting this kind of content, your content delivery is awesome. I'm also sharing my nice stuff to you guys please go through it and take a review.
    freelance web developer
    freelance web developer
    php developers
    Offshore Software Development
    seo india
    india seo service company

    ReplyDelete
  62. I feel satisfied to read your blog, you have been delivering a useful & unique information to our vision.keep blogging.
    Digital Marketing Course In Kolkata
    Web Design Course In Kolkata

    ReplyDelete
  63. Nice blog, it’s so knowledgeable, informative, and good looking site. I appreciate your hard work. Good job. Thank you for this wonderful sharing with us. Keep Sharing.
    Digital Marketing Course In Kolkata
    Web Design Course In Kolkata

    ReplyDelete
  64. Thank you for sharing the blog information
    Snapdeal online lucky draw Winner List 2020 here came up with an Offer where you can win Snapdeal lottery 2020 and more prize by just playing a game & win prizes
    Snapdeal winner 2020
    Snapdeal lucky draw winner 2020
    Snapdeal lucky draw contest 2020
    snapdeal winner prizes 2020

    ReplyDelete
  65. It’s good to check this kind of website. I think I would so much from you. Digital Marketing Class In Pune

    ReplyDelete
  66. May I just say what a relief to discover somebody who actually knows what they are talking about on the web. You certainly realize how to bring a problem to light and make it important. A lot more people should look at this and understand this side of your story. It's surprising you're not more informative popular because you definitely have the gift.

    ReplyDelete
  67. I blog frequently and I genuinely thank you for your information. This great article has really peaked my interest. I will book mark your blog and keep checking for new details about once per week. subject I opted in for your Feed as well.

    ReplyDelete
  68. i have been following this website blog for the past month. i really found this website was helped me a lot and every thing which was shared here was so informative and useful. again once i appreciate their effort they are making and keep going on.

    Digital Marketing Consultant in Chennai

    Freelance Digital Marketing Consultant

    SEO Consultant

    digital marketing in Chennai

    ReplyDelete