首页 > 编程语言 >《Javascript中关于this作用域的箭头函数,普通函数以及回调函数中的特殊作用》

《Javascript中关于this作用域的箭头函数,普通函数以及回调函数中的特殊作用》

时间:2023-08-18 17:25:15浏览次数:45  
标签:function 函数 指向 作用域 Javascript 箭头 print 指针

开言

这篇文章的内容很简单,一句话就是“涉及this指向谁”!!!

但是涉及JavaScript的普通函数,箭头函数,WIndow,回调,Object等各种知识点

其糅杂在一起,其知识点会很混乱,我们要理性头绪!!

简单来说,普通函数可以看做管理严格的孩子,箭头函数是一个自由自在的孩子

因此,在object中,使用箭头函数很危险,其this指向很随性!!

但在回调函数中,箭头函数可以帮助我们传入当前object的this值!!

而function同时使用bind,也可以达到管理内部的this指针效果!!

这并不是语法缺陷,其可以看到代码编写中有很强大的灵活性!!!

这并不是语法缺陷,其可以看到代码编写中有很强大的灵活性!!!

这并不是语法缺陷,其可以看到代码编写中有很强大的灵活性!!!

函数式编程中,箭头函数非常重要,甚至出现高阶函数,返回一个构建的函数

在上述这些过程中,如果你单纯想用function来实现,你this绑定会非常麻烦!!

 

目录

一、This指针的错误理解

二、JavaScript脚本语言(动态运行)中This指针的灵活性

三、嵌套对象中this指针

四、在对象定义中警惕在箭头函数中使用this指针,不会链式传导

五、function函数在回调函数中的局限性

六、箭头函数解决参数传递问题

七、常规函数中使用bind来绑定this指针,解决回调函数中的问题

 

-----

 

一、This指针的错误理解

我们之前一直不理解this作用域,其核心是一些概念模糊,this关键是看调用方,而不是声明方。

当你看一个存在函数的声明,并不能推断出this的指向,因为JavaScript中存在函数绑定,其可能在不同的对象上调用,导致this的指向不同!!!

这么说,看this的指向,之前我们一直采用静态的眼光看,而我们现在要采用动态的眼光看!!!!

 

采用 动态的眼光,看调用方!!

采用 动态的眼光,看调用方!!

采用 动态的眼光,看调用方!!

 

二、JavaScript脚本语言(动态运行)中This指针的灵活性

记住,JavaScript是脚本语言,脚本语言的意义在于“动态运行”,像python一样

而JavaScript的语法比python复杂,同时又兼有c++这种this功能

在c++这种静态语言中,像直接定义一个 this.Attribute,是无法接受的,如下

    printFunc = function() {
        console.log(this.FirstName)
    }

因为C++其要“先编译,后运行”,在编译中直接报错!!!

而JavaScript是动态语言,其属于“函数声明”,无所谓,先存着;

当在函数调用时,直接动态搜索当前调用方的this对象;

如果没有找到FirstName这个属性,其直接输出undefined

看下面这个例子,最后Lucy原本的FirstName属性被改为_FirstName,输出Undefined值,并没有报错!!!

  let Tom = {
        FirstName : "Tom",
    }

    let Jack = {
        FirstName: "Jack",
    }

    let Lucy = {
        _FirstName: "Lucy",
    }

    printFunc = function() {
        console.log(this.FirstName) // 动态绑定 this.FirstName
    }

    Tom.print = printFunc
    Tom.print() // Tom


    Jack.print = printFunc
    Jack.print() // Jack

    Lucy.print = printFunc
    Lucy.print() // undefined

 

三、嵌套对象中this指针

这里牵扯链式指向,代码如下,可以看到嵌套中输出环境是不同的

    function print(){
        console.log(this)
    }
    Tier = {
        name: "Tier",
        Tier1:{
            name: "Tier1",
            print(){
                console.log(this.name)
            }
        },
        print(){
            console.log(this.name)
        }
    }

    print() // Window
    Tier.print()    // Tier
    Tier.Tier1.print() // Tier1z

直接构建出其链式结构,其链式结构如下,其结构和输出很明确

 

四、在对象定义中警惕在箭头函数中使用this指针,不会链式传导

在对象中使用箭头函数,其this指针,并不会如预期那样,指向调用对象。

在箭头函数中,并不存在“指向外侧”,而是直接指向指向Windows

    const myObject = {
        property1: 'Hello',
        property2: 'World',

        regularFunction: function() {
            console.log(this.property1 + ' ' + this.property2);
        },

        // Object中尽量不要使用箭头函数
        arrowFunction: ()=> {
            console.log(this.property1 + ' ' + this.property2);
        }
    };

    myObject.regularFunction(); // 输出:Hello World
    myObject.arrowFunction(); // 输出:undefined undefined

这个要警惕,其箭头函数定义的直接指向全局对象window。

同时,这里不会链式传导,不会链式传导,不会链式传导!!

