摘要:中函数是一等公民。小明小明调用函数时,传递给函数的值被称为函数的实参值传递,对应位置的函数参数名叫作形参。所以不推荐使用构造函数创建函数因为它需要的函数体作为字符串可能会阻止一些引擎优化也会引起浏览器资源回收等问题。
函数
之前几节中围绕着函数梳理了 this、原型链、作用域链、闭包等内容,这一节梳理一下函数本身的一些特点。
javascript 中函数是一等公民。 并且函数也是对象,因为它们可以像任何其他对象一样具有属性和方法。它们与其他对象的区别在于函数可以被调用。简而言之,它们是 Function 对象。
一个函数是可以通过外部代码调用的一个“子程序”,函数内部包含了执行语句或表达式。每个自定义函数都是Function的实例,并继承Function的所有属性和方法,而Function 是语言本身提供的编程接口。
function foo (name) { console.log(`hello, ${name}`) } var res = foo(`小明`) // "hello, 小明" console.log(res)
调用函数时,传递给函数的值被称为函数的实参(值传递),对应位置的函数参数名叫作形参。如果实参是一个包含原始值(数字,字符串,布尔值)的变量,则就算函数在内部改变了对应形参的值,返回后,该实参变量的值也不会改变。如果实参是一个对象引用,则对应形参会和该实参指向同一个对象。假如函数在内部改变了对应形参的值,返回后,实参指向的对象的值也会改.(欢迎查看参数传值一节)
如果函数内部没有通过return返回一个值,则函数会默认返回undefined.
函数在实际开发中承担着代码分块、 功能封装等任务。
函数定义// 函数申明 foo() let a = 1 function foo () { console.log(a) } // 函数表达式 (函数变量) let too = function (){ console.log("hello") } too() // Function 构造函数实例化一个函数 let bar = new Function("console.log("hello")") bar() // 箭头函数 let fns = () => { console.log("hello") } fns()
需要注意的是函数申明和函数表达式在变量提升时的区别,强烈建议阅读变量对象相关内容:详情。同时函数申明的函数名称不能被改变,而函数表达式赋值给的变量可以被重新赋值。
函数的各种定义方式就不深入,这里需要注意的是通过使用构造函数(new Function())方式创建的函数,在每次调用的时候都会解析一次。所以不推荐使用 Function 构造函数创建函数, 因为它需要的函数体作为字符串可能会阻止一些 JS 引擎优化,也会引起浏览器资源回收等问题。
argumentsarguments同this一样是函数提供给函数内部使用的一个属性(ES6中箭头函数没有)。通过arguments我们可以获取调用函数时传递进来的实参, 该属性是一个类数组对象,我们可以像数组一样进行数值的读取。
function foo () { let a = arguments[0] // 获取函数的第一个参数 let b = arguments[1] // 获取函数的第二个餐宿 // 遍历函数的所有参数 for (let arg in arguments) { console.log(`${arg}: ${arguments[arg]}`) } arguments[2] = 1122 } foo(1, 2, 3)
虽然arguments是类似于数组,但是除了通过索引获取元素和length属性外,不能使用push、pop等方法。如果确实要进行修改操作,可以将其转换为真正的数组:
let args = Array.prototype.slice.call(arguments) let args = [].slice.call(arguments) // ES2015 let args = Array.from(arguments)自执行函数
把函数定义和函数执行结合到一起就是立即执行函数,也叫自执行函数。
在官方术语中叫做 IIFE( 立即调用函数表达式),是在定义时就会立即执行的函数。被称为自执行匿名函数的设计模式,主要包括两部分:
第一部分是包围在 圆括号运算符() 里的一个匿名函数,这个匿名函数拥有独立的词法作用域。这不仅避免了外界访问此 IIFE 中的变量,而且又不会污染全局作用域。借用这个特性我们可以对局部功能进行封装,只暴露很少的方法给外部环境,这也是很多第三方库的封装方法之一。
第二部分再一次使用 () 创建了一个立即执行函数表达式,JavaScript 引擎到此将直接执行函数。
当函数变成立即执行的函数表达式时,表达式中的变量不能从外部访问:
(function () { var a = 1 })() console.log(a) // a is not defined
将 IIFE 分配给一个变量,不是存储 IIFE 本身,而是存储 IIFE 执行后返回的结果:
let bar = (function (){ let a = 123 return a })() console.log(bar) // 123
别外自执行函数还有一些其他使用方式:
// 下面2个括弧()都会立即执行 (function(){ /* code */ }()); (function(){ /* code */ })(); // 由于括弧()和JS的&&,异或,逗号等操作符是在函数表达式和函数声明上消除歧义的 // 所以一旦解析器知道其中一个已经是表达式了,其它的也都默认为表达式了 var i = function(){ /* code */ }(); true && function(){ /* code */ }(); 0, function(){ /* code */ }(); // 如果你不在意返回值,或者不怕难以阅读 // 你甚至可以在function前面加一元操作符号 !function(){ /* code */ }(); ~function(){ /* code */ }(); -function(){ /* code */ }(); +function(){ /* code */ }(); // 上面这种使用一元表达式这种方式其实是不太常见的 // 而且有时候肯定在一些场景下存在一些弊端,因为一元表达式会有一个不为undefined的返回值 // 要想返回值为undefined,那么最保险的就是使用void关键字 void function(){/* code */}();
()左边必须是一个函数表达式,而不是一个函数申明。自执行函数也会用在模块封装的场景下。
闭包函数和函数声明时的词法作用域形成闭包。我们可以使用闭包来保存变量、封装不需要暴露的私有属性和方法。
var scope = "global scope"; function checkscope(){ var scope = "local scope"; function f(){ return scope; } return f; } var foo = checkscope(); foo(); // "local scope"
彻底理解闭包的最好途径就是理解闭包的由来,闭包的产生和作用域链有密切关系。在作用域链一节已经梳理过闭包的原理。
函数式编程未完成
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/107968.html
摘要:构造函数和实例都通过属性指向了原形。代码示例是构造函数的实例的属性与的属性保存的值相等,即他们指向同一个对象原形。 讲清楚之javascript原型 标签: javascript javascript 中原形是一个比较难于理解的概念。javascript 权威指南在原形这一章也花了大量的篇幅进行介绍,也许你已经读过javascript 权威指南,或者已经是读第N篇了,然而这篇文章的目...
摘要:讲清楚之参数传值参数传值是指函数调用时,给函数传递配置或运行参数的行为,包括通过进行传值。所以对的赋值会改变上下文栈中标识符保存的具体值此时如果使用的是按引用传递,则变量所指向的对象因该也被赋值为。 讲清楚之 javascript 参数传值 参数传值是指函数调用时,给函数传递配置或运行参数的行为,包括通过call、apply 进行传值。 在实际开发中,我们总结javascript参数传...
阅读 1876·2021-09-24 09:48
阅读 3218·2021-08-26 14:14
阅读 1673·2021-08-20 09:36
阅读 1459·2019-08-30 15:55
阅读 3625·2019-08-26 17:15
阅读 1424·2019-08-26 12:09
阅读 605·2019-08-26 11:59
阅读 3322·2019-08-26 11:57