• JavaScript经典语录


    Js的解析与执行过程:

    全局中的解析和执行过程:
    一:预处理:创建一个此法环境LE,

    扫描JS:1.用声明的方式声明的函数(不是函数表达式),2.用var定义的变量。加到预处理阶段的此法环境中

    全局环境中的预处理:预处理创建的词法作用域LE相当于windows

    二:命名冲突的处理:
    1.处理函数声明有冲突时,会覆盖,处理变量声明有冲突时,会忽略。
    2.两者都有时,函数声明的权重高一些,最终结果指向函数声明的引用
    三:全局函数的执行

    1.首先进行全局预处理
    2.执行完预处理后,代码一步步解析

    补充:运用词法的作用域,可以很好的解释一个带多个参数的函数只传递一个参数的例子。

     

    函数中的解析和执行过程:

    函数中的解析和执行过程区别不是很大,有一个(arguments)注意一下:

    1.函数的预处理和全局的预处理类似(只是加了一个arguments,调用函数时实际调用的参数个数)

    如果不是var 声明的变量,会变成最外部LE的成员,即全局变量

    JS的作用域和作用域链:

    作用域一般分为四类:块级作用域、函数作用域、动态作用域、词法作用域(静态作用域)

    块级作用域:(js没有块级作用域)

    函数作用域:没有纯粹的函数作用域

    动态作用域:没有动态作用域

    词法作用域(静态作用域)javascript的作用域为静态作用域

    f[[scope]]==LE==window

    分析:1.创建一个作用域对象f[[scope]]==创建它时的词法环境LE(LE==window)

            2.真正执行的时候(一步一步往上找)LE--->f.[[scope]]==window

    在词法解析阶段,就已经确定了相关的作用域。作用域还会形成一个相关的链条,称为作用域链

    new Function的情况又不一样

     

    问题:

    多个函数都想要一个变量,每次都要写一个好麻烦

    方法:将变量设置为全局变量

     

    问题:

    不是说要减少全局用量的使用么?因为在我们做大项目的时候难免要引入多个JS库,变量间的命名可能会有冲突,且出错后不易查找,这个时候我们该怎么办呢?

    方法:将变量设置在一个function中,

     

    问题:

    在外面又访问不到了,怎么办?

    方法:我们使用匿名函数的方法

    Javascript中的作用域链:

    当执行一段JavaScript代码(全局代码或函数)时,JavaScript引擎会创建为其创建一个作用域又称为执行上下文(Execution Context)

    在页面加载后会首先创建一个全局的作用域,然后每执行一个函数,会建立一个对应的作用域,从而形成了一条作用域链。

    每个作用域都有一条对应的作用域链,链头是全局作用域,链尾是当前函数作用域。

     

    作用域链的作用是用于解析标识符,当函数被创建时(不是执行),

    会将this、arguments、命名参数和该函数中的所有局部变量添加到该当前作用域中,

    当JavaScript需要查找变量X的时候(这个过程称为变量解析),它首先会从作用域链中的链尾也就是当前作用域进行查找是否有X属性,如果没有找到就顺着作用域链继续查找,直到查找到链头,也就是全局作用域链,仍未找到该变量的话,就认为这段代码的作用域链上不存在x变量,并抛出一个引用错误(ReferenceError)的异常。

    1.JavaScript中没有块级作用域,但是有词法作用域

    函数内部不用var关键字申明的变量,则默认该变量为全局变量

    在javascript中如果不创建变量,直接去使用则报错

    2.javascript中如果创建值而不赋值,则该值为unefined

       javascript 的函数在被执行之前,会将其中的变量全部申明而不赋值

    3.词法作用域是不可逆的

    闭包函数的由来:
    作用域的存在帮我们省不少事,如果想在函数A中调用函数B该怎么办?

    思路:我们给函数B设一个返回值,然后在函数A中调用,代码如下:

    function A(){
        function B(){
           console.log("Hello foodoir!");
        }
        return B;
    }
    var c = A();
    c();//Hello foodoir!
    

      

    这样我们就可以得到我们想要的结果。这样,我们基本上到了一个最简单的闭包形式。我们再回过头分析代码:

    (1)定义了一个普通函数A

    (2)在A中定义了普通函数B

    (3)在A中返回B(确切的讲,在A中返回B的引用)

    (4)执行A(),把A的返回结果赋值给变量 c

    (5)执行 c()

    把这5步操作总结成一句话:函数A的内部函数B被函数A外的一个变量 c 引用。当一个内部函数被其外部函数之外的变量引用时,就形成了一个闭包。

     

    来看下面的几种闭包:demo1:

    function fn(){
        var b = "foodoir";
        return function(){
            console.log(b);//foodoir
            return b;
        }
    }
    //console.log(b);//b is not defined
    var result = fn();
    console.log(result());//foodoir
    

    demo2:  

    var n;
    function f(){
        var b = "foodoir";
        n = function(){
            return b;
        }
    }
    f();
    console.log(n());//foodoir
    

    demo3:

    //相关定义与闭包
    function f(arg){
        var n = function(){
            return arg;
        };
        arg++;
        return n;
    }
    var m = f(123);
    console.log(m());//124
    //注意,当我们返回函数被调用时,arg++已经执行过一次递增操作了,所以m()返回的是更新后的值。
    

      

    demo4:闭包中的读取与修改

    //闭包中的设置与修改
    var getValue,setValue;
    (function(){
        var n = 0;
        getValue = function(){
            return n;
        };
        setValue = function(x){
            n = x;
        }
    })();
    //console.log(n);
    console.log(getValue());//0
    console.log(setValue());//undefined
    
    setValue(123);
    console.log(getValue());//123
    

      

    demo5:用闭包实现迭代效果

    //用闭包实现迭代器效果        
    function test(x){
        //得到一个数组内部指针的函数
        var i=0;
        return function(){
            return x[i++];
        };
    }
    var next = test(["a","b","c","d"]);
    console.log(next());//a
    console.log(next());//b
    console.log(next());//c
    console.log(next());//d
    

      

    demo6:循环中的闭包

    //循环中的闭包
    function fn(){
        var a = [];
        for(var i=0;i<3;i++){
            a[i] = function(){
                return i;
            }
        }
        return a;
    }
    var a = fn();
    console.log(a[0]());//3
    console.log(a[1]());//3
    console.log(a[2]());//3
    
    /*
     * 我们这里创建的三个闭包,结果都指向一个共同的局部变量i。
     * 但是闭包并不会记录它们的值,它们所拥有的只是一个i的连接,因此只能返回i的当前值。
     * 由于循环结束时i的值为3,所以这三个函数都指向了3这一个共同值。
     * */
    

      

    思考:如何使结果输出分别为0、1、2呢

    思路一:我们可以尝试使用自调用函数

    function fn(){
        var a = [];
        for(var i=0;i<3;i++){
            a[i] = (function(x){
                return function(){
                    return x;
                }
            })(i);
        }
        return a;
    }
    var a = fn();
    console.log(a[0]());//0
    console.log(a[1]());//1
    console.log(a[2]());//2
    

      

    思路二:我们将i值本地化

    function fa(){
        function fb(x){
            return function(){
                return x;
            }
        }
        var a = [];
        for(var i=0;i<3;i++){
            a[i] = fb(i)
        }
        return a;
    }
    console.log(a[0]());//0
    console.log(a[1]());//1
    console.log(a[2]());//2
    

      

    在这里,我们来对闭包进行更深一步的操作

    我们再将demo1的例子进行扩展

    代码示例如下:

    function funcTest(){
      var tmpNum=100; //私有变量    
      //在函数funcTest内
      //定义另外的函数作为funcTest的方法函数
      function innerFuncTest(
      {
           alert(tmpNum);
           //引用外层函数funcTest的临时变量tmpNum
      }
        
      return innerFuncTest; //返回内部函数
    }
        
    //调用函数
    var myFuncTest=funcTest();
    myFuncTest();//弹出100
    

      

    到这,我们对闭包的概念和用法有更加熟悉

    闭包和this相关

    闭包应用举例,模拟类的私有属性,利用闭包的性质,局部变量只有在sayAge方法中才可以访问,而name在外部也访问,从而实现了类的私有属性。

    function User(){
        this.name = "foodoir";  //共有属性
        var age = 21;    //私有属性
        this.sayAge=function(){
            console.log("my age is " + age);
        }
    }
    var user = new User();
    console.log(user.name); //"foodoir"
    console.log(user.age);  //"undefined"
    user.sayAge();   //"my age is 21"
    

    关于闭包更深入的了解

    (function(document){
        var viewport;
        var obj = {
            init:function(id){
               viewport = document.querySelector("#"+id);
            },
            addChild:function(child){
                viewport.appendChild(child);
            },
            removeChild:function(child){
                viewport.removeChild(child);
            }
        }
        window.jView = obj;
    })(document);
    

      

    这个组件的作用是:初始化一个容器,然后可以给这个容器添加子容器,也可以移除一个容器。功能很简单,但这里涉及到了另外一个概念:立即执行函数。 简单了解一下就行。主要是要理解这种写法是怎么实现闭包功能的。

     

    闭包并不是万能的,它也有它的缺点

    1、闭包会使得函数中的变量都保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页性能问题。另外在IE下有可能引发内存泄漏 (内存泄漏指当你的页面跳转的时候 内存不会释放 一直占用你的CPU 只有当你关闭了浏览器才会被释放);

    2、闭包会在父函数外部改变父函数内部的变量的值,所以不要随便改动父函数内部的值。

     

  • 相关阅读:
    Ajax核心对象和AjaxPro框架
    ASP.NET XML与JSON
    jQuery中Ajax的应用
    jQuery中操作表单与表格
    IOS 非常流畅的滑动tableView
    提高自己应用性能的总结架构篇
    LazyCode 自己开源的一个类库
    iOS 自己写的对话框中加入三个输入框
    icmp 流量抓取 转发 代理(2)
    sublime text ctags插件使用
  • 原文地址:https://www.cnblogs.com/sunliyuan/p/5988282.html
Copyright © 2020-2023  润新知