JS中的作用域以及函数

作用域

作用域指的是变量有效存在的某个区间或者范围内
所谓的变量有效存在:指的就是在这个区间内可以对变量进行读(获取它)写(修改它)的操作;

变量的作用域

变量可以分为你局部变量和全局变量,

全局变量

作用于全局环境上的变量,在浏览器中,全局环境指的就是window对象(全局环境是一最外围的执行环境,可以在代码任意位置访问到)

1、例:

var a=10;  //定义一个全局变量
function foo(){
    console.log(a);   //取到全局变量a
}
foo(); //调用foo函数打印出a的值
局部变量

只能在函数内部读写的变量,在外部无法访问到它。

2、例:

function bar(){
    var b=10; //在函数内定义变量b
}
console.log(b); //报错:b is not defined

在函数内通过var 声明的一个变量就是局部变量。如:b
此处的b就是一个在函数内部定义的局部变量,在函数外部无法访问到它。

3、例:当变量同名时的问题

var a=10;
function fn(){
    a=20;
    console.log(a);
}
fn();  //20;

注意:函数内并没有定义a,而是访问到了全局中的变量a,最初a的值为10,在函数调用时,将20重新赋给了a,所以调打印出来的a为20;

作用域链

代码在一个作用域中执行时,会自动创建相关资源(变量和函数)的一个作用域链,它用来保存当前执行代码所需要的所有变量和函数,默认遵循“从里到外”的查找规则

    function sum(a){
        var b=a*2;  //传入3,计算出b=6
        function bar(c){
            console.log(a,b,c);
        }
            bar(b*3);  //调用bar(18)   
    }
    sum(3); //  3,6,18

1、此处函数sum在执行时,传入参数3(即sum中的形参a),在函数内声明了b,此时b=6,
2、在sum函数内又嵌套了一个函数bar,且通过bar(b*3)调用它,当上面传入b为6时,这里的调用即bar(18),对应的,18就是传入到bar函数中的参数c,
3、在bar中console.log打印出a,b,c的值为:3,6,18

JS的编译原理

1、浏览器通过js引擎来读写一段js代码,这里的js引擎是浏览器的一段程序,负责去读写js代码并执行。(现在很多浏览器都是用V8引擎来解析代码的)
那么,js是怎么去读写js代码的呢?——js的编译原理

js代码在读写时,至少需要经历两个步骤:预解析、逐行执行代码

1、代码的预解析
* 预解析*:浏览器通过查找var,function关键字,来解析代码声明的变量及函数,当查找到var定义的变量时,就会在内存中开辟一个“仓库”用于存放这个变量,并默认的给它赋初始值undefined;当查找到function定义的函数时,会在这个“仓库”中存放这个函数名,并将这个函数的代码块赋值给它自身。
例:

 console.log(a)  //undefined
 var a=10;
function fn(){
        alert(1);
    }    
console.log(a)  //10
console.log(fn)  //函数fn的代码块

在上面例子中:

1)、在js预解析时,查找到关键字var,取到a放到“仓库”中,并默认给它赋初始值undefined
2)、接着解析时查到了function关键字,并将fn这个函数拿到“仓库”中,并将自身的代码块赋给fn
当解析完成后,开始逐行执行代码
1、第一个console处,从仓库中取出的a,此时为默认的undefined
2、向下执行,遇到a=10,即将10赋给了a,此时会用10在仓库中覆盖原来的undefined,所以,第二个console打出来的a为10;
3、打印fn,即从“仓库”中调出fn这个函数名,对应的就是它的代码块
【注意:如果用console.log(fn())的形式打印函数,此时的fn并没有返回值,那么会在调用fn后,默认为返回undefined】

递归函数

在函数内可以调用函数,当函数内调用的是函数自身时,就称之为递归函数;
例1:

function fn(n){
    if(n<=1){
        return 1;  //递归函数出口:当n<=1时就返回1,
    }
    return n*fn(n-1);  //再次调用fn,并传n-1进去
}
    console.log(fn(3));  //6

注意:递归函数一定要有出口,不然就会造成死循环;
例2:打印出1-3 3-1 共打印6次

function foo(n){
    console.log(n);
    n++;
    if(n<=3){
        foo(n);
    }
    console.log(n-1);
}
foo(1);

打印结果:1 2 3 3 2 1
在foo函数内,当n<=3时,就会调用自身,每次调用时都会让n++,所以执行顺序如下:
console.log(1); //1
n++; //n=2
2<3,满足if里的条件,所以执行foo(2);
console.log(2); //2
n++; //n=3;
3=3,满足if条件,所以执行foo(3);
console.log(3); //3
n++; //n=4;
4>3,不满足if,所以开始执行下一行代码;
console.log(4-1) //3
此时,foo(3)执行完了,接着返回到foo(2)中,向下执行
console.log(3-1) //2
此时,foo(2)执行完了,接着返回到foo(1)中,继续向下执行
console.log(2-1) //1

匿名函数

函数的创建方式有两种:函数声明方式,函数表达式方式
1、例:

//函数声明:
function foo(){
    console.log("这是函数声明写法");
}
foo();
//函数表达式:
var bar=function(){
    console.log("这是函数表达式写法");
}
bar();
//匿名函数:
function(){
    console.log("这是一个匿名函数")
}

匿名函数即没有名称的函数,要实现匿名函数的自执行,可以通过给它加()的方式将其转换为函数表达式,再接()调用即:(匿名函数)()——实现匿名函数的自执行
(匿名函数)——给匿名函数外加一个括号表示函数表达式
例:

  (function(){
        console.log("这是一个匿名函数")
    })()

闭包

闭包:指在函数中嵌套函数;

闭包的特点:
1、内部函数可以使用外部函数的参数或变量,
2、闭包写法的函数的参数或变量不会被 js的垃圾回收机制所收回。(js的垃圾回收机制:当函数调用完后,内存会将函数调用 时产生的数据收回)

闭包的作用:
1、私有化变量,避免变量的全局污染;
2、使变量常驻内存;
3、模块化代码,使函数相互之间的变量不会影响
闭包的缺陷:
会造成内存泄露
因为闭包中的变量不会被垃圾回收机制收回,所以会造成内存浪费
例:

function fn(a){
    function aa(){
        a++;
        console.log(a);
    }
    return aa;
}
var c=fn(10);
c();   //11;
c();    //12;
c();    //13;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sophie_U

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值