欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

前端笔记知识点整合之JavaScript(四)

程序员文章站 2022-05-25 18:30:21
一、自定义函数function 函数就是功能、方法的封装。函数能够帮我们封装一段程序代码,这一段代码会具备某一项功能,函数在执行时,封装的这一段代码都会执行一次,实现某种功能。而且,函数可以多次调用。 1.1函数的定义和调用 语法: 定义:把需要实现的功能预先做好 执行:需要的时候执行这个功能,而且 ......

一、自定义函数function

函数就是功能、方法的封装。函数能够帮我们封装一段程序代码,这一段代码会具备某一项功能,函数在执行时,封装的这一段代码都会执行一次,实现某种功能。而且,函数可以多次调用。

1.1函数的定义和调用

语法:

定义:把需要实现的功能预先做好

执行:需要的时候执行这个功能,而且还可以执行多次

 

定义:function myname(){}

执行:myname()

 

【语法解释】:

 function  定义函数的关键字

 myname    函数名称

 ()         参数集

 {}        函数体,执行的代码都放在{}里面

 

多条语句,组成一个“语句军团”,集体作战。

//定义一个函数,函数就是一组语句的集合
function haha(){
   console.log(1);
   console.log(2);
   console.log(3);
   console.log(4);
}
haha();//调用haha函数
haha();//调用haha函数
haha();//调用haha函数

前端笔记知识点整合之JavaScript(四)

函数必须先定义,然后才能调用

定义一个函数,用关键字function来定义function就是英语“功能”的意思。表示这里面定义的语句,完成了一些功能。function后面有一个空格,后面就是函数名字,函数的名字也是关键字,命名规范和变量命名是一样的。名字后面有一对儿圆括号,里面放置参数。然后就是大括号,大括号里面是函数的语句。

 function 函数名称(){

 

 }

 

函数如果不调用,里面的语句一辈子都不执行,等于白写。

调用函数的方法,就是函数名称加()()是一个运算符,表示执行一个函数。

 函数名称()

一旦调用函数,函数内部的代码不管对错,都会执行。

能感觉到,函数是一些语句的集合,让语句称为一个军团,集体作战。要不出动都不出动,要出动就全动。

函数的意义1:在出现大量程序代码相同时候,可以为它门封装成一个function,这样只调用一次,就能执行很多语句。

 


 

1.2函数的参数

定义在函数内部的语句,都是相同的,但是实际上可以通过“参数”这个东西,来让语句有差别。

定义函数时,内部语句可能有一些悬而未决的量,就是变量,这些变量,要求在定义时都罗列在圆括号中:

 

function fun(a){
   console.log("我第"+a+"次说你好!");
}
fun(100);
fun(1);
fun(2);
fun(3);

前端笔记知识点整合之JavaScript(四)

调用的时候,把这个变量真实的值,一起写在括号里,这样随着函数的调用,这个值也传给了a变量参数。

罗列在function圆括号中的参数,叫做形式参数;调用时传递的数值,叫做实际参数。

 前端笔记知识点整合之JavaScript(四)

参数可以有多个,用逗号隔开。

 

function sum(a,b){
   console.log(a + b);
}
sum(3,5);     //8
sum(8,11);    //19
sum("5",12);  //512
sum(10);      //nan,因为a的值是10,b没有被赋值是undefined,10+undefined=nan
sum(10,20,30,40,88); //30,后面的参数没有变量接收

函数的意义2:在调用函数时,不用关心函数内部的实现细节,甚至这个函数是你网上抄的,可以运行。所以这个东西,给我们团队开发带来了好处。

 

定义函数的时候,参数是什么类型,不需要指定类型:

调用的时候,传进去什么类型,ab变量就是什么类型

 

 sum("5",12);  //512,做的是连字符串运算

 

另外,定义和调用的时候参数个数可以不一样多,不报错。

前端笔记知识点整合之JavaScript(四)

 

 sum(10);      

