函数的实参 函数的形参 闭包 js

函数的实参和形参
# 可选形参
```
if(a === undefined) a = [];
```
等价于
```
a = a || [];
```
这两句是完全等价的,只不过后者需要提前声明a而已
如果参数没有传入,其余的填充undefined
可选的形式参数:通过注释/*optional*/来强调参数可选,并且要将其放在最后,否则就要使用null或者undefined来作为占位符来进行传入
# 可变长的实参列表
# callee和caller
callee为指代当前正在执行的函数
caller指代当前正在执行函数的函数
# 将对象属性用作实参
```
>
> function e(o){
... return o.Object;
... }
undefined
> e;
[Function: e]
> var a = {Object:33};
undefined
> e(a);
33
>
```
# 作为值的函数
函数能作为值传入另外一个函数

# 自定义函数属性
函数属性可以自定义
```
o.a = 3;
function o() {
return o.a;
}
```
![此处输入图片的描述][1]
# 作为命名空间的函数
在函数中声明的变量在整个函数体内都是可见的(包括在嵌套函数中),在函数外部是不可见的。不在任何函数内声明的变量为全局变量。在整个js程序中都是可见的。在js中无法声明只在一个代码块内可见的变量的。所以常常简单的定义一个函数用作临时的命名空间。在这个命名空间内定义的变量都不会污染到全局命名空间。
正是因为如果将变量定义在全局的时候,会出现变量的污染,污染到全局变量(好吧,这是动态语言的坑)导致出现一些未知的错误。所以呢将变量放置在函数中,在进行调用,这样放置其变量污染其全局空间,出现变量的冲突(尤其是在浏览器的环境下,很容易的,导致各种未知错误,所以必须要这样做)
## 定义完函数后直接调用函数
```
(
function() {
return 333;
}()
);
```
加()是必须的,因为如果不加()会让js解释器认为其为函数声明,function按照函数声明来进行解释,js解释器不允许创建一个匿名的函数声明,所以会报错。
加()变成一个函数表达式,js解释器运行创建一个匿名的函数表达式
# 闭包
终于到闭包了。(正经点Σ( ° △ °|||)︴)
(这是最难的地方,是函数式编程的基础,也是能否学好js的最关键的地方。。。。当然了,es6还有一个令人讨厌的箭头函数)
闭包是其函数式编程的重要的基础
和其他语言一样js采用的词法作用域,即函数的执行依赖于变量的作用域,作用域是在函数定义时确定的,不是在其调用所决定的
即js的函数对象的内部状态不仅仅包含函数的代码逻辑,还必须引用当前的作用域链(变量的作用域向下传递的,变量的作用域链在进行寻找的时候往上寻找,直到函数的顶部)函数对象可以通过作用域链相互关联起来,**函数体内部的变量可以保存在函数作用域内**,即闭包
> 很古老滴术语,指函数变量可以被隐藏于作用域链之内,因此看起来函数将变量包裹起来。

## 如何定义作用域链
作用域链为一个对象的列表,每次调用js函数的时候,都会创建一个新的对象来保存其局部变量,把这个对象添加到作用域链中,如果函数返回,就从作用域链中将绑定的对象删除,如果不存在嵌套函数,也不存在其引用指向这个绑定的对象,会被js解释器的垃圾回收机制不定时的回收,是不定时的,不是在没有完全引用的时候立马删除,如果定义了嵌套函数,每个嵌套函数都各自对应着一个作用域链,并且这个作用域链指向一个变量绑定的对象。如果这些嵌套函数对象在外部函数中保存下来,那么他们也会和所指向的变量绑定对象一样当做垃圾进行回收,如果这个函数定义了嵌套的函数,并将它作为返回值返回,或者存储在某处属性里,会有外部引用指向这个嵌套函数,即不会被当做垃圾回收,其变量所绑定的对象也不会当做垃圾进行回收。
> 函数执行完毕以后相关的作用域链不会删除,只有当不在有引用的时候,才会进行删除操作

![此处输入图片的描述][2]

## 关于栈的说明
原始栈
栈顶 window
执行下列js脚本
```
function a() {
function f() {
return 333;
}
return f;
}
a()();
```
栈顶 a → window
开始调用,执行到return
发现需要调用f
继续加栈
栈顶 f → a → window
执行完f弹出f
继续执行a,执行完毕弹出a
最后全部执行完毕弹出window

