Salesforce开发人员的JavaScript技能-处理对象,类和原型继承

学习目标

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

  • 使用对象文字符号和构造函数创建对象。
  • 将属性和功能分配给对象。
  • 确定原型在JavaScript对象继承中的作用。
  • 描述JavaScript类语法。
  • 描述闪电网络组件中继承的作用和对象文字表示法。

有很多方法可以将JavaScript描述为一种语言。无论您选择哪种定义,每个人都可以同意对象的JavaScript概念的重要性。您越了解JavaScript对象以及它们如何工作,就越能编写有效的JavaScript。

在开始之前,请先对对象进行一些说明。 

  • 对象没有Apex,Java或C#开发人员可能会想到的类。
  • 每个对象都继承自另一个对象。
  • 对象是可变的。
  • 对象在创建时会获得自己的变量上下文。

创建对象

从语法上讲,有几种方法可以用JavaScript创建对象。但是,无论您如何创建对象,它实际上都是在抽象一个称为的底层API Object.create()。  

在某些情况下,有充分的理由Object.create()直接使用,但我们不在此介绍。相反,让我们看一下创建对象的更常见方法。 

对象文字表示法

第一个对象创建语法称为对象文字表示法。这是一次同时声明和分配对象的简单声明方式。然后立即将对象分配为同一语句的一部分。 

const bike = {
  gears: 10,
  currentGear: 3,
  changeGear: function(direction, changeBy) {
    if (direction === 'up') {
      this.currentGear += changeBy;
    } else {
      this.currentGear -= changeBy;
    }
  }
}
console.log(bike.gears); // 10
console.log(bike.currentGear); //3
bike.changeGear('up', 1);
console.log(bike.currentGear); //4

对象文字符号本质上是声明性的。bike此 示例中的对象具有三个成员:gearscurrentGear属性以及changeGear函数。若要在创建对象后引用这些成员,请使用点表示法。 

注意

您可能会注意到JSON和对象文字表示法彼此相似,但是它们并不相同。JSON是一种数据交换格式。对象文字符号是一种编程语法。但是,JSON规范基于对象文字表示法,因此很容易将两者混为一谈。

文字对象非常适合一次性对象。但是,如果要创建两个或更多个相同类型的对象,则它们是不实际的。为此,您需要可重复的逻辑来创建新对象。 

构造函数的新对象

创建对象的另一种方法是使用构造函数。构造函数是一种函数,其中包含用于在创建和分配对象时建立对象属性的指令。与对象文字相比,这具有优势,因为您可以创建具有相同属性的对象的许多实例。 

function Bike(gears, startGear) {
  this.gears = gears;
  this.currentGear = startGear;
}
Bike.prototype.changeGear = function(direction,changeBy){
  if(direction === 'up') {
    this.currentGear += changeBy;
  } else {
    this.currentGear -= changeBy;
  }
}
const bike = new Bike(10, 3);
console.log(bike.gears); // 10
console.log(bike.currentGear); //3
bike.changeGear('up', 1);
console.log(bike.currentGear); //4

在此 示例中, Bike是定义对象的普通JavaScript函数。我们遵循JavaScript约定并大写第一个单词来表示此函数是构造函数。该new关键字是至关重要的。如果不使用new,则this指针将不会指向您期望的对象,并且会导致意外的行为。this当我们在以后的单元中介绍上下文时,我们会重新进行讨论。 

请注意,该changeGear函数的分配是通过使用来完成的prototype。这样可以确保函数定义一次,并由该构造函数创建的所有实例共享。我们将在本单元后面介绍原型的使用和继承。 

在语法方面,对象文字表示法和构造函数完全不同。但是在每种情况下,您仍然最终会在内存中创建一个新对象,并使用变量bike作为指向该对象的指针。使用构造函数,您可以使许多Bike具有相同属性和功能的对象。

为对象分配属性和功能

如果从bike上面的示例推断出对象中有两种可能的成员类型-属性和函数-您将是正确的。  

属性具有三种基本形状。

  • 原语
  • 对象
  • 数组

有六种基本类型在JavaScript:字符串,数字,布尔nullundefined和符号。如果变量是原始类型,则在分配时按值传递。也就是说,每次分配基元时,都会复制值并将其分配给新变量。 

几乎所有不是JavaScript中原始语言的东西都是对象。在对象文字表示法中,对象属性用大括号表示。 

数组本身也被实现为JavaScript中的对象。可以使用Array()构造函数或以方括号表示的文字符号来创建数组。 

函数在此模块中有其自己的单元,因此我们在这里不再讨论它们,但是基于以上内容,让我们通过bike使用对象文字符号定义更复杂的对象的方法。

const bike = {
  frontGearIndex: 0,
  rearGearIndex: 0,
  transmission: {
    frontGearTeeth: [30,45],
    rearGearTeeth: [11,13,15,17,19,21,24,28,32,36]
  },
  calculateGearRatio: function() {
    let front = this.transmission.frontGearTeeth[this.frontGearIndex],
        rear = this.transmission.rearGearTeeth[this.rearGearIndex];
    return (front / rear);
  },
  changeGear: function(frontOrRear, newValue) {
    if (frontOrRear === 'front') {
      this.frontGearIndex = newValue;
    } else {
      this.rearGearIndex = newValue;
    }
  }
};

通过括号语法引用属性

引用对象成员通常是使用点符号来完成的。例如,在前面的示例中,我们按如下方式引用对象的属性和功能。

bike.frontGearIndex
bike.transmission.frontGearTeeth
bike.calculateGearRatio()

用点表示法,对属性名称有严格的规定。但是,JavaScript还允许使用另一种称为括号符号的语法。上面的成员将在括号中引用如下。