因为只给a变量赋值,b没有被赋值,b被隐式的varb的值是undefined10+undefined=nan

 前端笔记知识点整合之JavaScript(四)

 

 sum(10,20,30,40,88); //30

只有前面两个参数被形参变量接收了,后面的参数没有变量接收,就被忽略了。

//封装一个函数,计算m+....+n的和
//比如m是4,n是15,4+5+6+7...+15
function sum(m,n){
   var s = 0; //累加器,累加和
   for(var i = m;i <= n;i++){
       s+=i //s = s + i;
   }
   console.log(s);
}
sum(1,100); //计算1到100的和
sum(10,13); //计算10+11+12+13的和
sum(13,10); //输出0,所以你就知道函数顺序关机,定义顺序是什么,传递顺序就是什么。

1.3函数的返回值

函数可以通过参数来接收东西,还可以通过return关键字来返回值,“吐出”东西。

 

function sum(a,b){
   return a+b; //现在这个函数的返回值就是a+b的和
}

//sum没有输出功能,就要用console.log输出
console.log(sum(3,8));       //计算sum(3,8);实际上称为表达式,需要计算,计算后是11
console.log(sum(3,sum(4,5)));//输出12,实际上两次执行了sum函数,先执行内层的,计算出9,然后sum(3,9)就是12

函数有一个return的值,那么现在这个函数,实际上是一个表达式,换句话说这个函数就是一个值。

所以这个函数,可以当做其他的函数参数。

 

前端笔记知识点整合之JavaScript(四)

 

 sum(3,sum(4,5));

 

程序从内层执行到外层,sum(3,9)

函数可以接收很多值,但是返回一个值。

 

函数的意义3:模块化编程,让复杂的逻辑变得更简单。

函数只能有唯一的return,有if语句除外。

 

程序遇见return,会做两件事:

1、立即返回结果,返回到调用它的地方

2、不执行return后面的代码。

 

