javaScript函数模块详解
javaScript函数模块详解
函数实际上是对象,每个函数都是Function类型的实例,而Function也有属性和方法,和其他引用类型一样
可以将函数名想象成指针,函数想象成对象
注意,严格模式下函数有以下规定:函数不能以eval或arguments作为名称,同样他们俩也不能做参数名,函数参数不能同名
//函数声明
//js引擎会在任何代码执行之前,先读取函数声明并在执行上下文中生成函数定义,叫做“函数声明提升”
function test(a,b){}
//函数体
//必须等到代码执行的那一刻才会在执行上下文中生成函数定义,用var和let都是这样
//1、命名函数表达式var test=function test(){}
//2、匿名函数表达式 --> 函数表达式var test = function (){}
//3.箭头函数let test = () => {}
//4.Function构造函数(不推荐)
//接收任意多个字符串参数,最后一个参数始终会被当成函数体,之前的参数都是函数参数
let test = new Function("arg1", "arg2", "return arg1 + arg2")
//这段代码会被解释两次:第一次将它当作常规ECMAScript代码,第二次解释传给构造函数的字符串
return:终止函数、返回值
作用域:变量和函数生效(能被访问的)的区域
没有重载
同一个函数被定义多次,那么,最后一个会覆盖之前所有的定义
箭头函数
任何可以使用函数表达式的地方,都可以使用箭头函数
箭头函数非常适合嵌入式场景,因为其简洁的语法
只有没有参数或者多个参数的情况下,才需要使用括号
箭头函数也可以不用大括号,如果不使用大括号,箭头后面只能由一行代码,例如一个赋值操作、或者一个表达式,而且会隐式返回这行代码的值
注意:箭头函数不能使用arguments、super和new.target,也不能用作构造函数,更没有prototype属性
函数名
ECMAScript6的所有函数对象都会暴露一个只读的name属性,其中包含关于函数的信息。大多数情况下,这个属性中保存的就是一个函数标识符,或者说是一个字符串化的变量名,即使函数没有名称也会如实显示成空字符串。如果他是Function构造函数构建的,则会标识成“anonymous”
如果是一个获取函数、设置函数,或者使用bind()实例化,那么标识符前面会加上相应的前缀bound foo、get foo、set foo
参数、arguments
实参在函数里被存在列表arguments(类数组对象)里,形参和实参没有强制规定个数
function abc(a){console.log(arguments)
}
abc(1,2,3)
arguments = [1,2,3] //实参列表
在形参和列表arguments存在一种映射,某个值改变相应的另一个值也改变
但是形参和arguments在内存中是分开的
function sum(a,b){a = 2console.log(arguments[0])arguments[0] = 4console.log(a)
}
sum(1,3)
2
10
4
但是如果函数一开始就没有存在的映射并不会有此效果
function sum(a,b){b = 2console.log(arguments[1])
}
sum(1) //只传一个实参情况
//undefined
严格模式下,给arguments赋值不会再影响形参的值;在函数中尝试重写arguments对象会导致语法错误
function sum(a,b){a = 2console.log(arguments[0])arguments[0] = 4console.log(a)
}
sum(1,3)
//1
//10
//2
arguments.callee
指向arguments对象函数所在的指针
function test(num) {if (num < 1) {return 1} else {return num * test(num - 1);}
}//这样只能调用名称为test的函数,如果函数名变化就会出现相应问题
let trueTest = test
test = function() {return 0;}
test(5) //0
trueTest(5) //0,因为trueTest函数里会调用test()
//下面可以避免这种情况
function test(num) {if (num < 1) {return 1;} else {return num * arguments.callee(num - 1)}
}
let trueTest = test
test = function() {return 0;}
test(5); //0
trueTest(5); //120
默认参数值
ES5.1以前需要手动检测某个参数是否为undefined,ES6之后就能显式定义默认参数了
但是这样将会断开形参与arguments之间的同步映射(只要有一个形参有默认值就会这样)
function test(name = 'lsn') {console.log(name)console.log(arguments[0])
}
//给函数传undefined相当于没有传值,也就是arguments不会和name建立联系
test(undefined);
//lsn
//undefined
function test(name = 'lsn', say) {name = 'haha'say = 'lll'console.log(arguments[0], arguments[1])
}
test('foo', 'l')
//foo
默认参数值不一定是原始值或对象类型,可以是调用函数后的返回值function test(name = getName()) {}
这个getName()求值函数只有在test()函数被调用的时候才会运行求值,test()函数定义时不会
箭头函数也能使用默认值,不过在只有一个参数时就必须加括号了
默认参数作用域与暂时性死区
先定义的默认参数可以被后面的命名参数使用,但是不能被前面的命名参数使用,这里和按顺序let定义let是一样的
function test(name = 'lsn', val = 'foo') {}
//同下
function test() {let name = 'lsn'let val = 'foo'
}function test(name = 'lsn', val = name) {}
//这里val就等于lsn//参数有自己的作用域,所以不能使用函数体的作用域
//function test(name = hi) {// let hi = 'lsn'
//}
//上面这种会报错
扩展参数
使用扩展操作符会将可迭代对象拆分,并将迭代返回值每个单独传入
function test() {console.log(arguments.length)
}
test([0, 1]) //1
test(...[0, 1]) //2
收集参数
正好和扩展参数相反,会得到一个Array实例,但是不影响arguments
function test(...value) {console.log(value)
}
test(1, 2, 3) //[1, 2, 3]
//Arguments(3) [1, 2, 3, callee: (...), Symbol(Symbol.iterator): ƒ]
this
在标准函数中,this引用的是把函数当成方法调用的上下文对象(如在全局调用函数时,this指向window)
在箭头函数中,this引用的是定义箭头函数的上下文,而且因为箭头函数默认不会使用自己的this,而是会和外层的this保持一致(看作箭头函数this指向的是外层this所指向的对象),并且箭头函数的this是不可改的
window.color = 'red'
let o = {color: 'blue'
}function say() {console.log(this.color)}
say() //red
o.say = say
o.say() //bluelet say1 = () => {console.log(this.color)}
say1() //red
o.say1 = say1
o.say1() //red
传递参数
因为原始值和引用值存储方式不一样,函数中传参方式有区别,原始值为按值传递,即复制一份副本传入到参数中;而引用值则会将引用值的堆内存位置复制到参数中。
两者其实和复制值是一样的,上文有解释
arguments(ES5严格模式下不允许使用)
1、callee
能够调用当前function(严格模式下访问会报错)
function test () {console.log(arguments.callee)
}test()
阶乘
var num = (function (n) {if(n == 1) {return 1}return n * arguments.callee(n-1)
}(100))
2、caller
es5会给函数对象上添加这个属性,基本浏览器早期版本都支持这个属性
引用当前函数被调用的环境,如果是全局作用域中调用则为null
function test () {demo()
}function demo () {console.log(demo.caller)
}test()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZKCxhK73-1639121328208)(https://dlnu19javastudy.yuque.com/api/filetransfer/images?url=https%3A%2F%2Fimg.samuel-luo.space%2Fimage-20210521103642552.png&sign=963641474beb54b91594613f8c60db1bcc09672aeeb134261b2b498257cd5613)]
如果要降低耦合度,则可以通过arguments.callee.caller来引用同样的值
new.target
在函数里调用,如果该函数是使用new关键字调用的,则target会引用被调用的构造函数,如果该函数被当成普通函数运用,则返回undefined
function test() {if (new.target) {throw 'hello'}
}
new test() //hello
test() //
属性
每个函数都有两个属性:length和prototype
length:该属性保存函数定义的命名参数的个数
function test(helo) {}
test.length //1
prototype:该属性不可枚举,所以使用for-in不会返回这个属性
apply()、call()
这两个方法可以改变函数内this的引用,下文有详细说明
fun.apply(obj, arguments) //可以是Array实例也可以是arguments对象
fun.call(obj, ...arguments) //参数必须一个个分开
在严格模式下,调用函数如果没有指定上下文对象,则this值不会指向window。除非使用apply()或call()把函数指定给一个对象,否则this值会变成undefined
函数表达式
任何时候,把函数当作值来用,他就是一个函数表达式
递归
阶乘
function mul(n){if(n == 1){return 1}return n * mul(n-1)
}
斐波那契数列
function fb(n){if(n == 1 || n == 2){return 1}return fb(n-1) + fb(n-2)
}
但是这样直接用函数名可能会有问题,因为如果这个变量被赋值为null则会报错,所以建议使用arguments.callee
但是在严格模式下,不能访问arguments.callee,所以这里可以用命名函数表达式
尾调用优化
es6规定如果一个函数的返回时另一个函数的返回,则执行尾调用优化,具体如下
function outter() {return inner() //尾调用
}上述代码在es6中优化如下:
1、执行到outter函数体,第一个栈帧被推到栈上
2、执行outter函数体,到达return语句。为求值语句,必须先求值inner
3、引擎发现把第一个栈弹出栈外也没关系,因为inner的返回值也是outter的返回值
4、弹出outter的栈帧
5、执行到inner函数体,栈帧被推到栈上
6、执行inner函数体,计算其返回值
7、将inner的栈帧推出到栈外
现在的浏览器没法测试尾调用优化是否起作用,但是这个是es6规范所规定的
尾调用优化条件
代码在严格模式下执行、外部函数的返回值时对尾调用函数的调用,尾调用函数返回后不需要执行额外的逻辑、尾调用函数不是引用外部函数作用域中自由变量的闭包
尾调用优化很适合递归,因为递归代码最容易在栈内存中产生大量栈帧
之所以要求严格模式,主要是因为非严格模式下函数调用允许使用f.arguments和f.caller,而它们都会引用外部函数的栈帧
立即调用的函数表达式(IIFE,Immediately Invoked Function Expression)
定义后立即执行,执行完立即释放,不占用内存
(function (){//...
}())
或
(function abc(){//...
}())
或
(function abc(){//...
})()
//W3C建议前两种执行方式
//只有表达式才能被执行符号"()"执行,function test(){}();这种将不会执行,因为()前方是函数定义式;var test = function (){}();这种将会执行,并且和立即执行函数没有区别
//被执行符号执行的表达式将会自动放弃函数的名称
//+ function (){}();这种将会执行,+ - !等都可以
//()可以是数学优先表达式所以(function (){})()、(function (){}())首先是数学表达式()将函数定义式转化为表达式,然后就可被执行如果传参
(function abc(a,b){//...
}(a,b))如果要返回值
var abc = (function abc(a,b){//... return 12
}(a,b))
在es5.1以前,为了防止定义的变量外泄,常常用IIFE,但是es6以后就可以用块级作用域了
//内嵌块级作用域
{let ifor (i = 1; i < count; i++) {console.log(i)}
}
console.log(i) //报错//循环块级作用域
for (let i = 0; i < count; i++) {console.log(i)
}
console.log(i);//报错
私有变量
私有变量和特权方法、静态私有变量、模块模式模块增强模式(详情请看红宝书p316)
预编译:在函数执行的前一刻发生(生成函数上下文)
1、创建AO(Activation Object)对象 [翻译:执行期上下文]
2、找形参和变量声明,将变量和形参名作为AO的属性名,值为undefined
3、将实参和形参相统一
4、找函数体的函数声明,赋值于函数体
5、创建arguments和this,这个this指向window()
function fn(a){console.log(a)var a = 123console.log(a)function a(){}console.log(a)var b = function (){}console.log(b)function d(){}
}
fn(1)//step1AO{}
//step2AO{a:undefined,b:undefined,}
//step3AO{a:1,b:undefined,}
//step4AO{a:function a(){},b:undefined,d:function d(){},}//step5AO{arguments:[],this:window,a:function a(){},b:undefined,d:function d(){},}//执行函数体(控制台打印)
f a(){}
123
123
f (){}
全局对象没有形参,所以没有第三步,而且第一步创建的是GO(Global Object)对象,
GO === window
console.log(a)
var a = 123
//控制台输出:undefinedconsole.log(a)
//控制台输出:error: a is not definedtest()
function test(){console.log("test")
}
//控制台输出:test
程序优先找自己所拥有的变量,在函数中优先AO中的对象,如果没有则向父级寻找,例如GO
注意:预编译不管有没有if ,只看声明
console.log(a) //undefined
console.log(c) //undefined
if(){var a//会被预编译function c(){}//会被预编译
}
最新:亲自实验IE、chrome、Edge,发现新特性!!!
if里面的function f函数声明会在GO和这个函数里面的AO同时声明f = function(),这是if语句里的特性
var a
console.log(f)
if (true) {function f () {console.log(f)f = aconsole.log(f)//console.log("test")}console.log(f)
}
f()
console.log(f)
//console
undefined
ƒ f () {console.log(f)f = aconsole.log(f)console.log("test")
}
ƒ f () {console.log(f)f = aconsole.log(f)console.log("test")
}
undefined
ƒ f () {console.log(f)f = aconsole.log(f)console.log("test")
}
!if里面的function f函数声明会在GO和这个函数里面的AO同时声明f = function(),这是if语句里的特性```javascript
var a
console.log(f)
if (true) {function f () {console.log(f)f = aconsole.log(f)//console.log("test")}console.log(f)
}
f()
console.log(f)
//console
undefined
ƒ f () {console.log(f)f = aconsole.log(f)console.log("test")
}
ƒ f () {console.log(f)f = aconsole.log(f)console.log("test")
}
undefined
ƒ f () {console.log(f)f = aconsole.log(f)console.log("test")
}
javaScript函数模块详解相关推荐
- python中import re_Python3中正则模块re.compile、re.match及re.search函数用法详解
本文实例讲述了Python3中正则模块re.compile.re.match及re.search函数用法.分享给大家供大家参考,具体如下: re模块 re.compile.re.match. re.s ...
- js onscroll android,JavaScript触发onScroll事件的函数节流详解
问题描述 常见的网站布局,顶部一个导航栏,我们假设本页面共有四个栏目:分别为A.B.C.D,我们点击A,锚点跳转至A栏目,同时顶部的A按钮高亮:点击B,锚点跳转至B栏目,同时顶部的B按钮高亮:我们在M ...
- python six模块详解_对python中的six.moves模块的下载函数urlretrieve详解
实验环境:windows 7,anaconda 3(python 3.5),tensorflow(gpu/cpu) 函数介绍:所用函数为six.moves下的urllib中的函数,调用如下urllib ...
- 【ES6】Module模块详解
[ES6]Module模块详解 一.Module的由来 二.严格模式 三.export命令 四.import命令 查看更多ES6教学文章: 参考文献 引言:由于两个JS文件之间相互使用必须通过一个ht ...
- python中json模块博客园_Python中的Json模块详解
Python中的Json模块详解 Json(JavaScript Object Notation)它是一种轻量级的数据交换格式,具有数据格式简单,读写方便易懂等很多优点.许多主流的编程语言都在用它来进 ...
- [转载] python支持complex吗_Python 内置函数complex详解
参考链接: Python complex() 英文文档: class complex([real[, imag]]) Return a complex number with the value re ...
- mybatis接口中的方法重载_MyBatis的Mapper接口以及Example的实例函数及详解
一.mapper接口中的方法解析 mapper接口中的函数及方法 方法 功能说明 int countByExample(UserExample example) thorws SQLException ...
- python常用内置模块-Python常用内置模块之xml模块(详解)
xml即可扩展标记语言,它可以用来标记数据.定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言.从结构上,很像HTML超文本标记语言.但他们被设计的目的是不同的,超文本标记语言被设计用来显示 ...
- python编程入门与案例详解课后题答案-Python入门之三角函数sin()函数实例详解
描述 sin()返回的x弧度的正弦值. 语法 以下是sin()方法的语法: importmath math.sin(x) 注意:sin()是不能直接访问的,需要导入math模块,然后通过math静态对 ...
最新文章
- msyql request quit
- 微服务网关解决方案调研和使用总结 专题
- 用户user空间和内核kernel空间
- 对软件工程实践课程的预定目标
- 基于JAVA+SpringMVC+Mybatis+MYSQL的社区养老服务网站
- Num37 spring 事务 ssh整合
- maxscale mysql5.7_Maxscale实现Mysql读写分离
- mysql的空白值mac,Mac下mysql安装启动遇到的坑,及数据库常用指令
- matlab对语音信号预加重处理,语音信号的预加重处理和加窗处理
- VEX到底是一种什么机器人?和乐高有什么区别?
- 一级建造师考试备考各科知识点记忆技巧
- android手机品牌排行,智能手机品牌排行榜2019前十名
- iOS平台游戏安全之IPA破解原理及防御(第三弹)
- java动效_前端实现炫酷动效_Lottie-前端实现AE动效
- python 爬取拉钩网数据
- python保存变量_python | 变量-保存与命名规则
- END-TO-END COMPLEX-VALUED MULTIDILATED CONVOLUTIONAL NEURALNETWORK FOR JOINT ACOUSTIC ECHO CANCELLA
- ~5 ccf 2021-12-2 序列查询新解
- python-web-下载所有xkcd漫画
- 北大青岛_深圳嘉华_shell特殊变量演示(五)生产环境下使用变量子串
热门文章
- MGRE、Tunnel、RIP、PPP综合实验
- 中国开源先驱的力与梦——开源六君子的黄金时代
- 反编译pbd文件中的dw,利用pb本身的功能
- php开源堡垒机,开源堡垒机在开发环境中的使用方案-麒麟开源堡垒机
- 打造开源、开放的生态系统,KubeSphere“三步走”布局云原生
- [管理]_[选择合适的项目-任务管理工具Jira Redmine Trac对比]
- 如何报考PMP项目管理认证考试?
- 显卡天梯图2021年1月 显卡天梯图2021最新版
- 经济不确定环境下,制造业的数字化转型之道
- 专访简书刘英滕 | 未来,以「产品设计」定位自己的设计师会越来越多