JavaScript中的箭头函数用法指南

2021年3月10日16:22:31 发表评论 781 次浏览

先决条件:这在JavaScript

在这篇文章中, 还有一些与之相关的函数这个在JavaScript中已有讨论。

此和箭头函数:

ES6中引入的Arrow函数提供了使用JavaScript编写函数的简洁方法。

它提供的另一个重要优势是它不绑定自己的事实

这个

。换句话说, 箭头函数内部的上下文是按词汇或静态方式定义的。

那是什么意思?

与其他函数不同,

这个

内部箭头函数不取决于如何调用它们或如何定义它们, 而仅取决于其封闭上下文。

让我们尝试通过一个例子来理解:

<!DOCTYPE html>
<html>
<body>
<script>
     let People = function (person, age) {
         this .person = person;
         this .age = age;
         this .info = function () {
  
          // logs People
          document.write( this );
  
          setTimeout( function () {
             // here this!=People
            document.write( this .person + " is " + this .age + 
                                               " years old" );
           }, 3000);
         }
     } 
    let person1 = new People( 'John' , 21);
  
// logs : undefined is undefined years old after 3 seconds
person1.info();
</script>      
</body>
</html>

输出如下:

[object Object] 
undefined is undefined years old

之所以我们得到未定义的输出而不是正确的信息作为输出, 是因为函数()定义为setTimeout的回调具有正常的函数调用, 并且我们知道, 这意味着其上下文设置为全局上下文, 或者换句话说, 这个设置为窗口对象。

发生这种情况是因为每个常规非箭头函数都定义了自己的函数这个或上下文取决于它们的调用。封闭对象/函数的上下文不影响这种自动定义其自身上下文的趋势。

我们该如何解决呢?

我想到的一个显而易见的解决方案是, 如果函数没有定义自己的上下文呢?信息(), 因为这将意味着此function()获得这个如定义信息()

好吧, 这正是箭头函数的作用, 它们保留了

这个

从他们的封闭

上下文。

也就是说, 在上面的示例中, 如果将函数定义为

setTimeout()

是一个箭头函数, 它将继承值

这个

从它的封闭上下文中

信息()

<!DOCTYPE html>
<html>
<body>
<script>
     let People = function (person, age) {
         this .person = person;
         this .age = age;
         this .info = function () {
  
             // logs People
             document.write( this ); 
  
            setTimeout(() => { 
             // arrow function to make lexical "this" binding
             // here this=People."this" has been inherited
             document.write( this .person + " is " + this .age 
                                            + " years old" );
            }, 3000);
         }
     } 
let person1 = new People( 'John' , 21);
  
// logs : John is 21 years old after 3 seconds
person1.info(); 
</script>                    
</body>
</html>

输出如下:

[object Object] 
John is 21 years old

因此, 无论使用函数调用还是方法调用调用了箭头函数, 它都保留了这个从其周围的上下文。换句话说, 箭头函数的这个值与其立即在其外部相同。

如果在任何封闭函数之外使用, 则箭头函数会继承全局上下文, 从而设置这个到全局对象。

在单独的方法中:

当任何对象的方法与之分离或存储在变量中时, 例如:让分开= People.info, 它将丢失对其调用对象的引用。

请注意, 之后缺少左括号和右括号信息。这表明我们不会立即调用该方法。

例如 :

<!DOCTYPE html>
<html>
<body>
<script>
     let People = function (person, age) {
         this .person = person;
         this .age = age;
         this .info = function () {
  
             // logs People
             document.write( this + ' ' );
  
             // here this=People
             document.write( this .person + " is " + this .age + 
                                       " years old" + '<br>' );
         }
     }
  
let person1
     = new People( 'John' , 21);
  
// logs : John is 21 years old
person1.info();
  
// separating the method info() from its
// object by storing it in a variable
let separated = person1.info;
  
// logs : undefined is undefined years old
separated();
</script>                    
</body>
</html>

输出如下:

[object Object] John is 21 years old
[object Window] undefined is undefined years old