bike["frontGearIndex"]
bike["transmission"]["frontGearTeeth"]
bike["calculateGearRatio"]()

虽然需要更多的输入,但方括号表示法有两个好处。您可以为属性或函数命名任何所需的名称,并且由于它是字符串,因此可以通过变量传递属性或函数名称并进行调用。

让我们通过重新构想changeGear功能来了解这一点。现在,我们使用四个功能来定义前后齿轮的上下变速。在changeGear函数中,我们根据String参数构造要调用的函数名称,然后调用它。 

changeGear: function(frontOrRear, upOrDown) {
  let shiftFunction = frontOrRear + upOrDown;
  this[shiftFunction]();
},
frontUp: function(){
  this.frontGearIndex += 1;
},
frontDown: function(){
  this.frontGearIndex -= 1;
},
rearUp: function(){
  this.rearGearIndex += 1;
},
rearDown: function(){
  this.rearGearIndex -= 1;
}

将它们添加到我们的自行车对象中,我们可以在工作中看到它们。 

console.log(bike.calculateGearRatio()); // 2.727272727
//Calls the frontUp() function
bike.changeGear("front", "Up");
console.log(bike.calculateGearRatio()); // 4.090909091
//calls the rearUp() function
bike.changeGear("rear", "Up");
console.log(bike.calculateGearRatio()); // 3.461538461

对象可变性

除了用于定义对象的不同语法外,JavaScript对象还有另一个关键原理:可变性。 

JavaScript中的对象是可变的,这意味着如果您想要修改对象的形状,则可以。 

让我们来看看bike我们创建的对象。例如,我们可以添加一个新的属性或函数。 

bike.isTandem = true;
bike.popAWheelie = function() {
…
};

即使您可能无法访问最初定义对象的代码,也可以在将对象保存在内存中后修改其形状。不过,重要的一点是,只有一个对象实例发生了变化。让我们回顾一下我们的Bike 构造函数:

const bike1 = new Bike();
const bike2 = new Bike();
bike1.isTandem = true;
console.log(bike1.isTandem); // true
console.log(bike2.isTandem); // undefined

如果您希望多个对象共享相同的属性或方法,则有一个继承模型。让我们看看。 

对象与继承

尽管没有古典语言定义的类,JavaScript仍具有一个继承模型,称为原型继承。 

实际上,原型是另一个对象。它位于内存中,并定义其他对象共享相同原型时所继承的属性或函数。 

传统上,JavaScript对象通过共享相同的构造函数来共享相同的原型。记住Bike 构造函数。我们将changeGear函数分配给prototype。 

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

这样,从中创建的每个对象都会Bike继承该changeGear函数。 

您还可以使用原型实现多级继承。它被称为原型链。使用构造函数来实现原型链非常复杂,并且需要大量样板代码。它也超出了本模块的范围。您需要知道的是,为了解决原型链的复杂性,ECMA为实现继承的更直接的语法制定了一个标准:class语法。 

类和JavaScript

如果您阅读“类”一词并感到温暖而模糊,以为您正在寻找可以创建基于类的真正继承的东西,请准备感到失望。尽管如此,classJavaScript中的关键字还是一个不错的语法糖,可以使用构造函数来解决原型继承的复杂性。在幕后,引擎仍在使用,Object.create并且没有类(在面向对象的意义上),只是内存中的原型对象是实际的继承源。 

好消息是,它确实从Java或C#中读取了很多类似的代码,并且需要考虑一些JavaScript特定的内容。 

注意

当由Lightning Web Components使用时,包括类语法的多级原型链超出了此模块。有关使用JavaScript对象的较新功能的更多详细信息,请参见本单元末尾的资源链接。我们强烈建议您在本教程中使用其他 模块,即Modern JavaScript Development,该模块在Classes中有一个完整的单元

尽管我们在这里不会深入探讨JavaScript类的细节,但出于学术目的,很高兴看到使用类语法实现的bike对象的版本。 

class Bike {
    constructor(gears, startGear){
        this.gears = gears;
        this.currentGear = startGear;
    }
    changeGear(direction, changeBy) {
        if (direction === 'up') {
            this.currentGear += changeBy;
        } else {
            this.currentGear -= changeBy;
        }
    }
}
const bike = new Bike(10, 5);
console.log(bike.currentGear); // 5
bike.changeGear('up', 2);
console.log(bike.currentGear); // 7

如您所见,语法看起来更像Java或Apex中的类。明显的区别是,构造函数始终为命名constructor。一个重要的功能是,函数和属性自动属于原型链,而不必直接引用Object.prototype。这也简化了创建多级原型继承的过程。 

闪电Web组件和对象

本单元的几个部分与开发Lightning Web Components有关,包括我们讨论的一些语法以及原型链。 

类和闪电Web组件

闪电Web组件利用了JavaScript的许多现代改进,最明显的是使用了类语法。组件通常由JavaScript类定义,该类扩展了另一个名为LightningElement的类。看起来是这样的: 

import { LightningElement } from lwc;
export default class MyComponent extends LightningElement {
    myProperty;
    myFunction() {
        console.log(this.myProperty);
    }
} 

Lightning Web组件的功能在JavaScript类中定义。此示例还使用了一些关于模块(importexport)尚未涉及的语法。

论对象文字

在本模块的某些示例中,为了学习对象的工作方式,我们在对象文字中声明了函数。请注意,这不是现代JavaScript中推荐的做法。对象文字是创建临时数据结构以在JavaScript程序的功能部分之间传递数据的一种好方法,但是您应避免在对象文字中定义函数。