zoukankan      html  css  js  c++  java
  • JavaScript进阶之理解篇

    一、函数声明与函数表达式


    1、函数声明之后,可以在声明之前调用,也可以在声明之后调用,因为所有的函数声明(包括var声明的变量)都会在代码执行之前就加载到作用域中,

    函数名其实是一个Function类型的对象的引用,声明函数时函数名其实也就被赋值了;

    2、而函数表达式则不同,函数表达式是将函数赋值给一个变量,只有当代码执行到那一行的时候,函数才真正的有定义,因此这个变量只有在表达式之后才能使用,
    否则这个变量为undefined,如果这个变量不是通过var关键字声明,那么它就没有任何值。

    例1:
    fn1(); //fn1
    fn2(); //fn2 is not a function
    console.log(typeof fn2); //undefined
    function fn1(){
    	console.log("fn1");	
    }
    var fn2 = function(){
    	console.log("fn2");
    }
    fn2(); //fn2
    
     例2:
    var fn = 10;
    function fn(){
    	console.log("test");
    }
    console.log(fn); //10
    fn(); //fn is not a function
    

    在例2的代码中,变量声明会在代码执行之前就加载到作用域中,函数声明时,函数名就是一个Function类型的对象引用,此时它已经被赋值了,
    同一个变量被多次声明时,只会忽略后面的声明,但会执行后面的变量初始化;此时变量fn的值已经被覆盖了,值为10。
    例2中的代码可以理解为下面的代码:
    var fn;
    fn = function() {
      console.log("test");
    }
    fn = 10;
    console.log(fn);  //10
    fn(); //fn is not a function
    
    
    

    二、函数执行环境、变量对象与作用域链

    1、后台中每个函数的执行环境都有一个变量对象,全局环境中的变量对象(浏览器中的window对象)始终存在,当函数被调用时,其活动对象等同于变量对象,
    函数中的局部执行环境中的活动对象只存在函数执行过程中,函数执行完后,其活动对象就会被撤销;


    2、当一个函数第一次被执行时,会创建一个执行环境(execution context)和相应的作用域链(包含了所有外部函数的活动对象及全局变量对象),
    并把作用域链赋值给函数的一个特殊的内部[[Scope]]属性中,然后用this,arguments和其他参数初始化函数的活动对象(activation object),函数在执行时,
    会通过复制函数的[[Scope]]属性中的对象构建起执行环境的作用域链,注意函数本身也有一个作用域,作为执行环境作用域链的前端。


    3、理解函数执行环境、变量对象与作用域链,是理解闭包的关键所在。

    4、关于this,arguments对象需要注意的地方:

    当一个函数第一次被执行时,就会用this,arguments对象等其他参数初始化函数的活动对象,但是如果是闭包(即内部函数)在搜索这两个对象时,
    只会搜索到其活动对象为止,而不会搜索其他函数作用域中的活动对象,也就是说内部函数无法访问其包含函数的this,arguments对象,
    但是可以通过变量的形式来访问外部函数中的this,arguments对象,由于闭包的执行环境具有全局性,因此其this指向window对象。

    三、闭包及其所存在的问题

    闭包是指有权访问外部函数作用域中的变量的函数,创建一个闭包的方式就是在一个函数中创建另一个函数
    例3:
    function fn(name){
    	return function(){
    		return name	
    	}
    }
    var nm = fn("yjh");
    console.log(nm()); //yjh
    

    在例3中,fn中有一个匿名函数,即闭包,在上面代码的执行中,结果为yjh,因为闭包可以访问外部函数作用域中的变量,
    内部函数中的变量是从作用域链的前端开始搜索的,如果没有找到,直至搜索到作用域链的末端(全局执行环境)。

    闭包所存在的问题

    就拿例3中的例子来说,在函数fn执行完毕后,其变量name(包括其活动对象)并没有被销毁,因为内部函数(闭包)的作用域链包含了fn中的作用域,
    内部函数仍然在引用fn中的活动对象,当fn执行完后,其作用域链会被销毁,但是它的活动对象会继续保存在内存中,直到匿名函数被销毁,它的活动对象才会被销毁;
    由于闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存,过多的使用闭包会导致内存占用过多。

    四、块级作用域

        JavaScript中没有块级作用域,不像其他编程语言中拥有块级作用域的概念,但是我们可以在js中模拟块级作用域的概念,我们都知道,
    一般而言,函数执行完毕后,执行环境中的活动对象就会被撤毁,因此我们可以像下面这样:
    (function(){
    
    })()
    
    
    
        声明一个匿名函数,然后立即调用它,这样在这个匿名函数中声明的变量在函数执行完毕就会被撤毁,外部函数也不能访问其中定义的变量

    函数声明和调用的几种写法:
    function(){} //缺少(或意外)标识符(ie9以下的ie浏览器不会报错)
    function fn(){} //常规函数声明
    var fn = function(){} //赋值表达式
    (fn = function(){})() //函数被立即调用
    var fn = function(){}() //函数被立即调用
    (function(){})() //函数被立即调用

    总结:

    1、变量,函数声明都会提前加载到作用域中,函数赋值表达式只有当代码执行到那一行时才会有定义

    2、每一个函数都有一个执行环境(作用域),变量对象(活动对象),以及相应的作用域链(各个活动对象的指针列表),当函数执行时,作用域链会赋值给函数
    的内部特殊属性[[Scope]]以构建起函数执行环境的作用域链

    3、闭包就是指有权访问外部作用域的变量的函数,创建闭包的方式就是在一个函数中创建另一个函数,闭包会包含外部函数的作用域,因此占用的内存会比其他函数多,
    过多的使用闭包会使内存占用过多,还会导致外部函数的活动对象不会被释放,只有闭包的引用被撤销,外部函数作用域中的活动对象才会被释放

    4、闭包(内部函数)在搜索this,arguments对象时,只会在自身的活动对象中搜索,永远也不能访问外部作用域中的this,arguments对象,但可以通过变量赋值的
    形式来访问外部函数作用域中的this,arguments对象,由于闭包的执行环境具有全局性,因此其this对象指向window对象,全局环境中没有arguments对象的

    5、在js中还可以模拟其他语言中的块级作用域,即声明一个匿名函数,然后立即调用它

    
    
  • 相关阅读:
    codeforces C. Cows and Sequence 解题报告
    codeforces A. Point on Spiral 解题报告
    codeforces C. New Year Ratings Change 解题报告
    codeforces A. Fox and Box Accumulation 解题报告
    codeforces B. Multitasking 解题报告
    git命令使用
    shell简单使用
    知识束缚
    php 调用系统命令
    数据传输方式(前端与后台 ,后台与后台)
  • 原文地址:https://www.cnblogs.com/yangjunhua/p/2513340.html
Copyright © 2011-2022 走看看