zoukankan      html  css  js  c++  java
  • JS中变量、作用域的本质,定义及使用方法

    全局作用域和局部作用域

    全局作用域

    局部作用域:函数作用域

    全局作用域在全局和局部都可以访问到,局部作用域只能在局部被访问到

    var name="cyy";
    
    function fn(){
        var age=25;
        console.log(name);//cyy
        console.log(age);//25
    }
    fn();
    console.log(name);//cyy
    console.log(age);//报错age is not defined

    js没有块级作用域,此处依然是全局变量

    if(){
        //全局变量
    }
    for(){
        //全局变量
    }

    在函数内,用var声明的变量是局部变量,没有用var的是全局变量

    function fn(){
        var x=y=1;
    }
    fn();
    console.log(y);//1
    console.log(x);//x is not defined
    var name="cyy";
    function fn(){
        name="cyy2";//没有用var,所以这里修改的直接是外面的全局变量name
        console.log(name);//cyy2
    }
    fn();
    console.log(name);//cyy2

    变量对象和作用域链

    此处全局作用域下有name变量和fn函数,实际上都是window对象上的

    全局作用域的变量对象的属性和方法,看得见摸得着,可以访问

    var name="cyy";
    function fn(){
        var age=25;
        function fn2(){
            var gender="girl";
        }
    }
    console.log(window.name===name);//true
    console.log(window.fn===fn);//true

    此处fn下有age变量和fn2函数

    局部作用域的变量对象的属性和方法,看不见摸不着


    访问不存在的变量,会报错

    访问不存在的属性,不会报错

    这里补充一下:一开始我用name进行测试,出现了意想不到的情况

    后来发现name是js对象的内置属性,应尽量避免命名时使用,建议使用 _name

    打印window对象即可查看到name

    console.log(window._name);
    console.log(_name);

     作用域链:

    如果要查找一个变量,首先会在当前作用域内查找;

    如果没有,就去上一级作用域内查找;

    直至到window

    因此,里层可以访问到外层的变量,但是外层无法访问到里层的变量


    延长作用域链的方法

    with()相当于开辟了一个新的作用域

    里面的属性,找不到的话会去外层window上查找

    但是不推荐使用with,会带来后续其他问题

    var person={};
    person.name="cyy";
    person.age=25;
    var score=90;
    
    console.log(person.name);//cyy
    console.log(person.age);//25
    console.log(score);//90
    
    with(person){
        name="cyy2";
        age=cyy2;
        score=99;
    }
    console.log(person.name);//cyy2
    console.log(person.age);//cyy2
    console.log(score);//99

    循环添加点击事件,获取到每个点击按钮的index

    单击方法需要在循环外面定义,否则只会输出循环结束后的 i 的值

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    <body>
        <button>1</button>
        <button>2</button>
        <button>3</button>
    <script>
    
    var btns=document.querySelectorAll("button");
    for(var i=0,len=btns.length;i<len;i++){
        myClick(i);
    }
    function myClick(i){
        btns[i].onclick=function(){
            alert(i);
        }
    }
    </script>
    </body>
    </html>

    不存在的变量或函数会报错;不存在的属性或方法,返回undefined;||是短路操作,形如a||b,如果a的值转换成布尔值是true的话,a||b等于a;如果a的值转换成布尔值是false的话,a||b等于b

    此处window.person = undefined,因此返回window.person="cyy"

    //console.log(person || (person="cyy"));//报错,person is not defined
    console.log(window.person || (window.person="cyy"));//cyy

    如果在循环中直接绑定点击事件,由于在循环时只是绑定;等到去点击按钮是,循环早已结束,因此 i 永远都是循环结束后的值

    var btns=document.querySelectorAll("button");
    for(var i=0,len=btns.length;i<len;i++){
        btns[i].onclick=function(){
            alert(i);//3
        }
    }

    JS解析机制:预解析

    最简单的情况

    var age=25;
    function fn(){
        var age=26;
        console.log(age);//26
    }
    fn();

    JS的解析过程:

    预解析

    逐行读代码

    var age=25;
    function fn(arg){
        console.log(age);
        var age=26;    
    }
    fn();
    /*
    js的解析机制:
    window:
        age undefined 25
        fn function fn(arg){
                console.log(age);//undefined
                var age=26;    
                console.log(age);//25
            }
    fn:
        arg(参数也作为局部变量) undefined
        age undefined
    首先查找所有的var,将var声明的变量都赋值给undefined
    函数直接拿过来
    读取时,由于函数已经声明完了,所以正常执行时直接跳过
    直接执行fn()
    console.log(age)中的age是预解析的undefined
    */

    变量名冲突:预解析时都是undefined

    var age=25;//预解析:undefined
    ...
    var age=26;//预解析:undefined

    变量和函数名冲突:预解析时跳过变量名,只声明函数

    var age=25;//预解析:undefined
    function age(){
        
    }

    函数与函数名冲突:预解析时选择后面一个函数,第一个函数被干掉

    function age(){
        var age=24;
    }
    function age(){
        var age=25;
    }

    在某些老版本浏览器,比如低版本火狐中,无法解析 if 或者 for 内部的函数

    所以不推荐在代码块里定义函数

    if(){
        function fn(){
    
        }
    }
    
    for(){
        function fn2(){
            
        }
    }

    变量的解析机制:

    以var 声明的变量会预解析

    以let 声明的变量不会预解析

    函数的声明会预解析

    函数表达式不会预解析

    document.write(fn);
    function fn(){}

     函数的声明在预解析时就已经被提前声明了

    函数变量的赋值,预解析时被定义为undefined

    document.write(fn);//undefined
    var fn=function(){}

    提前打印后面的变量,打印出来是undefined

    console.log(age);//预解析为undefined
    var age=25;

    如果没有用var声明,则无法进行预解析,这样提前打印时,不知道有该变量的存在,就会报错

    console.log(age);//报错
    age=25;
    console.log(a);//打印出预解析的结果,是函数a()
    var a=1;
    console.log(a);//1
    function a(){
        console.log(2);//跳过没有执行
    }
    console.log(a);//1
    var a=3;
    console.log(a);//3
    function a(){
        console.log(4);//跳过没有执行
    }
    console.log(a);//3
    a();//此时a是3,不能当做函数执行,因此报错
    
    /*
    预解析时先查找var:找到var a=1;和var a=3;预解析结果为undefined
    然后查找到函数,发现函数a与变量重名,此时变量被干掉
    由于存在两个重名函数,前面的函数被干掉,因此a的预解析结果为:
    function a(){
        console.log(4);
    }
    */

    预解析是分标签进行的

    此处上下没有联系,会报错

    <script>
        console.log(a);//报错
    </script>
    
    <script>
        var a=1;
    </script>

    但是上下顺序对换不会出现问题,因为已经正常执行了,与预解析无关

    <script>
        var a=1;
    </script>
    
    <script>
        console.log(a);//1
    </script>
        var a=1;//undefined 1
        function fn(){
            console.log(a);//undefined
            var a=2;//undefined 2
        }
        fn();
        console.log(a);//1

    当函数内部没有var时,不会进行预解析,变量就会通过作用域链进行查找

    如果函数内部存在var,会进行预解析,变量提前打印就会是undefined

        var a=1;//undefined 1
        function fn(){
            console.log(a);//1 没有var进行预解析,通过作用域链找到外面的a
            a=2;//没有var进行预解析,通过作用域链改变a
        }
        fn();
        console.log(a);//2

    参数作为局部变量,也会进行预解析

        var a=1;//undefined 1
        function fn(a){
            //a作为参数,也是局部变量,也会进行预解析
            console.log(a);//undefined
            a=2;//此处修改参数a为2,全局变量没有影响
        }
        fn();
        console.log(a);//1
        var a=1;//undefined 1
        function fn(a){
            //a作为参数,也是局部变量,也会进行预解析
            console.log(a);//undefined 1
            a=2;//此处修改参数a为2,全局变量没有影响
        }
        fn(a);//执行时参数为a=1
        console.log(a);//1

    内存管理与垃圾收集机制

    释放无用的数据,回收内存

    自动  JS

    手动  Objective-C

    原理:找出无用的数据,打上标记,释放内存;周期性执行

    标记策略:标记清除   引用计数(不常用)


    IE

    JS : BOM  DOM

    老版本IE的BOM和DOM,是在c++ COM的基础上来实现的,基于引用计数法

    因此即使使用了标记清除法,还是存在循环引用的问题


    内存管理

    设置为null,即为解除引用

  • 相关阅读:
    asp.net页面刷新后样式就发生了改变
    ASP.NET MVC 入门系列教程
    javascript打开邮箱服务器
    jquery验证邮箱
    MySQL数据库的索引类型
    JS判断浏览器
    Silverlight 3 新特性
    moss 2007 添加关键字及最佳匹配
    vs2008 常用快捷键
    Microsoft Enterprise Library 5.0正式版本已经发布
  • 原文地址:https://www.cnblogs.com/chenyingying0/p/12294830.html
Copyright © 2011-2022 走看看