浅谈作用域、this以及闭包

222 阅读6分钟

函数的声明方式

var f = function (){}
function f(){}
var f =()=>{}

形参 实参

            var f=function(a=10,b=100,...c){
                    //  a=a||10;b=b||100; 
                console.log(c)
                return a+b
            }
            f(1,2);
            f(1);
            f();

(形参默认值:a=a||10;b=b||100; 
            形参=形参||默认值 ,这是es6之前的赋值方法
            es6之后  就是直接赋值:a=10,b=100;
)
(剩余运算符: ...形参c; 
            把剩余的所有实参都放到c这个形参对应数组中去
)

            var f=(..ary)=>{
                console.log(ary)
            }
            f(1,2,3,4)
            f(1,2)

(arguments实参集合  箭头函数中没有这个关键字)
(this 函数的执行主体 谁让这个函数执行的)
(return 一个决定返回值 另一个是打断函数的执行)

例如:

var a='123465';
    a();    
    这样会报错,是因为函数才会加小括号  
var ary =[1,2,f2];
ary[2]();  
    // 索引2就是f2  再加上一个小括号,就代表f2().  
    // 也就是相当于 函数执行,就是函数名加小括号   
function fn2() {
    return function () {
        console.log(666)
    }
}
fn2()();  //这是一个种写法

(function(){

})()   //这是另一种写法

变量提升

        f();
        var f =()=>{
            console.log(1)
        }
        f();
        var f () {
            console.log(2)
        }
        f();
  • js 代码执行之前,先把代码中带var和带function的提前声明了,
  • var只声明不定义,function是声明+定义
     var a;  //声明
     a=12;   //定义
  • let const 暂时性死区: 也就是在let之上绝对不能调用对应的变量,一调用就报错。

例如:

    var a=10;
    funcution f(){
        console.log(a);
        let a =12;
    }
    console.log(a);
    let a =12;
    console.log('a'in window)
//let 和 const声明的变量是不存在变量提升的,不能在let 之前使用该变量 ,用了就报错.. 
//false    let声明的变量不会用在window中   
  • 变量提升只会提升等号左边的部分
  • var 出来的变量会在window增加一个对应属性:window是全局大对象
  • let const 声明的变量,不会再window中增加对应的属性

例如:

    console.log(a);
    console.log(b);
    var a=b=21;  //这样就是 一个undefined,一个报错
    var a=12,b=13;//这样就是undefined
  • function 声明的在{}块级作用域中只声明不定义;声明不定义默认值是undefined
  • function 声明的函数在整个代码执行之前,早就已经存在了,所以在整个js中都可以执行在条件句中的代码,也会进行变量提升,不管条件是否成立。此时的function只有声明,没定义。

let const var 之间的区别?

  • var 可以重复声明,let和const不能重复声明
  • let是变量,const是常量。
  • let const 没有变量提升,
  • let const 存在暂时性死区
  • let const 能够识别块级作用域

堆栈内存

if(1<2){
    let a=123;
}
console.log(a);  //这样就会报错
  • 栈内存 : 存储值类型,提供代码的运行环境;
  • 堆内存 : 存储引用数据类型;
  • 作用域 : 每个作用域就是js中的一个栈内存;
  •      全局作用域:页面打开的一瞬间就形成了。页面关闭时全局作用域销毁。
    
  •      私有作用域:在函数执行的时候才会形成。一般执行完函数后作用域就会销毁。但是,函数的返回值若是引用数据类型,则不能销毁。
    
                全局变量:在全局声明的变量
                私有变量:在私有作用域中声明的变量,形参
                函数执行:先开辟一个私有作用域,形参赋值 变量提升 代码执行
  •      块状作用域:在es6之后,所有的{}(除了对象)都是块状作用域。只有let const 能识别块状作用域,var不可以。这个大括号{}不是对象。理解成私有作用域就可以了。
    
  •       上级作用域 : 一个私有作用域的上级作用域是看     该函数在哪儿声明的。跟函数在哪里执行没有关系。
    
  • 对象的存储过程:

先开辟一个堆内存,把键值对一对儿对儿的存进去,完成之后把地址赋给对应的变量

  • 函数执行的过程:

先开辟一个私有作用域(也就是栈内存),然后给形参赋值(函数传参不传参都是有赋值的,因为默认值就是undefined),然后在执行变量提升。然后代码从上到下执行。

    for(var i = 0; i < 4 ; i++){
    setTimeout(()=>{
        console.log(1)//如果想要每秒输出i的值加一,就把var换成let  然后输出写i+1。 
    }),(1000*(i+1));//这个意思就是一秒输出一次。如果不加*(i+1),那就是同时输出四个1。
    } 

/************************ 换一个方法执行 ***********************/

    for(var j = 0; j < 4 ; j++){
    (function(n){
        setTimeout(()=>{
        console.log(n)
    }),(6000);
    })(j+1)
    }

接下来再看看几个简单例子:

var a =12; //全局变量
var  f = function(){
    console.log(a)  //undefined 存在变量提升的原因
    var a =100; //私有变量
    console.log(a) //100 内部复制为100 
}
f()//在代码执行之前,就有变量提升的这个阶段
console.log(a) //12,全局和私有两个不是同一个东西

/*********************** 两个对比看 ********************/

    var n=10;
    var f2=function(){
        console.log(n)//10 
        n=200;
        console.log(n)//200
    }
    f2()
    console.log(n)//200

let例子:

0

    var f3=function(c){
        //先形参赋值,在变量提升
        let c =100;
    }
    
// 暂时性死区:在let和const之前不能使用他们声明的任何变量

1

    var d=10;
    var f4=function(){
        console.log(d)
        let d=100;
    }
    f4();

2

    if(1<2){
        var e =12;
        let z =13;
    }
    console.log(e);
    console.log(z);

this函数执行主体

        var ary=[
                    1,
                    2,
                    function(){console.log(this)},
                    ()=>{console.log(this)};
            ];
            let f=ary[2];
            f();         //window
            ary[2]();    //ary.2() ary
            ary[3]();    //ary.3   //window
  • 时间绑定中的this都是当前操作的元素
  • 自执行函数中的this是window。
  • 一般函数执行时,内部this 看’点‘,点前边是谁this就是谁

总结:

    1.箭头函数的this 当做变量去处理了。
    2.普通函数this指向规律:
        *是将绑定函数中的this,是指向当前操作元素的
        *自执行函数中this是指向window的
        *其他的函数执行中的this看点(执行的时候看前面有没有点)

闭包(最终目的就是为了保护私有变量)

  • 闭包是什么?
  本质上就是一个变量保护机制,但平时说的闭包都是置一个不销毁的作用域,用来保护私有变量和存储某私有值。
  • 那些地方用过闭包?
 自己开发某个模块时,或者自己封装一个写工具库的时候才会使用闭包。
  • 闭包的优缺点?(→)

一个不销毁的作用域(栈内存).

  •    1.保护私有变量不受外界的污染
    
  •    2.存储值
    

闭包缺点: 不销毁的作用域,用的多了会造成内存泄漏


a=123;
var f=function(){
    var a=10;
    var b=13;
    var f1=function(){
        console.log(a,b) 
        }
        return {f1}  
}
var obj = f()
obj.f1()

var obj1=(funcution(){
    var a =12;
    var b=13;
    return function(){
        console.log(a,b)
    }
})()
obj1()

垃圾回收、堆栈内存销毁:

  • 堆内存的销毁:

谷歌浏览器会定期检查堆内存,会销毁没有被引用的堆内存。

  • 栈内存的销毁:

全局作用域销毁:只有在页面关闭的时候才会被销毁

  • 私有作用域销毁:只有当函数的返回值是一个引用数据类型的时候才不会被销毁。