Salesforce开发人员的JavaScript技能-对事件和功能采取行动

学习目标

完成本单元后,您将能够:

  • 定义功能
  • 区分函数声明和函数表达式
  • 调用功能
  • 传递和分配功能
  • 描述闪电网络组件中功能和事件的使用

当要使事情发生在JavaScript中时,一切都与事件和函数有关。

还记得我们的运行时模型吗?让我们扩展一下。 

使用浏览器中的JavaScript,事件无处不在。DOM的某些部分发出的事件与该DOM对象的行为相对应。按钮具有单击事件,输入和选择控件具有更改事件,并且可见DOM的几乎每个部分实际上都具有鼠标光标与其交互(例如,经过它)的事件。窗口对象甚至具有用于处理设备事件的事件处理程序(例如检测移动设备的运动)。 

为了使某件事发生在网页中,将函数分配给这些事件作为事件处理程序。 

重申一下,DOM事件和与浏览器环境相关的其他事件实际上并不是核心JavaScript语言的一部分,而是它们是在浏览器中为JavaScript实现的API。 

发出事件时,将在引擎中创建一条消息。这些消息被放置在我们前面讨论的事件队列中。 

队列中的消息和堆栈中的帧。

释放堆栈后,将调用事件处理程序。这将在调用堆栈上创建所谓的框架。每次一个函数调用另一个函数时,都会将一个新框架添加到堆栈中,完成后会从堆栈中弹出,直到最后弹出实际事件处理程序的框架,堆栈为空,然后我们重新开始。 

定义和分配功能

在JavaScript中,函数本质上是特殊的对象。作为对象,它们是JavaScript的一流成员。可以将它们分配为变量的值,将其作为参数传递给其他函数,然后从函数中返回。 

函数生命周期有两个基本阶段:定义和调用。  

声明函数时,其定义将加载到内存中。然后以变量名,参数名或对象属性的形式将指针分配给它。但是,有几种不同的语法可以做到这一点也就不足为奇了。

功能声明

声明是使用function关键字创建函数的语句。实际上,当我们查看对象构造函数时,我们已经看到了它。该构造函数是一个函数。但是构造函数有点特殊,所以让我们退一步,谈论普通的旧函数,看看它们是如何工作的: 

// declare function
function calculateGearRatio(driverGear, drivenGear){
  return (driverGear / drivenGear);
}
// call function
let gearRatio = calculateGearRatio(42, 30);
console.log(gearRatio); // 1.4

在此代码示例中,函数后跟函数名称,并在括号中包含参数。 

这可以正常工作,但是有一些隐式事件正在发生。首先,函数名称成为变量名称。它还将变量隐式分配给封闭的上下文。最后,您可以在声明此函数之前调用它,例如在声明下面calculateGearRatio的那一行调用下面。 

// call function
let gearRatio = calculateGearRatio(42, 30);
// function is declared after the line it is called
// this is allowed in function declaration
function calculateGearRatio(driverGear, drivenGear){
  return (driverGear / drivenGear);
}
console.log(gearRatio); // 1.4

函数表达式

函数表达式更明确地实现了与声明相同的功能。 

const calculateGearRatio = function(driverGear, drivenGear){
  return (driverGear / drivenGear);
}
// the rest works the same
let gearRatio = calculateGearRatio(42, 30);
console.log(gearRatio); // 1.4

在这种情况下,我们有一个明确分配的变量。由于我们已经命名了指针,因此可以将函数名称放在function关键字之后。这里唯一的陷阱是必须在调用函数之前声明该函数。 

值得注意的是,函数表达式还用于将函数分配为对象的成员。回想一下我们将changeGear功能分配给Bike.prototype什么时候?  

Bike.prototype.changeGear = function(direction, changeBy) {
  if (direction === 'up') {
    this.currentGear += changeBy;
  } else {
    this.currentGear -= changeBy;
  }
}

返回函数

由于函数是一类对象,因此声明函数的另一种方法是函数返回另一个函数。这种模式通常称为工厂功能。 

// when invoked, this function will assign a function
function gearFactory(){
  return function(driverGear, drivenGear){
    return (driverGear / drivenGear);
  }
}
// calculateGearRatio can now be invoked as a function
const calculateGearRatio = gearFactory();
// and all the rest

虽然上面的例子很简单,但是它是有效的。工厂函数对于一次性可重用函数很有用,尤其是在闭包中捕获变量引用时。我们将在以后的单元中讨论闭包。 

匿名函数

JavaScript中有许多API,要求您传递一个函数才能使它们起作用。例如,假设您有一个数组,并且想要创建一个从该数组的值派生的新数组。在这种情况下,您可能会使用该Array.map 功能。

let myArray = [1, 5, 11, 17];
let newArray = myArray.map( function(item){ return item / 2 } );
console.log(newArray); // [0.5, 2.5, 5.5, 8.5]