哪怕内层的Object,当使用箭头函数定义时,其也会指向全局对象

 下面是代码结构,当你理解这个,你会真正理解其所处的函数

    const globalObject =
    {
         myObject : {
            property1: 'Hello',
            property2: 'World',

            regularFunction: function () {
                console.log(this.property1 + ' ' + this.property2);
            },

            // 箭头函数在内侧定义 Window --> globalObject --> myObject 
            arrowFunction: () => {
                console.log(this + ' ' + this);
            }
        }
    }

    globalObject.myObject.regularFunction(); // 输出:Hello World
    globalObject.myObject.arrowFunction(); // 输出:[Object Window] [Object Window]

  

五、function函数在回调函数中的局限性

我们之前提到过,在object中警惕使用箭头函数,但是真的一事无成吗箭头函数?

当然不是,在回调函数中,我们可以看看箭头函数的“威力”

现在来,什么是回调函数?相当于我们定义一个函数,交给回调器,其定义方不在我们

这里,存在一个问题,其回调函数原型中,参数已经被定义好了,这是非常关键的

我们如何传递自己的值到回调函数中?

我们如何传递自己的值到回调函数中?

我们如何传递自己的值到回调函数中?

此时就要借助 箭头函数 了!!!!

 

看下面这个代码,什么问题呢?

    var tahoe = {
        resorts: ["Kirkwood","Squaw","Alpine","Heavenly","Northstar"],
        print: function(delay=1000) {

            setTimeout(function() {
                console.log(this.resorts.join(","))
            }, delay)

        }
    }

    tahoe.print() // Uncaught TypeError: Cannot read properties of undefined (reading 'join')

其function中的this,其尝试获取 tahoe对象中的  ["Kirkwood","Squaw","Alpine","Heavenly","Northstar"] 这组数据

这样写很天真,因为JavaScript是动态语言,其回调函数调用这个函数,this指针被setTImeout调用时,this指向Window

自然获取不到这个数据(this并不指向tahoe),其是一个undefined,然后找不到join方法

最后自然抛出一个异常 Uncaught TypeError !!!

---

当然,这里可以使用bind解决,bind返回一个新函数,负责管理函数内部this指针的指向问题,在(七)中会提到的。

 

六、箭头函数解决参数传递问题

函数如下,内侧函数改用箭头函数,这里注意一下重点。

其箭头函数的作用域不限定,如下函数,其this指针直接是函数定义时的this指针 object tahoe

其箭头函数的作用域不限定,如下函数,其this指针直接是函数定义时的this指针 object tahoe

其箭头函数的作用域不限定,如下函数,其this指针直接是函数定义时的this指针 object tahoe

    var tahoe = {
        resorts: ["Kirkwood","Squaw","Alpine","Heavenly","Northstar"],
        print: function(delay=1000) {

            setTimeout( ()=> {
                console.log(this.resorts.join(","))
            }, delay)

        }
    }

    tahoe.print() // Kirkwood,Squaw,Alpine,Heavenly,Northstar

其链子如下,其传入回调函数之后,this值已经确定,不会改变!

其链子如下,其传入回调函数之后,this值已经确定,不会改变!

 其链子如下,其传入回调函数之后,this值已经确定,不会改变!

 

 

但是下列代码,当使用两个箭头函数,其this指针就无法无天了,声明时就指向Window对象!!

这个前面讲过,这个一点也不难,很好理解。

   var tahoe = {
        resorts: ["Kirkwood","Squaw","Alpine","Heavenly","Northstar"],
        print: (delay=1000) => {

            setTimeout( ()=> {
                console.log(this.resorts.join(","))
            }, delay)

        }
    }

    tahoe.print() // Uncaught TypeError: Cannot read properties of undefined (reading 'join')

关于Object的回调函数,记住下面三种情况:

① ()=>{} + ()=>{this}  // 回调函数传入时this指向Window

② function(){} + ()=>{this}  // 回调函数传入时this指向Window

③ function(){} + function(){this} // this直接指向调用方

 

七、常规函数中使用bind来绑定this指针,解决回调函数中的问题

function(){ xx }.bind(),其返回一个新函数,这个其实非常关键

我们使用function.bind(this)可以解决回调函数问题

bind()的核心是帮助管理函数内部的this指针!!

bind()的核心是帮助管理函数内部的this指针!!

bind()的核心是帮助管理函数内部的this指针!!

---

如下代码,其对象中的函数可以单独拆分出来,但是此时其this指针会丢失而指向Window

此时,函数最好的方法是调用bind(),把对象重新绑定函数上,此时this就会重新指向person

const person = {
  name: 'John',
  greet: function() {
    console.log(`Hello, my name is ${this.name}`);
  }
};

const greetFunction = person.greet;
greetFunction(); // 输出:Hello, my name is undefined

const boundGreetFunction = person.greet.bind(person);
boundGreetFunction(); // 输出:Hello, my name is Johne

而下面解决我们提到的setTimeout回调函数的困惑,直接function(){xxx}.bind(this),这个this指针,很明显指向Object

此时该函数挂载到回调函数时,不会再改变了,this就指向这个Object

但是如果使用原来的Function,其传入时就是被视作一个单纯的普通函数,因为其是单独定义的!

但是如果使用原来的Function,其传入时就是被视作一个单纯的普通函数,因为其是单独定义的!

但是如果使用原来的Function,其传入时就是被视作一个单纯的普通函数,因为其是单独定义的!

    var tahoe = {
        resorts: ["Kirkwood","Squaw","Alpine","Heavenly","Northstar"],
        print: function (delay=1000) {

            setTimeout( function () {
                console.log(this.resorts.join(","))
            }.bind(this), delay)

        }
    }

    tahoe.print() // Kirkwood,Squaw,Alpine,Heavenly,Northstar

  

标签:function,函数,指向,作用域,Javascript,箭头,print,指针
From: https://www.cnblogs.com/qwe-asd-zxc-nm/p/17640221.html

相关文章

  • 7 JavaScript循环语句
    7循环语句在js中有三种循环语句.首先是while循环.它的逻辑和咱们python中的while几乎一模一样,就是符号上有些许的区别.//语法while(条件){循环体->里面可以有break和continue等关键字}/*判断`条件`是否为真,如果`真`,则执行`循环体`.执行完`循环体`,会再次......
  • 6 JavaScript条件判断
    6条件判断除了HTML以外.几乎所有的编程语言都有条件判断的功能.比如,python,我们用if语句来做条件判断.到了javascript中也是一样的,也使用javascript来做条件上的判断./*语法1*/if(条件1){代码块1}if(条件)a,b,c,d;该语法表示当条件为真.运......
  • C++快速入门 第四十二讲:链接和作用域
    与作用域有关的另一个概念是链接,当同时编译多个文件时,每个源文件被称为一个翻译单元,在某一个翻译单元里定义的东西在另一个翻译单元里使用正是链接发挥作用的地方。存储类(storageclass):每个变量都有一个存储类,它决定着程序将把变量的值储存在计算机的什么地方、如何存储、以及变......
  • C++快速入门 第四十三讲:链接和作用域2
    1header.h文件23#ifndefHEADER_H4#defineHEADER_H56unsignedlongreturnFactorial(unsignedshortnum);7staticconstunsignedshortheaderNum=5;//定义静态恒定值的全局变量89#endif1011that.cpp文件:1213#include"header.h"14uns......
  • C++快速入门 第四十四讲:函数模板swap使用
    泛型编程技术支持程序员创建函数和类的蓝图(即模板,template),而不是具体的函数和类。标准模板库STL(StandardTemplateLibrary),STL库是泛型编程技术的经典之作,它包含了许多非常有用的数据类型和算法。当拥有一个模板时,编译器将根据模板自动创建一个函数,该函数会使用正确的数据类型......
  • 无涯教程-Perl - unshift函数
    描述此函数按顺序将LIST中的元素放在ARRAY的开头。这与shift()相反。语法以下是此函数的简单语法-unshiftARRAY,LIST返回值此函数返回ARRAY中新元素的数量。例以下是显示其基本用法的示例代码-#!/usr/bin/perl-w@array=(1,2,3,4);print"Valueofarray......
  • [转]如何在 JavaScript 中遍历对象
    原文地址:如何在JavaScript中遍历对象在JavaScript中,当你听到“循环”一词时,你可能会想到使用各种循环方法,例如 for 循环、forEach() 方法、map() 方法等等。但不幸的是,这些方法对于对象不起作用,因为对象是不可迭代的。这并不意味着我们不能循环遍历一个对象——只是我......
  • C++快速入门 第三十二讲:assert函数和捕获异常
    C语言和C++都有一个专门为调试而准备的工具函数---assert函数。这个函数是在assert.h库文件里定义的。实例1:assert函数应用1#include<cassert>2//assert()函数需要有一个参数,它将测试这个输入参数的真或者假状态3//如果为真Donothing4//如果为假Dosomething......
  • C++无法将类的成员函数赋值给一般的函数指针,解决方案
    遇到需要绑定回调函数的API接口。看了定义的函数指针,是静态的函数指针,将非静态的类成员函数赋值过去的时候,无法编译通过。将成员函数改成静态又得将整个类的内容都改成静态。这种情况下,可以构造匿名函数,引用this,这样可以在类中,将自己的成员函数赋值给需要的函数指针类型上:ser......
  • 无涯教程-Perl - unpack函数
    描述此函数使用TEMPLATE中指定的格式解压缩二进制字符串STRING。基本上颠倒打包的操作,根据提供的格式返回打包值的列表。Youcanalsoprefixanyformatfieldwitha%<number>toindicatethatyouwanta16-bitchecksumofthevalueofSTRING,insteadofthevalue.......