zoukankan      html  css  js  c++  java
  • 函数作用域中的this问题

    首先一起回顾下预解析作用域吧:

      预解析:
        浏览器每读到一个script标签或function,先不执行任何代码,会先把整个代码快速的浏览一遍,然后从中 挑出 var 和 function两个关键字 .
        var: 预解析遇到 var 就把 var 连同它后边的名字一块 提到script(或function) 的最前边,预解析完成之后,在从上向下一行一行执行代码,如果碰到了 = 就赋值;
        function:预解析遇到function,就把整个函数提到提到script(或function) 的最前边(跟在var的后边预解析先解析var 在解析 function)

        全局作用域 --- 在任何地方都能访问
                                 函数外定义的变量拥有全局作用域
                                不使用var定义的变量拥有全局作用域
                                所有window对象上的属性拥有全局作用域
                                没有声明在任何函数内部的函数拥有全局作用域
          局部作用域 --- 只能在函数内部访问
                                使用var在函数内部定义的变量,和使用function在函数内部声明的函数,拥有局部作用域

         [[Scopes]] : 作用域
                                当我们声明一个函数的时候,同时该函数就会创建一个属性这个属性是[[Scopes]](作用域),我们在这个函数中 声明的变量都会被存入这个函数的[[Scopes]]属性中
         变量与函数的查找规则:
                                当我们调用一条数据的时候,js首先会在当前作用域中进行查找,如果找不到,就向上找到父级的作用域,如果在父级的作用域中也找不到,就继续向上查找,直                               到window的作用域。如果在window中也找不到,就报错了

    小例子中有涉及到箭头函数,所以我们先认识一下ES6中的箭头函数吧。

    // 箭头函数
        var fn3 = (a, b) => {
            console.log(a, b);
        }
    
        // 几种有条件的简化写法
        // 当参数只有一个的时候
        var fn4 = a => {
            console.log(a);
        }
        // 当参数有多个的时候
        var fn5 = (a, b) => {
            console.log(a, b);
        }
        // 当没有参数的时候
        var fn6 = () => {
            console.log(1);
        }
        // 有且仅有一个形参的时候,可以省略参数的小括号
    
    
    //    var fn7 = r => {
    //        return r * r * Math.PI;
    //    }
        // 只有一条语句的时候,可以省略{},同时该条语句的结果将作为该函数的返回值
        var fn7 = r => r * r * Math.PI;

    下面是箭头函数的特性,主要是this需要多加注意

        在es6中,提供了一种新的函数格式:箭头函数
        注意:
          1. 箭头函数不能作为构造函数,也就是箭头函数不能使用new运算符
          2. 箭头函数的this永远指向当前申明作用域对象     带大括号的范围
               1. 普通函数this指向取决于调用
               2. 箭头函数this指向取决于申明
          3. 箭头函数没有arguments对象
          也会因为call的改变指向而改变this。即使在构造函数中。所谓的声明时候的this,也可能是被变化掉了。
         
     

    接着,我们一起来看一个小例子吧!

    var name='window';
        var person1 = {
            name:'person1',
            show1: function() {
                console.log(this.name)
            },
            show2: () => console.log(this.name),
            show3: function() {
                return function() {
                    console.log(this.name)//这里的this跟外层函数的this没关系
                }
            },
            show4: function(){
                return () => console.log(this.name)
            }
        };
        //call是更改this指向的。
        
        //对象属性中的函数,写法相当于 person1.show=function(){}
        //函数是全局作用域,挂载到window下,方便理解后面的this指向window
        
        var person2 = {name:'person2'};
        person1.show1()//person1
        //person1是个对象所以这里指向的是对象的this person1
        
        person1.show1.call(person2)//person2
        //函数执行前先改了this指向
        
        person1.show2()//window
        //函数声明是挂载到window下的,箭头函数与声明作用域对象有关
        //即使这里调用该函数执行的是对象,但this还是指的window
        
        person1.show2.call(person2)//window
        //同上,虽然执行函数时更改了this指向,但是这一套对箭头不好使。所以仍然是window
        
        person1.show3()()//window
        //person1.show3()得到的其实是function(){console.log(this.name)}
        //函数执行,this指向的就是window
        
        person1.show3().call(person2)//person2
        //person1.show3()得到的其实是function(){console.log(this.name)}
        //然而接着在执行function前更改了指向
        
        person1.show3.call(person2)()//window
        //person1.show3.call(person2)更改了function() {
        //        return function() {
        //            console.log(this.name)
        //        }
        //    }
        //但再次调用括号执行本质还是跟上面一样,执行的是return出来的内部函数
        //先更改的指向是障眼法而已
        
        person1.show4()()//person1
        //person1.show4()先执行得到的是() => console.log(this.name)
        //再指向该箭头函数,它的声明作用域this指向的是person1
        
        person1.show4().call(person2)//person1
        //箭头函数this在声明时候指向的是person1。
        //person1.show4()先执行得到的是() => console.log(this.name)
        //这里的call是更改箭头函数本身的指向,是改不了的。
        
        person1.show4.call(person2)()//person2
        //person1.show4.call(person2)第一步执行这个的时候就先更改了指向
        //在执行() => console.log(this.name)前先更改了person1的指向

     

    对比: 上面第一个是一个对象而已,所以show2的箭头函数声明时直接指向的window, 下面的是构造函数,能够为s1的箭头函数提供执行环境,所以这里指向的是TImer对象本身。

           以上两种情况是需要注意区别的。

    有错误或者不足之处请大家多多指正,谢谢啦!!!

  • 相关阅读:
    MSSQL系列 (一):数据库的相关操作
    C#数据结构与算法系列(二十三):归并排序算法(MergeSort)
    C#数据结构与算法系列(二十二):快速排序算法(QuickSort)
    Quartz.Net系列(十七):Misfire策略在SimpleScheduler和CronScheduler中的使用
    Quartz.Net系列(十六):通过Plugins模式使用Xml方式配置Job和Trigger和自定义LogPrivider
    Quartz.Net系列(十五):Quartz.Net四种修改配置的方式
    Quartz.Net系列(十四):详解Job中两大特性(DisallowConcurrentExecution、PersistJobDataAfterExecution)
    Quartz.Net系列(十三):DateBuilder中的API详解
    Quartz.Net系列(十二):六大Calendar(Annual、Cron、Daily、Holiday、Monthly、Weekly)
    Quartz.Net系列(十一):System.Timers.Timer+WindowsService实现定时任务
  • 原文地址:https://www.cnblogs.com/hjj2ldq/p/7476516.html
Copyright © 2011-2022 走看看