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>
posted @ 2021-11-14 22:19  LuDuo  阅读(489)  评论(0编辑  收藏  举报