算了文字解释太无力,直接上代码
```
var scope = "global scope"; // 一个全局变量
function checkscope()
{

var scope = "local scope"; // 定义一个局部变量

function f()
{
return scope; // 返回变量作用域中的scope的值
}

return f(); // 返回这个函数
}
```
调用一下这个函数
```
checkscope();
"local scope"
```
接着这样执行
```
var scope = "global scope"; // 一个全局变量
function checkscope()
{

var scope = "local scope"; // 定义一个局部变量

function f()
{
return scope; // 返回变量作用域中的scope的值
}

return f; // 返回这个函数
}
```
继续调用函数
```
checkscope()();
"local scope"
```
## 闭包有什么用
先看一个函数uniqueInteger()使用这个函数能够跟踪上次的返回值
```
var uniqueInteger = (
function() {
var count = 0;
return function() {return count++}
}()
);
```
这样子就使用闭包
```
uniqueInteger();
0
uniqueInteger();
1
```
每次返回是其上一次的值,并随便直接将值加1
至于为什么要这样写,如果不使用闭包,那么恶意代码就可以随便的将计数器重置了。。
```
uniqueInteger.count = 0;
function uniqueInteger() {
return uniqueInteger.count++;
}
```
类似这样的,完全可以做到直接通过赋值,将其count的值重置。
而如果使用闭包,没有办法进行修改,为私有状态,也不会导致其一个页面内变量的冲突,或者是其覆盖。
### 立即调用的函数
```
var a = (function c(){
var a = 1;
a++;
console.log(‘已经执行‘);
return function b(){return a++};
}())
```
额,我大概解释一下这段代码。
首先呢,解释最外层的圆括号,因为如果没有圆括号,则这个是一个赋值语句,将一个匿名函数赋值给变量a,实际上是在内存中完成了栈中变量a指向匿名函数存储的空间的地址,如果有圆括号,实际上是告诉js解释器这是一个语句,需要js执行,消除了其function带来的影响。(ps;貌似火狐上不加也可以,也可以正常的运行)执行和引用的关系下方有。
然后呢,最后的圆括号,代表着其执行这个函数,因为js解析器将()解析为调用前方的函数名称,类似于运算符吧。但是实际上并不是运算符,因为能往其内传值,注意,这点是将其执行的结果保存在堆中,并完成其指向
其后,当直接输入a;,实际上执行并完成了一次调用,其返回值为函数b,将函数b完成一次引用,即变量a引用函数b,由于其存在引用关系,即栈中变量a保存的为其函数a的返回结果,(因为其不是不是对象,如果写a()()表示将函数a调用后返回的对象保存在栈中,然后将栈中的内容再次调用,由于是保存,并不存在其应用关系,所以执行完毕后直接垃圾回收)由于其保存的是函数b的作用域链,而函数b的作用域链是继承自函数a的作用域链,但是由于函数a的作用域链并没有引用导致其执行完后被垃圾回收(当不在有变量指向的时候)。所以呢,函数其值是在函数b中进行保存,如果修改函数c此时函数c并不会影响到函数b中的保存,因为其函数c的变量列表已被销毁,
最后,继续讨论起嵌套函数的引用,由于其父函数已被销毁,但是嵌套函数被引用,(注意:因为其父已经没有,所以是另开辟一块新的堆空间,用于存储其函数c的返回结果,注意是返回结果,而不是函数b)此时另外指定变量保存其结果,无论指定多少个变量保存其结果,**都是新的空间的执行,没有任何的干扰**,详细了解看下面,继续讨论
> 1. ps;如果是()()则代表其会被其垃圾回收
> 2. ps 还需要注意一点点的是由于其引用的是result的值,并不是其

最后,这样就能完成其变量保存在函数中,貌似叫做记忆?

所以呢,借助堆和栈就很好的能理解了闭包

### 再继续看代码
```
function count() {
var n = 0;
return {
count: function() { return n++; },
reset: function() { n = 0; }
};
}
```
```
var c = count(); var d = count();
undefined
```
在分别执行一下下
```
c.count();
0
d.count();
0
c.count();
1
d.count();
1
c.reset();
undefined
c.count();
0
d.count();
2
```
这一点体现了其互不影响性,表明其由于其父被回收,导致其子分别开创了一块在堆中新的内存空间,并完成其指向,互相不干扰。
**其作用域链互不干扰**

### 使用getter和setter完成其闭包
```
function count(n) {
return {
get count() { return n++; },
set count(m) {
if ( m >= n)
n = m;
else
throw new Error( ‘请输入一个正确的值‘ );
},
};
}
```
这个就不用解释啦,很简单啦

### 同一个作用域链中定义两个闭包
```
function test1() {
val = value = 111;
this.test = function() { return value - 1; };
this.test2 = function() { return value + 1; };

}
```
这同样是两个作用链域
不过这样写需要先执行其o.test1(),因为其方法在其函数内部,必须先执行一下,完成其方法的添加,否则会报错,
```
ee.test is not a function
```
提示找不到这个方法,
因为执行
```
ee.test1 = test1;
function test1()
```
只是简单的进行赋值,并不能进行查看,所以导致其无法使用
所以嘛,要先执行一遍,让其方法添加进去
```
ee.test1();
undefined
ee.test();
110
ee.test2();
112
```
这就是两个闭包,这两个闭包互相平行,同时继承于其父,但是又不受其父影响,很神奇吧,(*@ο@*)
> 叮 又发现一个莫名奇妙的东东 https://us.leancloud.cn 貌似目前水平能看懂一些了

### 关于this的问题
this在父闭包显示的即为使用该方法的对象。
但是子就不一定了。
```
function test1() {
val = value = 111;
this.test = function() { return this.x - 1; };
this.test2 = function() { return this.x + 1; };
}
```
执行一下
```
ee.test();
4443
```
这就尴尬了。
好吧。只能说是一般不这样用
一般这样写
```
var self = this;
```
将其值保存进一个self中

