zoukankan      html  css  js  c++  java
  • ES6箭头函数的this彻底理解

    shell ES6标准新增了一种新的函数:Arrow Function(箭头函数)。

    基础语法

    通常函数的定义方法

    var fn1 = function(a,b) {
    	return a+ b
    }
    function fn2 (a,b) {
    	return a+ b
    }
    

    ES6箭头函数语法定义函数,将原函数的“function”关键字和函数名都删掉,并使用“=>”连接参数列表和函数体。

    // 把function省掉 用=>连接(参数列表)=>{函数体}
    var fn1 = (a,b) => {
    	return a + b
    }
    // function省掉 函数名 fn2省掉
    (a, b) => {
    	return a+b 
    }
    

    1.当函数参数只有一个,括号可以省略;

    2.无参数时括号不能省略。

    3.多个参数括号不能省略

    4.可变参数

    // 无参-括号不可以省略
    var fn1 = function() {}
    var fn1 = () => {}
    
    // 单个参数-括号可以省略
    var fn2 = function (a) {}
    var fn2 = a => {}
    
    //多个参数
    var fn3 = function (a,b) {}
    var fn3 = (a,b) => {}
    
    // 可变参数
    var fn4 = function (a,b,...args) {}
    var fn4 = (a,b,...args) => {}
    

    箭头函数有两种格式。

    • 1.一种只包含一个表达式- 省掉了{...}和return。
    • 2.还有一种可以包含多条语句-不能省掉{...}和return。没有return返回值就是undefined。
    //1.一种只包含一个表达式- 省掉了{...}和return。
    (a,b) => a + b
    () => return 'hello'//错误,理由如下
    /*
    return 得加大括号
    let a = () => {
    return 'hello world'
    }
    var fn2 = () =>'hello';
    这么写的时候 省略花括号的时候
    是默认自带了一个return的
    省略了花括号的时候 编译器是会给隐含加个return的,
    这个时候你再加个return 
    实际上就相当于有两个return了 就错误了
    
    */
    

    image
    还可以这样用
    image

    箭头函数 可以改变this指针的作用域

    • 计算机的所有数据,都是存放在内存的的

    • 但是内存不是公共的

    • 内存会划分为一个一个的块
      image

    • 然后一个进程用一个块

    • 同个块里面的 是一个作用域

    • 不同块之间是不能访问的

    • js 是单线程的

    • 也就是一整个js 是占用一个内存块的

    • 但是 一整个内存块 js 拿到之后,并不是全部都通用的

    • js 的进程,现在主流的游览器是谷歌的v8引擎

    • js 是运行在v8上的 这个v8 会将这个内存块,按照js的代码再进行分块

    • 一个块内一个作用域

    • 所有情况下 js 一个文件先是一个大的作用域

    • 我们在dhtml 里面插入 一个 的时候 不同标签之间可以分开,但是都是在同个html里面

    • 也就是公用一个大的作用域(但是 对应的node 里面,一个node.js文件是一个作用域)

    • 作用域的作用,是用来指针寻址用的,以及用let 定义的

    • 作用域、指针、内存的地址的、这些都是逻辑概念。

    • 比如我们说的 在内存上是个块,物理上并不是一个块,物理上计算机有一个独特的内存处理算法 是可能分散的

    • 但是对于面对我们程序员的时候,计算机会将其封装好,让程序员感觉像是个块

    • 这些个内存物理的处理算法,可以见 计算机组成原理 操作系统原理等,但是这里我们先简化理解
      image

    • 逻辑上 我们在最外面的时候 这个this 指代的是放置这个script的区块

    • 也就是 this并不是指这个灰色的作用域(而是外面的那一层白色的,最底层-自上的一层)

    • 而是外面的那一层白色的,最底层

    • 所以我们在最外面用this的时候 会指向 window 这个全局

    • 因为 window是最底层的
      image

    • 当我们在定义一个obj对象的时候,这时 花括号里面的this 还是指代外面的 还没有改变

    • 为什么往下看

    • image

    • 我们在这个obj里面 再嵌套定义一个this 根据指到上面原则

    • 第一个不在嵌套的this 会指向灰色的作用域,但是灰色作用域本身不是对象,就会往上指到白色-也就是window

    • 第二个,往上指就指到了橙色

    • 下再放个function 会再次改变
      image

    • 给这个function里面的this标明黑色

    • 是因为 function里面的this 现在是没有指代物的 只是放着。

    • 为什么function的this是没有指代的呢 (这是因为内存设计,a:{} 是个对象实体,是直接在obj这个块内的内存占用的)
      image

    • 但是 function不是,function只是个指针

    • 因为 function的代码一般都很大,占用的内存块很大

    • 所以 内存划分上 会将function跟obj分开放

    • 这样子可以节省obj的内存

    • 因为function是一整个连续的大块

    • 所以我们将function放出去会性能会更好点

    • 然后用个指针b指向这个方向

    • 这个指针b 就是我们定义的时候 b:function(){}

    • 或者var b = function(){}这个时候b都是个指针

    • (js虽然表面上没有变量类型,但是 实际上还是有的,只是模糊掉了)

    • 下面我们将this也写进来
      image

    • 聪明的你会发现

    • function的this 不是可以直接指向obj吗?

    • 这样子不就解决 这一些作用域问题了?(this是个指针)

    • 实际上是 这个this是可以指向obj的,但是js没有这样子设计

    • 在别的一些编程语言里面就有这样子的设计,比如C++ 什么的,但是 js没有
      (js最早呢sun公司里面,搞java的几个程序员设计的,他们最早的设计可能是为了简化java,在java里面就应该有这样子的设计,那样子的话 如果将this指向obj,
      那我如果这个function 不是写在obj里面的
      是直接定义的function呢,那样子的话this又该默认指向哪里?
      所以 设计人员可能是出于简化也有可能是出于别的目的,这个我也不知道当时他们的想法
      总之,他们将js的function里面的this 设计成了指向上下文
      也就是内存堆栈里面的上一个作用域
      这个就是为什么我们开头要说内存,内存的底层 实际上并不是像数组那样子直接操作的
      大多数时候 进程是放到内存的一个堆栈进行操作的
      堆栈是个数据结构的概念 我简单介绍一下就是这样子
      image

    • 像往一个篮子放书一样

    • 每次只能放在最上面,然后再取最上面的,我虽然没有读过v8的源码,但是我猜测,js的代码访问是这样的,

    • 我们obj.b的时候,先入obj 然后再入b,然后发现后面没有东西了,就再弹出b
      image

    • 先进先出

    • 然后b是个函数 下面就执行b的代码

    • 执行的时候 遇到了this,就会在内存上去查上一个,结果是obj

    • 所以 执行b的时候,this指向了obj

    • 因为我们是 obj.b 这个时候 function里面的this就会指向obj。

    现在来看

    image
    image

    • 所以 为什么我们定义一个function
    • school.a = function
    • obj.b = function
     <script>
            const Maid = {
                name: "陆",
                whereAreYou: () => {
                    console.log("我在${this.name}")
                },
                whatIsYourName: function() {
                    console.log('我是${this.name}');
                }
            }
    
    
            function School() {
                let School = {
                    name: "学校"
                };
                School.a = Maid.whatIsYourName;
                /*
               相当于School = (
            let School = {
                    name: "学校"
                };
               a: function() {
                    console.log('我是${this.name}');
                }
               ) 
                */
    
                School.a()
                    //学校,因为虽然赋值了Maid.whatIsYourName,给他这个function而已,
                    //当他自己找this.name的时候当然找所在的地方的上一级
                    //,当他运行时还是会找到自己上面的this
                Maid.whatIsYourName() //陆因为调用他自己的,他属于是Maid的所以他上一级就是this.name就为陆
    
    
            }
            School();
        </script>
    
    • 两个指向的时候 一个指向school 一个指向Maid
    • 因为function的this 会指向上一个堆栈里的对象
    • 同样的 如果你是在嵌套function的话,
    • function里面又有function,那么就得看你代码里面 是怎么样子的一个进堆栈的方式
    • 然后this 向上查找
    • 所以 this并不总是指全局,指是指向function里面的上一个对象

    然后 呢 知道了这个再来看 箭头函数就简单了

    • 箭头函数是es6提出的,也就是2015年提出的

    • 也就是 2015年的时候 设计人员觉得早期的function设计有点功能不够,再加上来的

    • 所以 实际上箭头函数只是个语法糖

    • 有很多工具可以将es6转成es5,也就是babel等这些工具

    • 就是将箭头函数 await promise这些转成老版本的表达

    • 箭头函数实际上的本质 只不过是
      image

    • 在定义箭头函数的时候,先存一下最外面的this

    • 然后将箭头函数里面的this 替换成外面的这个p

    • 因为箭头函数是新增的补丁,所以不大可能改进 function的内核,只是加个语法替换的补丁而已

    • 这就是个历史原因了,function的内核设定已经是this指向上一级了,要改起来就很头疼了

    • 加个文本替换,就出现了箭头函数

    • 而且 多出了一个p, p不是个放在function的指针

    • p是在定义的时候就写死的

    • 这个时候箭头函数里面的this 在运行的时候会替换成p
      image

    • 箭头函数的代码本体也是不跟obj放一起的

    • 也是外放的,但是 它的所有this都会重定向到p

    • 然后p再指向定义的时候的this

    • 这个p不一定指向obj 也可以指向别的,因为我们上面的示例是定义在obj的,所以指向obj
      image

    • (你看)

     const Maid = {
                name: "陆",
    	 /*相当于
    	 var p = this 这个this指出去,那就是全局啦
    	 */
                whereAreYou: () => {
    				// this
                    console.log("我在${this.name}")
                },
                whatIsYourName: function() {
    				//这this 指的是上一级对象
                    console.log('我是${this.name}');
                }
            }
    

    ** 以上整理来自我与师傅的聊天对话**

    总结: 普通函数function的this跟谁调用它有关,比如是js调用呀,那它的指向就是js规定的windows,(1.那是不是所有的普通函数都是js调用的,那永远指的都是windows?)-之后探讨。而箭头函数的this指的是上一级的this,假如是上级this指的是vue,那它的this就是vue.

    • 探讨结果出来了

    • 1.凡是在函数内部调用的函数 this 都指向window

    • 2、在事件中一般情况下 this 的指向都指向当前对象

    • 3、在对象的函 数中一般情况 this 的指向都指向当前对象

    • 4、计时器 this 的指向都指向window
      下面这篇文章讲的很彻底普通函数的指向问题,忘记的时候就点击查看。
      参考文章:知乎

    彻底总结:箭头函数的this指向,指的永远是上一级的this,上一级指的是哪,它就是哪,普通函数的this指向总结:
    1.凡是在函数内部调用的函数 this 都指向window
    2、在事件中一般情况下 this 的指向都指向当前对象
    3、在对象的函 数中一般情况 this 的指向都指向当前对象
    4、计时器 this 的指向都指向window
    

    参考文章:点击跳转

    ***总结:

    • 构造函数和普通函数的指向问题。看下面代码
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>普通函数和构造函数this指向问题</title>
    </head>
    
    <body>
        <button>点击</button>
        <script type='text/javascript'>
            // this 指向问题 一般情况下this的最终指向是那个调用它的对象
    
            // 1.全局作用域或者普通函数中this指向全局对象 window
            //(注意定时器里面的this指向windows
            //因为它其实是属于windows下面的函数或者是方法,是window在调用它)
            console.log(this); // 全局windows
            function fn() {
                console.log(this); //window
            }
            //windows调用
            window.fn();
            window.setTimeout(function() {
    
                console.log(this);
                //window
            }, 1000)
    
            //2.方法调用中谁调用就指向谁
            var o = {
                sayHiy: function() {
                    console.log(this); //只向 o对象
                }
            }
    
            o.sayHiy();
            var btn = document.querySelector('button')
            btn.onclick = function() {
                console.log(this); //this指向的它的调用者也就是 btn
                //this 指向的是btn这个按钮对象
            }
    
            btn.addEventListener('click', function() {
                console.log(this); //this 指向的是btn 这个按钮对象 因为是btn这个按钮对象调用了它
            })
    
            //3.构造函数中this指向构造函数实例
            function Fun() {
                console.log(this); //this 指向的是 fun 实例对象
            }
    
            var fun = new Fun()
        </script>
    </body>
    
    </html>
    
  • 相关阅读:
    归并排序(非递归)
    centos7.2 安装jenkins2.274
    归并排序
    Jmeter5.4支持TPS测试
    centos下安装rocketmq4.6.1
    Java 8新特性:lambda表达式
    tomcat putty启动
    Linux启动tomcat带控制台
    每个Java开发者都应该知道的5个JDK工具
    强大易用的日期和时间库 线程安全 Joda Time
  • 原文地址:https://www.cnblogs.com/LuDuo/p/15546686.html
Copyright © 2011-2022 走看看