{"id":16401932,"url":"https://github.com/allthingssmitty/functions-and-scope-in-javascript","last_synced_at":"2025-02-23T15:42:33.207Z","repository":{"id":150578830,"uuid":"64889574","full_name":"AllThingsSmitty/functions-and-scope-in-javascript","owner":"AllThingsSmitty","description":"Examples of functions and scope (closure) in JavaScript","archived":false,"fork":false,"pushed_at":"2017-03-22T10:53:06.000Z","size":9,"stargazers_count":16,"open_issues_count":0,"forks_count":3,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-01-05T04:45:01.960Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AllThingsSmitty.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-08-04T00:50:36.000Z","updated_at":"2024-03-12T09:07:34.000Z","dependencies_parsed_at":"2023-04-17T10:54:01.601Z","dependency_job_id":null,"html_url":"https://github.com/AllThingsSmitty/functions-and-scope-in-javascript","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AllThingsSmitty%2Ffunctions-and-scope-in-javascript","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AllThingsSmitty%2Ffunctions-and-scope-in-javascript/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AllThingsSmitty%2Ffunctions-and-scope-in-javascript/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AllThingsSmitty%2Ffunctions-and-scope-in-javascript/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AllThingsSmitty","download_url":"https://codeload.github.com/AllThingsSmitty/functions-and-scope-in-javascript/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240339489,"owners_count":19785956,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-11T05:44:33.374Z","updated_at":"2025-02-23T15:42:33.161Z","avatar_url":"https://github.com/AllThingsSmitty.png","language":null,"readme":"# Functions and Scope (Closure) in JavaScript\n\n\n## Table of Contents\n\n* [First Class Functions](#first-class-functions)\n* [Scoping](#scoping)\n* [Invocation Context (`this`)](#invocation-context-this)\n* [Indirect Invocation](#indirect-invocation)\n* [`this` and Nested Function Issue](#this-and-nested-function-issues)\n* [Arguments](#arguments)\n\n\n## First Class Functions\n\n### Assign a Function to a Variable\n\nThis function returns the circumference of a circle:\n\n```javascript\nvar circumference = function (circle) {\n  return 2 * Math.PI * circle.radius;\n};\n//define circle object\nvar circle = {x: 100, y: 100, radius: 50};\n\n//invoke the function\nconsole.log(circumference(circle)); //314.159\n```\n\n\n### Passing a Function as an Argument to Another Function\n\nThis example is a JSON replacer function which filters out property `radius` from object serialization process:\n\n```javascript\nvar filter = function (key, value) {\n//don't serialize property 'radius'\n  if (key === 'radius') {\n    return undefined;\n  } else {\n    return value;\n  }\n};\n\n//define circle object\nvar circle = {x: 100, y: 200, radius: 50};\n\n//get string version of circle object\nvar info = JSON.stringify(circle, filter);\n\n//print the string equivalent of object\nconsole.log(info);  // '{'x': 100, 'y': 200}'\n```\n\n### Returning a Function as a Value From Another Function\n\nIn this example an outer function is defined that returns inner function:\n\n```javascript\nfunction outer(x) {\n//this secret is a closure\n  var secret = 5;\n  return function () {\n    console.log(secret + x);\n  }\n}\n\n//get the inner function\nvar inner = outer(10);\n\n//invoke inner function\ninner(); //15\n```\n\n\n## Scoping\n\n### Variable Declaration Hoisting Rule\n\nThis function tries to check the presence of variable `a` before and after its declaration:\n\n```javascript\nfunction scopeTest() {\n  console.log(a); // undefined - this means variable a is hoisted at this point. No ReferenceError\n  var a = 1;\n  console.log(a); //1\n}\n\nscopeTest();\n```\n\n\n### Function Declaration Statement Hoisting Rule\n\nThis function illustrates the hoisting of function statement:\n\n```javascript\nfunction outer() {\n  //function inner is in scope here\n  //it could be invoked at this place\n  inner();\n\n  console.log(typeof inner === 'function');\n  //true\n\n  function inner() {\n    console.log('I can be invoked anywhere inside outer function');\n  }\n\n  //function inner is in scope here\n  //it could be invoked at this place\n  inner();\n  console.log(typeof inner === 'function');\n  //true\n}\n\nouter();\n```\n\n\n### Function Definition Expression Hoisting Rule\n\nThis function illustrates the hoisting of function definition expression.\n\n```javascript\nfunction outer() {\n  //function inner is not in scope here\n  //it can not be invoked at this place\n  inner(); //typeError\n\n  console.log(typeof inner === 'function');\n  //false\n\n  var inner = function () {\n    console.log('I\\'m not hoisted at the top');\n  };\n\n  //function inner is in scope here\n  // it could be invoked at this place\n  inner();\n  console.log(typeof inner === 'function');\n  //true\n}\n\nouter();\n```\n\n## Invocation Context (`this`)\n\n### Function Invocation\n\nThis function returns the word boundaries of a string:\n\n```javascript\n//\\w to \\W or \\W to \\w transition is a word boundary\nfunction wordBoundaries(subject) {\n  //regex for word boundary position\n  var pattern = /\\b/g;\n\n  //invoke match method defined on the string\n  return subject.match(pattern).length;\n}\n\nvar book = 'JavaScript: The Good Parts';\nconsole.log(wordBoundaries(book)); //8\n```\n\n\n### Method Invocation\n\n```javascript\n//define cylinder object\nvar cylinder = {\n  radius: 10,\n  height: 20\n};\n\n/*\ndefine function on cylinder object\nvolume is the property of cylinder object\nthis inside function is the cylinder object\nthis.radius means radius property of the cylinder object\nthis = invocation context = cylinder object\n*/\ncylinder.volume = function () {\n  return Math.PI * Math.pow(this.radius, 2) * this.height;\n};\n\n//invoke the method on the cylinder object\nconsole.log(cylinder.volume());\n```\n\n\n### Constructor Invocation\n\nThis is a constructor function, and in this example it accepts 2 arguments and sets the newly created object's properties:\n\n```javascript\n//`this` or invocation context is the newly created cylinder object\nfunction Cylinder(radius, height) {\n  this.radius = radius; //object property\n  this.height = height; //object property\n  //object method\n  this.volume = function () {\n    return Math.PI * Math.pow(this.radius, 2) * this.height;\n  };\n}\n\n//create object using constructor function\n//this inside constructor = cylinder1\nvar cylinder1 = new Cylinder(10, 20);\nconsole.log(cylinder1.volume());\n\n//create another object\n//this inside constructor = cylinder2\nvar cylinder2 = new Cylinder(20, 10);\nconsole.log(cylinder2.volume());\n```\n\n### Using Prototype Object\n\n```javascript\n//Accepts 2 arguments and set the newly created object's properties\n//this or invocation context is the newly created cylinder object\nfunction Cylinder(radius, height) {\n  this.radius = radius; //object property\n  this.height = height; //object property\n}\n```\n\nNow volume method is defined on the prototype object.\n\n```javascript\n//prototype object is the property defined on Cylinder constructor\nCylinder.prototype.volume = function () {\n  return Math.PI * Math.pow(this.radius, 2) * this.height;\n};\n\n//create object using constructor function\n//this inside constructor = cylinder1\nvar cylinder1 = new Cylinder(10, 20);\nconsole.log(cylinder1.volume());\n\n//create another object\n//this inside constructor = cylinder2\nvar cylinder2 = new Cylinder(20, 10);\nconsole.log(cylinder2.volume());\n```\n\n\n## Indirect Invocation\n\n### Call Method\n\nThis is a function that returns the circumference of a circle.\n\n```javascript\n//`this` keyword is not associated with any object\nvar circumference = function () {\n  return 2 * Math.PI * this.radius;\n};\n//define circle objects\nvar circle1 = {x: 100, y: 200, radius: 50};\nvar circle2 = {x: 200, y: 300, radius: 25};\n\n//invoke the function\n//this = circle1\nconsole.log(circumference.call(circle1)); //314.159\n//this = circle2\nconsole.log(circumference.call(circle2)); //157.079\n```\n\n\n### Apply Method\n\nThis function makes all arguments non enumerable.\n\n```javascript\nvar makeNonEnumerable = function () {\n  //iterate over all arguments and change the enumerable attribute false\n  for (var i = 0; i \u003c arguments.length; i++){\n    Object.defineProperty(this,arguments[i],{enumerable:false});\n  }\n};\n\nvar testObject1 = {x:1,y:2,z:3};\n\n//make x and y property non enumerable\n//We pass individual argument instead of array\nmakeNonEnumerable.call(testObject1, 'x', 'y\");\n//check the enumerable attribute by console.log\nObject.getOwnPropertyDescriptor(testObject1, 'x').enumerable; //false\nObject.getOwnPropertyDescriptor(testObject1, 'y').enumerable; //false\nObject.getOwnPropertyDescriptor(testObject1, 'z').enumerable; //true\n\nvar testObject2 = {p:1, q:2, r:3};\n//We pass array instead of individual argument\nmakeNonEnumerable.apply(testObject2,['p', 'q']);\nObject.getOwnPropertyDescriptor(testObject2, 'p').enumerable; //false\nObject.getOwnPropertyDescriptor(testObject2, 'q').enumerable; //false\nObject.getOwnPropertyDescriptor(testObject2, 'r').enumerable; //true\n\nvar property;\nfor(property in testObject1){\n  console.log(property);\n}\n```\n\n\n### Call Method: Search Binary Numbers\n\nThis function finds all binary numbers inside a string.\n\n```javascript\n/*\nregex pattern checks digit (0 or 1) one or more times between word boundaries\n\\b -\u003e word boundary\n+ -\u003e repeat 1 or more time - you can make it lazy by +?\n[01]+ -\u003e repeat 0 or 1 one or more time\ng -\u003e global match\nmatch method -\u003e return an array with all matches\n*/\n\nfunction binaryNumbers() {\n  var pattern = /\\b[01]+\\b/g;\n  //this keyword is not associated with any object\n  this.result = this.subject.match(pattern);\n}\n\n//create 2 objects\nvar object1 = {subject: '100 1234 1010 string'},\n  object2 = {subject: '1234 1112 1010 string'};\n\n//associate this with object1\n//this.result will set result property on object1\nbinaryNumbers.call(object1);\n\n//associate this with object2\n//this.result will set result property on object2\nbinaryNumbers.call(object2);\n\n//query result property on object1\nconsole.log(object1.result); //[ '100', '1010' ]\n\n//query result property on object2\nconsole.log(object2.result); // [ '1010' ]\n```\n\n### Call Method: Internals\n\nThis function finds all binary numbers inside a string.\n\n```javascript\n/*\nregex pattern checks digit (0 or 1) one or more times between word boundaries\n\\b -\u003e word boundary\n+ -\u003e repeat 1 or more time - you can make it lazy by +?\n[01]+ -\u003e repeat 0 or 1 one or more time\ng -\u003e global match\nmatch method -\u003e return an array with all matches\n*/\n\nfunction binaryNumbers() {\n  var pattern = /\\b[01]+\\b/g;\n  //this keyword is not associated with any object\n  this.result = this.subject.match(pattern);\n}\n\n//create 2 objects\nvar object1 = {subject: '100 1234 1010 string'},\n  object2 = {subject: '1234 1112 1010 string'};\n\n//associate this with object1\n//this.result will set result property on object1\nobject1.method = binaryNumbers;\nobject1.method();\ndelete object1.method;\n\n\n//associate this with object2\n//this.result will set result property on object2\nobject2.method = binaryNumbers;\nobject2.method();\ndelete object1.method;\n\n//query result property on object1\nconsole.log(object1.result); //[ '100', '1010' ]\n\n//query result property on object2\nconsole.log(object2.result); // [ '1010' ]\n```\n\n\n## `'this` and Nested Function Issues\n\n### Basic Reducer Function\n\nIn this example the reducer object has one array and a method reduced:\n\n```javascript\n//Below is the calculation using reduce method and array [100, 200, 300]\n//x = 100, y = 200\n//0.5 * (100 + 200) = 150 -\u003e this will become x in next iteration\n//x = 150, y = 300\n//0.5 * (150 + 300) = 225 -\u003e final value\nvar reducer = {\n  a: [100, 200, 300],\n  reduce: function () {\n    return this.a.reduce(function (x, y) {\n      return 0.5 * (x + y);\n    });\n  }\n};\nconsole.log(reducer.reduce()); //225\n```\n\n### Simulate Problem: Reducer Factor and `this`\n\n```javascript\n\n//reducer object has one array a and method reduce\n//reduce does the job of reducing using reduce method 0.5 * (x + y)\n//Below is the calculation using reduce method and array [100, 200, 300]\n//x = 100, y = 200\n//0.5 * (100 + 200) = 150 -\u003e this will become x in next iteration\n//x = 150, y = 300\n//0.5 * (150 + 300) = 225 -\u003e expected value -\u003e but we get NaN\nvar reducer = {\n  a: [100, 200, 300],\n  factor: 0.5,\n  reduce: function () {\n    return this.a.reduce(function (x, y) {\n      return this.factor * (x + y);\n    });\n  }\n};\nconsole.log(reducer.reduce()); //NaN\n```\n\n\n### Using `this` Keyword Inside a Nested Function\n\n```javascript\n//reducer object has one array a and method reduce\n//reduce does the job of reducing using reduce method 0.5 * (x + y)\n//Below is the calculation using reduce method and array [100, 200, 300]\n//x = 100, y = 200\n//0.5 * (100 + 200) = 150 -\u003e this will become x in next iteration\n//x = 150, y = 300\n//0.5 * (150 + 300) = 225\nvar reducer = {\n  a: [100, 200, 300],\n  factor: 0.5,\n  reduce: function () {\n    var self = this;\n    return this.a.reduce(function (x, y) {\n      return self.factor * (x + y);\n    });\n  }\n};\nconsole.log(reducer.reduce()); //225\n```\n\n\n## Arguments\n\n### Basics\n\nThis function tries to explain the flexibility of arguments supplied:\n\n```javascript\nfunction test(x, y) {\n  // I don't do anything\n  console.log(x);\n  console.log(y);\n  //print arguments object - Array like object\n  console.log(arguments);\n}\n\n//no argument supplied\ntest();\n// x - undefined\n// y - undefined\n// { }\n\n//less arguments supplied\ntest(1);\n// x - 1\n// y - undefined\n// { '0': 1}\n\n//arguments = parameters = 2\ntest(1, 2);\n// x - 1\n// y - 2\n// {'0': 1, '1': 2 }\n\n//more argument supplied than actual parameters\ntest(1, 2, 3);\n// x - 1\n// y - 2\n// {'0': 1, '1': 2, '2': 3}\n```\n\n### Objects\n\nThis function adds all the arguments supplied:\n\n```javascript\nfunction add() {\n  console.log(arguments.length); //3\n  var sum = 0;\n  //iterate over all arguments\n  //trick - save arguments.length in some variable\n  for (var i=0; i \u003c arguments.length; i++){\n    sum +=arguments[i];\n  }\n  return sum;\n}\n\nconsole.log(add(1,2,3)); //6\n```\n\n### Default Parameters: Keys and `getOwnPropertyNames`\n\nThis function returns object properties based on the flag onlyEnumerable:\n\n```javascript\n/*\ngetProperties(obj) -\u003e return enumerable own properties\ngetProperties(obj, false) -\u003e return enumerable as well as non enumerable properties\ngetProperties(obj, true) -\u003e return enumerable own properties\n*/\n\nfunction getProperties(obj, onlyEnumerable) {\n  //if onlyEnumerable is not passed, set it to true\n  if (onlyEnumerable === undefined) {\n    onlyEnumerable = true;\n  }\n\n  if (onlyEnumerable) {\n    return Object.keys(obj);\n  } else {\n    // enumerable + non enumerable\n    return Object.getOwnPropertyNames(obj);\n  }\n}\n\n//define object with 2 properties\n//by default newly created properties are enumerable\nvar obj = {x: 1, y: 2};\n\n//define one non enumerable property \"z\"\nObject.defineProperty(obj, 'z', {enumerable: false, value: 3});\n\nconsole.log(getProperties(obj)); // [ 'x', 'y' ]\nconsole.log(getProperties(obj, false)); // [ 'x', 'y', 'z' ]\nconsole.log(getProperties(obj, true)); // [ 'x', 'y' ]\n```","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fallthingssmitty%2Ffunctions-and-scope-in-javascript","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fallthingssmitty%2Ffunctions-and-scope-in-javascript","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fallthingssmitty%2Ffunctions-and-scope-in-javascript/lists"}