[1]: https://www.iming.info/wp-content/uploads/2018/07/1-113.png
[2]: https://www.iming.info/wp-content/uploads/2018/07/1-114.png

原文地址:https://www.cnblogs.com/melovemingming/p/9311659.html

时间: 2024-10-29 02:13:20

函数的实参 函数的形参 闭包 js的相关文章

js函数的两种定义形式,函数的实参列表arguments/形参列表函数名

1.声明式函数:function test(){}; 2.表达式函数:var test=function(){} 例:function test(a,b){} test(2,3,4) ->函数的形参和实参是可变的 函数内参数参数列表: 1.实参列表:在函数内用arguments表示,如上例:arguments=[2,3,4] 2.在函数内test.length表示形参列表长度 原文地址:https://www.cnblogs.com/fangming/p/8847650.html

《JS权威指南学习总结--8.3 函数的实参和形参》

内容要点: 一.可选形参      当调用函数的时候传入的实参比函数声明时指定的形参个数要少,剩下的形参都将设置为undefined值.     例如:       //将对象o中可枚举的属性名追加至数组       //如果省略a,则创建一个新数组并返回这个新数组.        function getPropertyNames(o,/*optional*/a){           if(a === undefined) a= [];           for(var property

JavaScript函数遇实参与形参——智能而有原则

1.实参与形参个数可以有偏差,不会因为参数不统一而错误. 例1: functionbox(a,b){ return a + b; } alert(box(1,3,5)); //运行结果:"4". //进行的运算为"1+3",没有用到"5". 例2: functionbox(a,b,c){ return a + b+c; } alert(box(1,'5')); //运行结果:15undefined. //结果为sring类型.进行的运算1与'5'

已定义的函数有返回值,函数调用可以作为一个函数的实参,但是不能作为形参

1.问题描述 若已定义的函数有返回值,则以下关于该函数调用的叙述中错误的是( D  ) A)函数调用可以作为独立的语句存在 B)函数调用可以作为一个函数的实参 C)函数调用可以出现在表达式中 D)函数调用可以作为一个函数的形参 解析:返回值存在寄存器中, 没有地址, 不能作为形参,但可以作为实参. 2.形参与实参区别 形参出现在函数定义中,在整个函数体内都可以使用, 离开该函数则不能使用. 实参出现在主调函数中,进入被调函数后,实参变量也不能使用. 形参和实参的功能是作数据传送.发生函数调用时,

php中函数的实参和形参

php中函数的实参和形参严格一一对应.例子: <?php function test($a,$b=2,$c){  //函数test第二个参数给默认值 $result=3*$a+2*$b+$c; return $result; } echo test(3,4,5);//调用正确,结果为22 echo test(3,,5);  //错误,没有给第二个形参传值,即使第二个形参有默认参数也不能绕过他而给第三个参数传值 echo test(3,4);//错误,没有给第三个参数传值 总结:函数中的形参带默认

js 函数 /变量/ 以及函数中形参的预解析的顺序

//js 解析分为两个阶段:预编译和执行期 //预编译:声明但是没有初始化 //声明式:funcion a(){} //赋值式:var a = function(){} //匿名函数:function(){} //-----函数之间比较 //结论 1 同一个函数声明,后来者居上 //结论 2 声明式函数要提前赋值式函数 //-----函数和变量的比较 //结论 3 函数声明提升优先级大于变量声明 函数声明要覆盖变量声明 console.log(f1)//打印的是函数 f1() //2 funct

python(五)函数一:函数的定义,实参、形参

1  函数的定义: def 函数名(): 写内容 2.  函数通过调用,才能执行. 3  def 函数名(参数1,参数2)  这里的参数就是形参,当主函数调用这个函数的时候会传2个数,这里的参数相当于占位 函数名(参数1,参数2)这里的参数就是实参,这个参数会传函数里的参数,然后就行运算. 4. 函数会有返回值,返回给调用的函数. 5. 函数 通过return返回.函数遇到return就结束这个程序. 原文地址:https://www.cnblogs.com/dmjsd/p/11007004.h

[js]变量声明、函数声明、函数定义式、形参之间的执行顺序

一.当函数声明和函数定义式(变量赋值)同名时 function ledi(){ alert('ledi1'); }; ledi(); var ledi = function (){ alert('ledi2'); }; ledi(); 执行顺序: var ledi: function ledi(){ alert('ledi1'); }; //预编译结束 ============== ledi(); ledi = function (){ alert('ledi2'); }; ledi(); 函数

JS杂谈系列-函数知识、函数模式

函数,函数,function,function,go go go! 创建函数: 第一种:function aa(){alert(1)}; 第二种:var aa=function(){alert(1)}; 其实对于使用没有太大的区别,第一个是用函数关键字创建,第二个是创建变量,然后赋值为一个函数. 同样我们还可以创建匿名函数 function(){alert(1)}; 函数的里面可以传递参数arg function aa(arg){alert(arg)}; function aa(arg1,arg