zoukankan      html  css  js  c++  java
  • 闭包和作用域

    1、执行上下文

      在一段 JS 脚本(即一个`<script>`标签中)执行之前,会先创建一个**全局执行上下文**环境,先把代码中即将执行的(内部函数的不算,因为不知道函数何时执行)变量、函数声明(和“函数表达式”的区别)都拿出来。变量先暂时赋值为`undefined`,函数则先声明好可使用。这一步做完了,然后再开始正式执行程序。这是在代码执行之前才开始的工作

      另外,一个函数在执行之前,也会创建一个**函数执行上下文**环境,跟**全局上下文**差不多,不过**函数执行上下文**中会多出`this` `arguments`和函数的参数。

    总结一下

    - 范围:一段`<script>`或者一个函数
    - 全局:变量定义,函数声明
    - 函数:变量定义,函数声明,this,arguments

    #参考实例
    console.log(a) // undefined var a = 100 fn('zhangsan') // 'zhangsan' 20 function fn(name) { age = 20 console.log(name, age) var age }

     2、 this

    定义:`this`的值是在执行的时候才能确认,定义的时候不能确认!

    因为`this`是执行上下文环境的一部分,而执行上下文需要在代码执行之前确定,而不是定义的时候。

    var a = {
      name: 'A',
      fn: function () {
      console.log(this.name)
    }
    } //this作为对象属性执行
    a.fn() // this === a
    a.fn.call({name: 'B'}) // this === {name: 'B'}
    var fn1 = a.fn
    fn1() // this === window

    `this`执行会有不同,主要集中在这几个场景中

    - 作为构造函数执行
    - 作为对象属性执行
    - 作为普通函数执行
    - 用于`call` `apply` `bind`

    //作为构造函数执行,Foo是构造函数
    function Foo (name) {
        this.name = name
        console.log(this.name)
    }
    var fn1 = new Foo("zhangsan")
    fn1();    ----this指向fn1
    
    //作为一个对象属性
    var a = {
        name: 'A',
        fn: function () {
            console.log(this.name)
        }
    }
    a.fn();   ---this指向 a
    
    //作为普通函数执行,函数inner独立调用(不论这个函数在哪调用),this默认指向到window
    function outer(){
         function inner(){
            console.log(this === window);
         }
         inner();
    }
    outer();   ---this指向window
    
    //call、apply、bind调用:三者的第一个参数都是this要指向的对象;
    //bind 只是返回函数,还未调用,所以如果要执行还得在后面加个();call、apply 是立即执行函数;
    func.call(obj,value1,value2);     //call 后面的参数用逗号隔开
    func.apply(obj,[value1,value2]); //apply 后面的参数以数组的形式传入
    func.bind(obj,value1,value2)();  
    func.bind(obj)(value1,value2);  //bind则可以在指定对象的时候传参,和 call 一样,以逗号隔开,也可以在执行的时候传参,写到后面的括号中
    

     3、作用域

      JS 没有块级作用域,只有全局作用域和函数作用域。

       作用域就是一个独立的地盘,让变量不会外泄、暴露出去。下面的`name`就被暴露出去了,因此,JS 没有块级作用域

      if (true) {
        var name = 'zhangsan'
      }
      console.log(name)

      全局作用域就是最外层的作用域,如果我们写了很多行 JS 代码,变量定义都没有用函数包括,那么他们就全部都在全局作用域中。这样的坏处就是很容易装车。

      函数作用域是放在`(function(){....})()`中的所有变量,都不会被外泄和暴露,不会污染到外面,不会对其他的库或者 JS 脚本造成影响。

      作用域链是向父级作用域寻找,一层一层向上寻找,直到找到全局作用域还是没找到当前的自由变量,就宣布放弃的这种一层一层的关系。

    var a = 100
    function F1() {
        var b = 200
        function F2() {
            var c = 300
            console.log(a)
            console.log(b)    //‘a','b'都为【自由变量】
            console.log(c)
        }
        F2()
    }
    F1()
    

      4、闭包

      自由变量将从作用域链中去寻找,但是【依据的是函数定义时的作用域链,而不是函数执行时】,这就是闭包

      闭包主要有两个应用场景:

      - 函数作为返回值
      - 函数作为参数传递

    #函数作为返回值
    function F1() {
        var a = 100
        return function () {
            console.log(a)
        }
    }
    var f1 = F1()
    var a = 200
    f1()
    
    
    #函数作为参数传递
    function F1() {
        var a = 100
        return function () {
            console.log(a)
        }
    }
    function F2(f1) {
        var a = 200
        console.log(f1())
    }
    var f1 = F1()
    F2(f1)

    5、实际开发中闭包的使用场景

      闭包的实际应用,主要是用来封装变量。即把变量隐藏起来,不让外面拿到和修改。

      在 isFirstLoad 函数外面,根本不可能修改掉 _list 的值。

    function isFirstLoad() {
        var _list = []
    
        return function (id) {
            if (_list.indexOf(id) >= 0) {
                return false
            } else {
                _list.push(id)
                return true
            }
        }
    }
    
    // 使用
    var firstLoad = isFirstLoad()
    firstLoad(10) // true
    firstLoad(10) // false
    firstLoad(20) // true

    6、创建 10 个`<a>`标签,点击的时候弹出来对应的序号

    #错误的写法   【监听的i是全局作用域的变量:“自由变量“,因此每次点击都是10】
    var i, a
    for (i = 0; i < 10; i++) {
        a = document.createElement('a')
        a.innerHTML = i + '<br>'
        a.addEventListener('click', function (e) {
            e.preventDefault()
            alert(i)    //自由变量,要去父作用域获取值
        })
        document.body.appendChild(a)
    }
    
    #正确的写法【每个i都生成一个函数var i
    for (i = 0; i < 10; i++) {
        (function (i) {
         //生成了一个函数作用域
    var a = document.createElement('a') a.innerHTML = i + '<br>' a.addEventListener('click', function (e) { e.preventDefault() alert(i) }) document.body.appendChild(a) })(i) }
  • 相关阅读:
    JSON.parse与eval
    加密算法
    asp.net权限管理
    asp.net登录状态验证
    U3D Debug.log的问题
    yield(C# 参考)
    U3D 动态创建Prefab的多个实例
    U3D事件系统总结
    C#事件与接口
    C#泛型委托,匿名方法,匿名类
  • 原文地址:https://www.cnblogs.com/shary-blue/p/14008174.html
Copyright © 2011-2022 走看看