function fun(){
   console.log(1);
   console.log(2);
   //return; //返回一个空值,undefined
   return "你好啊!";
   console.log(3); //这行语句不执行,因为函数已经return,所以会终止执行后面的代码。
}
console.log(fun();

前端笔记知识点整合之JavaScript(四)


1.4函数模块化编程

实现前提:函数有返回值,可以作为其他函数执行时传的实参。

习惯将复杂工作,进行一步步的分工,将一部分工作的结果作为下一步工作的条件。

将程序中某个单独的功能制作成单独函数,这就是造*的过程。

 

业务逻辑上:将所有的*进行拼装。

将程序分成有层次的模块,制作过程中一部分函数要有返回值,执行结果作为另一些模块的参数、条件。

 

现在做一个程序,输出2~100的所有质数,所谓的质数,就是只有1和原数本身两个约数,没有其他约数。

把一个复杂的问题,拆分成一个个小问题,每个都是一个单独的函数:

逻辑思维:约数个数函数 → 判断质数函数 → 高层业务

编程需要逆向思维编程:制作约数个数函数 → 制作判断质数函数 → 高层业务

 

函数思维找质数:

//约数个数函数:能够传入一个数字,返回它的约数个数
function yueshugeshu(a){
   //计算这个数字的约数个数
   var count = 0; //累加约数个数
   for(var i = 1;i <= a;i++){
       if(a % i == 0){ //判断是否为约数
           count++;
       }
   }
   return count;
}
//判断是否是质数,如果一个函数名字取名为is,就暗示了将返回布尔值
//要么返回true,要么返回false。
//接收一个参数m,返回是否为质数(true或false)
function iszhishu(m){
   if(yueshugeshu(m) == 2){
       return true;
   }else{
       return false;
   }
}
//寻找1~100的质数
for(var i = 1;i <= 100; i++){
   if(iszhishu(i)){ //可以省略==true的判断
       //iszhishu()给我们返回了true和false
       console.log(i);
   }
}

利用函数验证哥德巴赫猜想:用户输入偶数拆分两个质数和:

哥德巴赫猜想:任何一个偶数,都可以拆分为两个质数的和。

现在要求,用户输入一个偶数,你把所有的质数拆分可能,写出来。

 

比如:

4 = 2 + 2

6 = 3 + 3

8 = 3 + 5

48 = 5 + 43

 

代码见案例:

约数个数函数,里面的细节不需要关心,它足够的鲁棒,就能返回约数个数。

上层的函数,可以使用下层的api

 

//约数个数函数:能够传入一个数字,返回它的约数个数
function yueshugeshu(a){
   //计算这个数字的约数个数
   var count = 0; //累加约数个数
   for(var i = 1;i <= a;i++){
       if(a % i == 0){ //判断是否为约数
           count++;
       }
   }
   return count;
}
//判断是否是质数,如果一个函数名字取名为is,就暗示了将返回布尔值
//要么返回true,要么返回false。
//接收一个参数m,返回是否为质数(true或false)
function iszhishu(m){
   if(yueshugeshu(m) == 2){
       return true;
   }else{
       return false;
   }
}
//哥德巴赫猜想,用户输入一个数字
//验证偶数是否能被拆分两个质数的和,拆分的思想就是穷举法
//比如用户输入48,那么就:
//看看1、47是不是都质数
//看看2、46是不是都质数
//看看3、45是不是都质数
//...
var even = parseint(prompt("请输入一个偶数"));
for(var i = 4;i < even;i++){
   if(iszhishu(i) && iszhishu(even - i)){
       console.log(even + "可以拆分为" + i + "与" + (even - i) + "的和");
   }
}

利用函数验证哥德巴赫猜想-一百万以内的偶数拆分:优化

 

function yueshugeshu(a){
    ...
}
function iszhishu(m){
    ...
}
//注意验证,验证偶数能否被拆成两个质数
waiceng:for(var i = 4 ; i <= 1000000 ; i+=2){
    for(var j = 2 ; j < i ; j++){
        if(iszhishu(j) && iszhishu(i - j)){
            console.log(i +"可以拆分为"+  j +"与"+  (i - j) + "的和");
            continue waiceng;
        }
    }
}

1.5函数递归

函数可以自己调用自己,就是递归。

function haha(){
   console.log("哈哈");
   haha();//调用自己
}
haha();
function sum(a){
   if(a == 1){
       return 1;
   }else{
       return a + sum(a-1); //10 + 9 + 8 + sum(7)
   }
}
console.log(sum(10));

斐波那契数列就是经典的递归算法:

1 1123581321345589144233...

输出斐波那契数列

 

只需要一个函数,就可以搞定全部问题。

fib(n); 就能得到第n位的数字

fib(2) = 1

fib(3) = 2

fib(4) = 3

fib(5) = 5

...

fib(10) = 55

 

function fib(n){
   if(n == 1 || n == 2){
       return 1;
   }else{
       return fib(n - 1) + fib(n - 2);
   }
}
// console.log(fib(10));

for(var i = 1;i <= 50;i++){
   console.log(fib(i));
}

1.6函数表达式

定义函数除了使用function之外,还有一种方法,就是函数表达式。就是函数没有名字,称为“匿名函数”,为了今后能够调用它,我们把这个匿名函数,直接赋值给一个变量。

var haha = function(){
   console.log("哈哈");
}
// console.log(haha);
haha(); //以后要调用这个函数,就可以直接使用haha变量调用。

 

等价于:

function haha(){
   console.log("哈哈");
}
haha();

如果这个函数表达式的function不是匿名,而是有名字的:

var haha = function xixi(){
   console.log("哈哈");
}
xixi();  //这是错误的
haha();  //这的对的

 

那么js表现非常奇怪,在外部只能用haha()调用,xixi()会引发错误。

前端笔记知识点整合之JavaScript(四)

也就是说,js这个奇怪的特性,给我们提了个醒,定义函数,只能用以下两种方法,不能杂糅:

 function haha(){}

 

 var haha = function(){}

 

错误的:

var haha = function xixi(){}

 


1.7函数声明的提升(预解析)

 

//先调用,可以输出,因为有函数声明提升的特性
fun();
fun();
fun();
//后定义
function fun(){
   console.log("我是函数!");
}

不会报错。

js在执行前,会有一个预解析的过程,把所有的函数声明和变量的声明,都提升到了最开头,然后再执行第一行代码。所以function定义在哪里,都不重要,程序总能找到这个函数。

 

函数声明头可以提升,js程序执行前,都会有一个函数预解释阶段,预解释阶段是自动进行的

函数优先:函数声明和变量声明都会被提升,但是面试常考的一个细节是:函数会被首先提升,然后才是变量。

函数提升是没节操的,无视if等语句的判断,强制提升

javascript世界中,函数是一等公民。

 

函数声明会被提升,但是函数表达式却不会被提升:

fun();
var fun = function(){  //因为它是函数表达式,而不是function定义法
   alert("我是函数!");
}

前端笔记知识点整合之JavaScript(四)

又给我们提了个醒,没有特殊的理由,都要用function haha(){}来定义函数。

 

函数优先:

aaa(); //现在这个aa到底是函数,还是变量5?
console.log(aaa);//函数优先,遇见同名的标识符,预解析阶段一定把这个标识符给函数
var aaa = 5; //定义一个变量,是5
function aaa(){
   alert("我是aaa函数!")
}

面试题:

函数优先,现在foo这个标识符冲突了,一个函数叫foo,一个变量也叫foo。预解析阶段,如果遇见标识符冲突,这个标识符给函数。

 前端笔记知识点整合之JavaScript(四)


1.8函数是一个引用类型

基本类型:numberstringbooleanundefinednull

引用类型:objectfunctionarrayregexpmathdate

 

function fun(){}
var haha = function (){}

console.log(typeof fun); //引用类型中的function类型
console.log(typeof haha);//引用类型中的function类型

前端笔记知识点整合之JavaScript(四)

函数也是一种类型,这个类型叫function,是引用类型的其中一种。

 

基本类型:保存值

引用类型:保存地址

前端笔记知识点整合之JavaScript(四)

 

 

现在变量a = 1,那么这个a变量里面存储1这个数字

 

//基本类型的赋值
var a = 1;
var b = a; //b得到的值是a的副本,a把自己复制了一份,给了b
b = 3;     //改变了b的值,a不受影响
console.log(a); //1
console.log(b); //3
//引用类型的赋值:
//定义了一变量a,引用了一个function
//这个a变量存储的是这个匿名函数的内存地址
var a = function(){
   alert("我是一根函数");
}
var b = a;  //就是把匿名函数的地址也给了b。
b.xixi = 1; //给b添加一个属性
console.log(a.xixi); //输出a的xixi属性,a也有这个属性了
//b的xixi属性和a的变量都改变了,因为都是指向同一个对象(同一个内存地址)
b.xixi++;
b.xixi++;
b.xixi++;
console.log(a.xixi);
console.log(b.xixi);

前端笔记知识点整合之JavaScript(四)

 

 


总结:

预解释:在js中,代码从上到下执行之前,(浏览器默认)首先会把所有带varfunction关键字的进行提前声明或者定义

var num=88;

声明(declare):相当于种树时"挖坑"  var num; 只声明没有定义时,num的默认值是undefined

定义(defined):相当于种树时"栽树"  num=88;(给变量赋值)

 

在预解释的时候,带var和带function的还不一样:

var:只是提前的声明(定义赋值的部分是在代码执行的时候完成的)

function:提前的声明+定义

 

在浏览器加载html页面时,首先会开辟一个供js代码执行的环境-->"全局作用域"(window/global)

栈内存(作用域):存储基本数据类型的值;提供js代码执行的环境;

 

堆内存:在js中,对于引用数据类型来说,首先会开辟一个新的内存空间,然后把代码存储到这个空间中,最后把空间的地址给相关的变量--->我们把新开辟的这个内存空间称为"堆内存"

堆内存的作用:存储引用数据类型值

 


 

二、作用域

前端笔记知识点整合之JavaScript(四)

 

 

2.1函数能封闭住作业域

变量的作用域无非就两种:全局变量和局部变量。

2.1.1全局变量(全局作用域)

全局变量:在最外层函数定义的变量拥有全局作用域,即对任何内部函数来说,都是可以访问的。

言外之意:如果变量没有定义在任何的function中,那么它将在程序中任意范围内都有定义:

 

var a = 100; //定义在全局的变量,在程序任何一个角落都有定义
function fn(){
   console.log("我是函数里面的语句,我认识全局变量a值为:" + a);
}
fn();
console.log("我是函数外面的语句,我认识全局变量a值为:" + a);

前端笔记知识点整合之JavaScript(四)


2.1.2局部变量(局部作用域)

局部变量:和全局作用域相反,局部作用域一般只在固定的代码片段内可访问,而对于函数外部是无法访问的。

例如:变量定义在function里面,这个变量就是局部变量,只在当前这个function函数内部能使用。在函数外部不能使用这个变量,出了这个function,就如同没有定义过一样。

 

function fn(){
   var a = 100; //定义在函数的变量,局部变量
   console.log("我是函数里面的语句,我认识变量a值为:" + a);
}
fn();
console.log("我是函数外面的语句,我认识变量a值为:" + a);

前端笔记知识点整合之JavaScript(四)

avar在了function里面,所以现在这个a变量只能在红框范围内有定义:

es5语法中,javascript变量作用域非常简单,能关住作用域的只有一个,就是:函数。

 前端笔记知识点整合之JavaScript(四)

【总结】:

● 定义在function里面的变量,叫做局部变量,只在function里面有定义,出了function没有定义的。

● 定义在全局范围内的,没写在任何function里面的,叫做全局变量,都认识。

【原理】:

全局变量在定义时,就会直接生成一个新的变量,在任何位置查找变量都有定义。

局部变量定义在函数内部,函数如果不执行,相当于内部的代码没写,局部变量等于从未定义过,在函数执行时,会在函数作用域内部立即定义了一个变量,使用完之后,变量立即被销毁。所以在外部永远找不到局部变量定义。

 


 

2.2作用域链

作用域链:根据在内部函数可以访问外部函数变量的这种机制,用链式查找决定哪些数据能被内部函数访问。

当遇见变量时,js引擎会从其所在的作用域依次向外层查找,查找会在找到第一个匹配的标识符时停止。

 

在私有作用域中出现了变量,首先看是否为私有的,如果是私有变量,那么就用私有的即可。如果不是私有变量,则往当前作用域的上级作用域查找,如果上级作用域也没有,则继续往上查找....一直找到window为止。

 

//变量的作用域,就是它var的时候最内层的function
function outer(){
   var a = 1; //a的作用域是outer
   inner();
   function inner(){
       var b = 2;  //b的作用域是inner
       console.log(a); //能输出1,a在本层没有定义,就往上找
       console.log(b); //能输出2
   }
}
outer();
console.log(a); //报错,因为a的作用域是outer

前端笔记知识点整合之JavaScript(四)

多层嵌套:如果有同名的变量,那么就会发生“遮蔽效应”:

 

var a = 10; //全局变量
function fn(){
   console.log(a); //undefined,提升声明了局部变量
   var a = 13;     //把外层的a变量遮蔽了,这函数内部看不见外层的a
   console.log(a); //输出13,变量在当前作用域寻找,找到a定义为13
}
fn();
fn();
fn();
console.log(a); //10,变量在当前作用域寻找,找到全局a

前端笔记知识点整合之JavaScript(四)

 

 

一个变量在使用的时候得几?就会在当前作用域去寻找它的定义,找不到,去上一层找,直到找到全局(window),如果全局也没有,就报错。这就是作用域链。

 

题目:

var a = 1;        //全局变量
var b = 2;        //全局变量
function outer(){
    var a = 3;        //遮蔽了外层的a,a局部变量
        function inner(){
            var b = 4;  //遮蔽了外层的b,b局部变量
            console.log(a);   //① 输出3,a现在在当前层找不到定义的,所以就上一层寻找
            console.log(b);   //② 输出4
    }
    inner();        //调用函数
    console.log(a);    //③ 输出3
    console.log(b); //④ 输出2 b现在在当前层找不到定义的,所以就上一层寻找
}
outer();        //执行函数,控制权交给了outer
console.log(a);    // ⑤ 输出1
console.log(b); // ⑥ 输出2

2.3不写var就自动成为全局变量了

需要注意,函数内部声明的时候,一定要用var命令,如果不用,实际上声明了一个全局变量。

 function fn(){

    a = 100;//这个a第一次赋值时,没有var,所以就自动在全局作用域var了一次

 }

 fn();

 console.log(a);//100

 

这是js的机理,如果遇见一个标识符,从来没有var过,并赋值了:

 a = 100;

那么就会自动在全局作用域定义var a;

 


2.4函数的形参变量,会默认定义为这个函数的局部变量

var a = 0;
var b = 0;
function fn(a,b){
   a = 3;
   b = 4;
   console.log(a,b);
}
fn();
console.log(a);
console.log(b);

前端笔记知识点整合之JavaScript(四)

a,b就是fn内部的局部变量,只能在当前function函数内部使用,出了fn就没有定义。

 


2.5全局变量的作用

在函数内部使用自己的变量,尽量定义为局部。

全局变量有自己独特的用途:累加、传递。

累加:函数没执行一次,都要求变量在原来基础上发生变化。

功能1:通信,共同操作传递同一个变量

两个函数同时操作一个变量,一个增加,一个减少,函数和函数通信。

 

var num = 0; //全局变量
function add(){
   num++;
}
function remove(){
   num--;
}
add();
add();
add();
add();
remove();
remove();
add();
add();
console.log(num); //4

功能2:累加,重复调用函数的时候,不会重置

 var num = 0;
 function baoshu(){
    num++;
    console.log(num);
 }
 baoshu();//1
 baoshu();//2
 baoshu();//3
 

 

如果num定义在baoshu里面,每次执行完函数,作用域就被销毁,所以里面变量都是全新的。

function baoshu(){
    var num = 0;
    num++;
    console.log(num);
}

baoshu();    //1
baoshu();    //1
baoshu();    //1

 2.6函数定义也有作用域

function outer(){
   var a = 10;
   function inner(){ //局部函数
       console.log("哈哈");
   }
}
outer();
inner(); //报错,因为全局作用域下,没有inner函数的定义
console.log(a);//报错

公式:

function 大(){
   function 小(){
 
   }
   小(); 可以运行
}
小(); //不能运行,因为小函数定义在大函数里面,离开大函数就没有作用域。

三、闭包

闭包有两个作用:

1、可以读取自身函数外部的变量(沿着作用域链寻找)

2、可以让这些外部变量始终保存在内存中

3.1闭包

推导过程:之前已经学习过,inner这个函数不能在outer外面调用,因为outer外面没有inner定义。

当函数执行的时候,会形成一个新的私有作用域,来保护里面的私有变量不受外界干扰,我们把函数的这种保护机制--->"闭包"

 

function fn(){

}
fn();

function outer(){
   var a = 100;
   function inner(){
       console.log(a);
   }
}
outer();
inner(); //在全局作用域调用inner,全局没有inner的定义,所以报错

但是我们就想在全局作用域下,运行outer内部的inner,此时我们必须想一些奇奇怪怪的方法。

有一个简单可行的方法,就是让outer自己returninner

非常经典的闭包案例,任何培训机构、书、讲闭包,一定是下面的案例:

 

function outer(){
   var a = 888;
   function inner(){
       console.log(a); //888
   }
   return inner; //outer返回了inner的引用
}
var inn = outer();//inn就是inner函数了
inn(); //执行inn,全局作用域下没有a的定义,但是函数闭包,能够把定义函数时的作用域一起记忆住,输出888

一个函数可以把自己内部的语句,和自己声明时,所处的作用域一起封装成了一个密闭的环境,就叫“闭包”。

 前端笔记知识点整合之JavaScript(四)

每个函数都是闭包,每个函数天生都能够记忆自己定义时所处的作用域环境。但是,我们必须将这个函数,挪到别的作用域,才能更好的观察闭包。这样才能实验它有没有把作用域给记住

我们发现,把一个函数从它定义的那个作用域,挪走,运行。嘿,这个函数居然能够记忆住定义时的那个作用域。不管函数走到哪里,定义时的作用域就带到了哪里。这就是闭包。

 

闭包在工作中是一个用来防止产生隐患的事情,而不是加以利用的性质。

因为我们总喜欢在函数定义的环境中运行函数。从来不会把函数往外挪。那为啥学习闭包,防止一些隐患,面试绝对考。

 

使用全局变量接收,返回函数:

 

var inn; //全局变量
function outer(){
    var a = 250;
    var b = 500;
    //全局变量inn此时被赋值了一个函数
    //这个函数此时将立即生成闭包,记忆住此时所处的环境
    inn = function inner(){
        console.log(a);
        console.log(b);
    }
}
outer();
var a = 300;
var b = 400;
inn();//一个函数在执行时,找闭包里面的变量,不会理会当前作用域

闭包题目1

 

function outer(x){
   function inner(y){
       console.log(x+y)
   }
   return inner;
}
var inn = outer(3);//接收到了inner函数,inn就是inner
inn(5); //8
inn(7); //10
inn(10); //13

闭包题目2

 

function fun1(x,y){
   function fun2(x){
       console.log(x + y);
   }
   return fun2;
}
var f = fun1(3,4); //f就代表fun2
f();  //nan
f(6); //10

一般情况下:当函数执行会形成一个私有的作用域(形参赋值→预解析→代码执行),当这三步都进行完成后,浏览器会刚刚开辟的这个私有作用域进行回收,也就是说,函数执行完成,作用域立即销毁。

 


3.2闭包的性质

每次重新接收引用的函数时,闭包都是全新。

 

function outer(){
   var count = 0;
   function inner(){
       count++;
       console.log(count);
   }
   return inner;
}
var inn1 = outer();
var inn2 = outer(); //两个变量引用的是同一个inner函数,实际上两个引用变量中,都是全新闭包
inn1(); //1
inn1(); //2
inn1(); //3
inn1(); //4
inn2(); //1
inn2(); //2
inn1(); //5
inn1(); //6
inn1(); //7

无论它在何处被调用,它总是能访问定义时所处作用域中的全局变量。

每个新的函数,不管通过何种结构生成,闭包都是新的,作用域也是新的,语句也是新的。通过同一个结构组成两个不同的函数,直接不会互相影响。

实际应用:要考虑闭包对程序造成影响,了解原因,平时不会写个特殊结构。

 


3.3作用域销毁的问题

在函数中,return后面返回的值如果是一个函数,这个函数是不参与预解释的;函数体中return后面的代码也不执行,但是需要把后面的代码参预解释;

 

销毁的作用域:一般情况下,函数执行完成后,当前的作用域都立即销毁;

不销毁的作用域:

当函数执行时,在私有作用域中返回了一个引用数据类型的值(例如:函数、对象、数组...)

并且在函数外面,有变量接收了这个返回值,此时当前的这个私有作用域就被占用了,这个作用域也不能销毁了;

作用域不销毁,里面的私有变量也不再销毁了。

 

不立即销毁的作用域:

当函数执行时,在私有作用域中返回了一个引用数据类型的值(例如:函数、对象、数组...)

但是并没有变量在函数的外面接收,那么浏览器暂时先不销毁,等到浏览器空闲的时候,会自己销毁这个作用域。

 


 

ps:尽量让它越来越规范,前期的文章都是本人的学习时的笔记整理,希望看完后可以指点一二,提提意见多多交流; 

笔记流程:html>css>javascript>jquery>html5/css3>移动端>ajax>面向对象>canvas>nodejs>es678>vue>react>小程序>面试问题

意见请留言,邮箱:scarf666@163.com