一旦我们分离了

信息()

来自

人1

通过将其存储在对象中

分开的

, 我们将丢失所有对

人1

目的。

我们无法再使用来访问父对象

这个

在分离方法内部, 因为分离方法的上下文被重置为全局上下文。

因此, 当我们打电话split()我们看到了这个现在设置为全局窗口对象。

我们该如何解决呢?

一种方法是

捆绑

在将方法存储在其中时带有方法的对象的值

分开的

。这样可以确保所有对

这个

即使在分离方法中也引用此绑定对象。

这可以使用

bind()

像这样:

<!DOCTYPE html>
<html>
<body>
<script>
     let People = function (person, age) {
         this .person = person;
         this .age = age;
         this .info = function () {
  
             // logs People
             document.write( this + ' ' );
  
             // here this=People
             document.write( this .person + " is " + this .age + 
                                     " years old" + '<br>' );
  
         }
     }
  
let person1
     = new People( 'John' , 21);
  
// logs : John is 21 years old
person1.info();
  
let separated = person1.info.bind(person1);
  
/*
the bind(person1) statement ensures that "this" always
refers to person1 inside the bound method- info()
*/
  
// logs : undefined is undefined years old
separated();
</script>                    
</body>
</html>

输出如下:

[object Object] John is 21 years old
[object Object] John is 21 years old

注意:我们可以使用任何对象来bind()方法信息(), 而不是code.person1, 并且输出将进行相应的更改。将方法绑定到其父对象不是强制性的。

如果你走了这么远, 你会注意到我们提到了

bind()

一些时间。

现在让我们研究一下

bind(), call()和apply()

确实是。

绑定, 致电和申请

bind(), call()和apply()均用于修改函数的上下文。所有这三个帮助都明确指定了

这个

应该在函数内部。

但是, 它们各自的工作方式存在一定差异。让我们研究这些差异。

bind()

bind()允许我们显式定义什么值

这个

通过将对象绑定到该函数将在函数内部。

绑定的对象用作上下文(

这个

值)。

要使用它, 我们首先需要做两件事–一个对象绑定到一个函数以及该对象要绑定到的函数。

绑定的一般语法为:

boundfunction = someFunction.bind(someObject, additionalParams);

在bind()指令中使用的第一个参数用作这个值和其后的参数是可选的, 并且用作绑定函数的参数。

<!DOCTYPE html>
<html>
<body>
<script>
     let fruit = function (person, color) {
         this .person = person;
         this .color = color;
         this .displayInfo = function () {
             document.write( this .person + " is " + this .color + '<br>' );
         }
     }
  
let bindingObj
     = {
         // creating an object using object literal syntax
         person : "Banana" , color : "Yellow" , }
  
// Constructor invocation to create an object fruit1
let fruit1
     = new fruit( "Orange" , "orange" );
  
// logs :Orange is orange
// Method invocation of displayInfo()
fruit1.displayInfo();
  
// binding the separated method to a new object
let newBound = fruit1.displayInfo.bind(bindingObj);
  
// logs : Banana is Yellow
newBound();
</script>                    
</body>
</html>

输出如下:

Orange is orange
Banana is Yellow

注意, 我们使用方法调用在fruit1上调用displayInfo():fruit1.displayInfo因此可能期望它具有以下内容水果1。但是, 似乎并非如此, 因为记录了"香蕉是黄色"而不是"橙色是橙色"。

发生这种情况是因为我们明确绑定了

这个

在displayInfo()内部使用

bind()

命令

结合物

.

如我们所见, 将对象显式绑定到函数会覆盖其常规上下文规则并强制设置所有

这个

值到其中的绑定对象。

通常, 传递函数时会丢失上下文, 例如:

<!DOCTYPE html>
<html>
<body>
<script>
     let noBinding = {
         persons : "John" , passAround() {
  
         // displayInfo function is passed
         // as a callback to setTimeout
         setTimeout( this .displayInfo, 3000);
         }, displayInfo() {
  
         // logs the Window object
         document.write( this + ' ' );
  
         // logs undefined
         document.write( this .persons);
  
         }
     }
  
     noBinding.passAround();
