当前位置: 首页 > 后端技术 > Node.js

为了说清楚javascript的函数

时间:2023-04-03 10:06:09 Node.js

函数前面几节我们围绕函数梳理了this、原型链、作用域链、闭包等。本节梳理了函数本身的一些特点。JavaScript中的函数是一等公民。函数也是对象,因为它们可以像任何其他对象一样具有属性和方法。它们与其他对象的区别在于可以调用函数。简而言之,它们是Function对象。函数是一个可以被外部代码调用的“子程序”,函数内部包含执行语句或表达式。每个自定义函数都是Function的实例,继承了Function的所有属性和方法,而Function是语言本身提供的编程接口。functionfoo(name){console.log(`hello,${name}`)}varres=foo(`Xiaoming`)//'你好,小明'console.log(res)调用函数时,传递的值给函数取值称为函数的实参(传值),相应位置的函数参数称为形参。如果实参是一个包含原始值(数字、字符串、布尔值)的变量,即使函数内部改变了对应形参的值,返回后实参变量的值也不会改变。如果实参是对象引用,则对应的形参会指向与实参相同的对象。如果函数内部改变了相应形参的值,那么返回后实参指向的对象的值也会改变。(欢迎来到按值传递参数的部分。)如果函数没有通过return返回值,函数将默认返回undefined。function在实际开发中承担代码块、函数封装等任务。函数定义//函数声明foo()leta=1functionfoo(){console.log(a)}//函数表达式(函数变量)lettoo=function(){  console.log('hello')}too()//函数构造器实例化一个函数letbar=newFunction('console.log("hello")')bar()//箭头函数letfns=()=>{console.log('hello')}fns()变量提升时需要注意函数声明和函数表达式的区别。强烈推荐阅读变量对象的相关内容:详解。同时,函数声明的函数名不能改变,函数表达式赋值的变量可以重新赋值。函数的各种定义方法不深入。这里需要注意的是,使用构造函数(newFunction())创建的函数,每次调用都会被解析。所以不建议使用Function构造函数来创建函数,因为它要求函数体是字符串,这可能会妨碍一些JS引擎的优化,也可能会造成浏览器资源回收等问题。argumentsarguments,像这样,是函数提供的一个属性,供函数内部使用(ES6中没有箭头函数)。通过arguments,我们可以获取调用函数时传入的实际参数。这个属性是一个类似数组的对象,我们可以像读取数组一样读取值。functionfoo(){leta=arguments[0]//获取函数的第一个参数letb=arguments[1]//获取函数的第二个参数//遍历函数的所有参数for(letarginarguments){console.log(`${arg}:${arguments[arg]}`)}arguments[2]=1122}foo(1,2,3)尽管参数类似于数组,除了获取它们byindex除了elements和length属性外,不能使用push、pop等方法。如果你真的想修改它,你可以把它转换成一个真正的数组:letargs=Array.prototype.slice.call(arguments)letargs=[].slice.call(arguments)//ES2015letargs=Array.from(arguments)自执行函数将函数定义和函数执行结合起来,成为一个立即执行函数,也称为自执行函数。用官方的话来说,叫做IIFE(immediatelyinvokedfunctionexpression),即定义后立即执行的函数。称为自执行匿名函数的设计模式主要由两部分组成:第一部分是一个用括号operator()括起来的匿名函数,这个匿名函数有一个独立的词法作用域。这样既可以防止外界访问这个IIFE中的变量,也不会污染全局作用域。借用这个特性,我们可以封装本地函数,只对外暴露很少的方法。这也是很多第三方库的封装方式之一。第二部分再次使用()创建一个立即函数表达式,JavaScript引擎将直接执行该函数。当函数变成立即执行的函数表达式时,表达式中的变量不能从外部访问:(function(){vara=1})()console.log(a)//aisnotdefinedwillassigntheIIFE给一个变量,不是存储IIFE本身,而是IIFE执行后返回的结果:letbar=(function(){leta=123returna})()console.log(bar)//123执行本身还有一些其他的函数使用方式://下面两个括号()会被立即执行(function(){/*code*/}());(function(){/*code*/})();//因为中括号()和JS的&&、异或、逗号等运算符消除了函数表达式和函数声明的歧义//所以一旦解析器知道其中一个已经是表达式,其他的也会默认为表达式vari=function(){/*代码*/}();true&&function(){/*代码*/}();0,function(){/*代码*/}();//如果你不这样做不关心返回值,或者不怕难读的话//你甚至可以在函数前面加一个一元运算符!function(){/*代码*/}();~function(){/*代码*/}();-function(){/*代码*/}();+function(){/*代码*/}();//上面这种使用一元表达式的方法其实不是很常用//而且有时候在某些场景下肯定有一些缺点,因为一元表达式会有一个不是undefined的返回值//如果你想返回值未定义,那么最安全的方法是使用void关键字voidfunction(){/*code*/}();()的左边必须是函数表达式,而不是函数声明。自执行函数也用在模块封装的上下文中。闭包函数和函数声明的词法作用域形成一个闭包。我们可以使用闭包来存储变量,封装不需要暴露的私有属性和方法。varscope="globalscope";functioncheckscope(){varscope="localscope";函数f(){返回范围;}returnf;}varfoo=checkscope();foo();//“localscope”彻底理解闭包的最好方法是理解闭包的起源。闭包的产生与作用域链密切相关。闭包原理在作用域链部分已经整理过了。函数式编程没做