在此代码段中,myArray.map采用一个参数:该函数对中的每个项目执行一次myArray。 

此功能永远不会重用。它被声明为传递给函数的参数(没有名称…因此“匿名”),并在map函数实现的内部执行。匿名函数( 在某些语言中也称为lambda)在JavaScript中很常见。 

功能调用

声明函数后,您可能想遍历调用它。调用函数时,会发生一些事情。 

请记住,第一件事是将新框架推入堆栈。然后在内存中创建一个包含其变量和参数的对象。this然后,将指针与其他一些特殊对象绑定到该对象。然后,将传递给参数的值赋值,最后运行时开始执行函数体内的语句。 

的绑定this有一个重要的例外,我们将在异步JavaScript的单元中重新进行讨论。 

调用与分配

使用函数时,与JavaScript新手混淆的一个潜在原因是您是分配/传递函数还是调用它。这一切都取决于您是否使用()。 

考虑我们的自行车对象的calculateGearRatio功能。 

let bike = {
  ...,
  calculateGearRatio: function() {
    let front = this.transmission.frontGearTeeth[this.frontGearIndex],
    rear = this.transmission.rearGearTeeth[this.rearGearIndex];
    return (front / rear);
  },
  ...
}

现在考虑这两种访问calculateGearRatio函数的不同方式。  

// invoke function and assign value to ratioResult
let ratioResult = bike.calculateGearRatio();
// assign calculateGearRatio function to a new pointer
const ratioFunction = bike.calculateGearRatio;

在第一种情况下,calculateGearRatio将调用,并将从函数返回的结果分配给该ratioResult变量(在这种情况下作为原始值)。另一方面ratioFunction ,仅已分配或指向该calculateGearRatio功能。然后,您可以掉头并以方式调用它ratioFunction。 

有理由将一个函数分配给另一个指针,特别是作为另一个函数的参数,就像该Array.map()函数一样。但是使用this参考的任何功能都有可能会被破坏,因为它this可以在不同的时间指向不同的事物。以后再说。 

充当事件处理程序

如果您希望某个函数作为事件的结果而触发,则需要将其连接到该事件。这样做会使该函数成为事件处理程序。函数定义需要包含一个参数:指向触发它的事件的指针。 

var handleClick = function(event) {
}

每个事件都具有告诉您处理该事件所需了解的属性。例如,click您可以检测到有关点击的数据(事件类型,触发该事件的元素,点击的坐标等)。  

var handleClick = function(event) {
  console.log(event.type);  // click
  console.log(event.currentTarget); // the thing you clicked
  console.log(event.screenX); // screen X coordinate
  console.log(event.screenY); // screen Y coordinate
}

通过DOM API分配事件处理程序

在简单的网页中,您有时可能会在HTML中看到显式分配的事件处理程序。 

<button onclick="handleClick(event)">
  Click to Go
</button>

但是,现代Web应用程序很少在HTML中使用事件绑定。相反,首选DOM API,特别是JavaScript Element.addEventListener()函数。 

首先,您需要对HTML元素的引用。下面,我们在id按钮中添加了一个属性,并删除了onclick属性。 

<button id=”clicker”>

现在我们进入DOM,获取对按钮的引用,并通过将事件侦听器handleClick作为值(注意,不带括号)传递给事件侦听器。 

let button = document.getElementById("clicker");
button.addEventListener("click", handleClick);

使用DOM API可使开发人员灵活地使UI高度交互并响应用户的操作。如果需要关闭功能,开发人员还可以删除事件侦听器。 

button.removeEventListener("click", handleClick);

您还将看到匿名函数添加为事件侦听器。 

button.addEventListener("click", function(event){
  //...anonymous function body...
});

请记住,不能使用来删除匿名函数removeEventListener,因为没有指针可以传入以识别该函数。 

Lightning Web Components中的事件和功能

Lightning Web组件的关键代码工件是JavaScript模块,HTML模板和CSS文件。其中唯一需要的是JavaScript模块(注意,.xml还需要的文件不是代码,而是有关组件的元数据)。 

Lightning Web组件中的功能通常以方法的形式出现,这些方法是该组件的JavaScript模块导出的类的成员。这些函数可以是事件处理程序,也可以是从下游向下游调用的其他函数。 

HTML模板以类似于静态HTML绑定的方式引用处理程序函数,但实际上是不同的。由于模板已编译为JavaScript工件,因此看起来静态的绑定实际上只是框架addEventListener在组件生命周期中某个时间调用的语法约定。 

模板中的此标记显示事件处理程序绑定。

<lightning-input onchange={handleChange} label="Input Text" value={text}>
</lightning-input>

这是事件处理程序。 

    handleChange(event){
        this.text = event.target.value;
    }
注意

注意

Lightning Web Components中有许多事件的更高级的功能。要探索这些功能,请完成Lightning Web Components基础知识模块,或深入研究其中一个示例 应用程序。