</script>                 
</body>
</html>

输出如下:

[object Window] undefined

为了防止在作为回调传递的函数中重置上下文, 我们显式绑定了这个与里面一样绕过(), 即:捆绑对象:

<!DOCTYPE html>
<html>
<body>
<script>
     let binding = {
         persons : "John" , passAround() {
  
             // displayInfo function is passed as a callback 
             // to setTimeout binding the context of displayInfo 
             // to "this" = binding in this execution context
             setTimeout( this .displayInfo.bind( this ), 3000);
  
         }, displayInfo() {
             // logs the binding object
             document.write( this + ' ' );
  
             // logs John
             document.write( this .persons);
  
         }
  
     }
  
      binding.passAround();
</script>                    
</body>
</html>

输出如下:

[object Object] John

call()和apply()

call()和apply()

通过显式指定什么值来执行类似于绑定的任务

这个

应该存储在一个函数内。

但是, 它们与

bind()

就是它

call()和apply()

立即调用该函数, 而不是简单地准备带有绑定的函数副本

这个

将来使用的价值。

语法:

呼叫

:

function.call(thisValue, arg1, arg2, ...)

应用:

function.apply(thisValue, [ arg1, arg2, ...])

两种情况下的第一个参数是这个我们希望被调用函数具有。

本质上, 这两种方法之间的唯一区别是应用, 第二个参数是参数的数组对象, 而在呼叫, 所有参数均以逗号分隔的格式发送。

例如:

<!DOCTYPE html>
<html>
<body>
<script>
     // Create two different objects to test call() and apply()
     let banana = { person : 'Banana' };
let orange = { person : 'Orange' };
function fruits(adj)
{
     document.write( this + ' ' );
     return ( this .person + " is " + adj + '<br>' );
}
  
   // call() and apply() will immediately invoke
   // the function they are being called on
  
   // logs : Banana is yummy
   document.write(fruits.call(banana, 'yummy' ));
  
  // logs: Orange is sour
   document.write(fruits.apply(orange, [ 'sour' ]));
</script>                    
</body>
</html>

输出如下:

[object Object] Banana is yummy
[object Object] Orange is sour

注意:两者呼叫()和应用()是默认Function对象的原型上可用的方法。

这与事件监听器

内部函数用作事件监听器的回调, 这个保留触发事件的元素的值。

<!DOCTYPE html>
<html>
<body>
<button>Click me to see console logging < /button>
<script>
     let elem = document.querySelector( 'btn' )
         elem.addEventListener( 'click' , function () {
         // logs: btn 
        document.write( this )
      })
</script>                   
</body>
</html>

如果此处使用的回调函数是箭头函数, 并且我们的事件监听器嵌套在另一个方法中, 这个将引用外部嵌套方法的上下文, 我们将无法再使用来访问添加了事件侦听器的元素这个, 就像我们在前面的代码示例中一样。

幸运的是, 使用currentTarget元素上的方法如下:

<!DOCTYPE html>
<html>
<body>
<button>Click me to see console logging < /button>
<script>
function outerfunc(elem)
{
  
     this .clickHandler = function () {
     elem.addEventListener( 'click' , (e) => {
  
        // logs the<button />element 
       document.write(e.currentTarget);
  
    // this has the value of outerfunc
this .displayInfo();
})
}
;
this .displayInfo = function () {
     document.write( ' Correctly identified!' );
}
}
  
let button = document.body.querySelector( 'button' );
let test = new outerfunc(button);
test.clickHandler()
</script>                    
</body>
</html>

输出如下:

[object HTMLButtonElement] Correctly identified!

如我们所见, 使用currentTarget允许我们访问添加了事件监听器的元素这个允许我们访问封闭函数的上下文-即:这个使我们能够成功调用displayInfo()方法。


木子山

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: