学习目标
完成本单元后,您将能够:
- 描述JavaScript运行时环境的性质。
- 区分JavaScript引擎和语言。
- 学习JavaScript时避免关键陷阱。
- 描述一些重要的JavaScript最佳实践。
JavaScript的内容,原因和方式
在2000年代初期,软件世界开始爆炸,您可以构建完全通过Internet托管和交付的应用程序。尽管我们现在将Web应用程序视为理所当然,但当时它们就像看到机器人骑自行车一样令人着迷。
浏览器是简单的HTML渲染器,而HTML和JavaScript标准是分散的。要创建具有甚至稍微复杂的功能的网页,您必须强制用户仅使用一种浏览器,通常是Internet Explorer。直到今天,由于这个时代做出的设计决策,仍有一些应用程序强制执行此操作。
为了解决这些局限性,服务器端UI框架得到了发展,允许软件开发人员在服务器上动态创建网页。在服务器上执行计算能力的复杂逻辑:然后,将渲染的HTML投放到功能弱的浏览器中。在这个世界上,JavaScript主要是一种使页面具有某种程度的交互性并执行基本逻辑而无需服务器跳闸的方法。服务器端框架非常流行,其中包括Salesforce框架Visualforce。为了证明这种受欢迎程度,今天在Salesforce中存在着数百万个Visualforce页面。
快进当今世界是一个不同的地方。浏览器是功能强大的应用程序,可以优化网页查看。JavaScript已按照ECMAScript标准进行了很好的标准化,并且顶级浏览器制造商通常都擅长采用新功能。今天的Web应用程序具有丰富的用户界面,该用户界面在浏览器中运行现代JavaScript。代替服务器端框架,现代Web应用程序倾向于客户端渲染。在Salesforce中,这是通过Lightning Component Framework完成的。但是,对于许多开发人员而言,编写JavaScript仍然是新事物。如果您主要使用Visualforce页面和Apex控制器,则可能需要全面了解JavaScript的工作原理,以便更好地了解您的组件。
是时候提高您的JavaScript技能了。
JavaScript运行时
JavaScript运行时是解释JavaScript代码的引擎。它可以是浏览器或服务器等其他运行时环境的一部分。现代JavaScript引擎功能完善,功能强大,可以优化执行,并符合ECMAScript标准。
JavaScript引擎的定义功能是由下面的堆栈表示的单线程运行时。在堆栈中完成的工作拥有线程,并且必须先完成其同步逻辑,然后再移交对线程的控制。
运行时是一个繁忙的地方。可以随时随地从许多来源来进行新工作,包括用户(UI事件)和Web API(例如地理位置或设备移动)。由于只有一个线程,因此有一个队列等待工作轮到使用该线程。
当堆栈为空时,事件循环从队列中获取等待完成的工作,并将其移入堆栈。
当然,这是一种简化,但是它说明了JavaScript引擎如何完成工作的基本模型。由于具有这种体系结构,因此JavaScript语言可以像实际一样工作。
JavaScript语言
作为一种语言,JavaScript经常被误解。如果您将其拾起任何时间,无疑会问自己一些问题。它是脚本语言吗?它与Java有什么关系?它是一种真正的编程语言吗?为什么会做那?
让我们围绕JavaScript作为一种语言来设置一些上下文,以帮助您回答其中的一些问题。
JavaScript正在改变
因为JavaScript是根据ECMAScript标准构建的,所以它一直在变化。每年都会发布描述新功能的标准更新,然后JavaScript引擎项目(浏览器和运行时制造商)将它们安装到位。
随着JavaScript的成熟,更新可能包括更多现代语言功能。在其他情况下,添加功能以实现现有功能的更简洁的语法(这些称为语法糖)。
API的采用并不普遍
这句话看起来很吓人,但是绝大多数JavaScript API都可以在最常见的浏览器平台上使用。尽管如此,实现者并没有以相同的速度采用每种语言功能或API。有些人从不采用某些功能(尽管这种情况很少见)。通常,如果您想使用该语言的较新功能,则应使用caniuse.com之类的资源来了解它在目标浏览器中的 运行情况。
如果新功能不是本机实现的,那么通常会编写一些代码来暂时实现该缺失功能的目的。此临时填写代码称为polyfill。实际上,Lightning Component Framework使用精选的polyfill列表,这些列表在运行其他代码之前被应用以自动改善浏览器的兼容性。
必不可少的事情
现在该看一些代码了。让我们从每个JavaScript开发人员应该知道的一些事情开始,使他们的生活更轻松。
记住区分大小写
JavaScript区分大小写。对于许多习惯于Apex和SOQL不区分大小写的Salesforce开发人员而言,这是棘手的。请记住,每次在Salesforce中编写JavaScript代码时,请注意区分大小写。
声明变量
变量声明做是使用三个运营商之一:var
,let
,和const
。一般使用let
和const
。下表总结了这三个关键字之间的功能差异。
关键词 | 范围 | 可变分配 |
---|---|---|
变种 | 功能 | 是 |
让 | 块 | 是 |
const | 块 | 没有 |
范围是我们稍后讨论的主题,因此让我们解决可变性的含义。
所有变量都是指针。赋值是将变量指向内存中某物的行为。可变性是在初始分配变量后是否可以重新分配变量。使用var
或let
创建可变指针,而该指针const
是不可变的。通常,最好通过演示来理解这一点 。
注意
JavaScript游乐场是一个有价值的工具,可以试用代码,并在浏览器中实时查看其工作方式。有很多JavaScript游乐场,但是在本模块中,我们将链接到jsbin中的示例 。
//primitive assignments
var myBike = "Mountain Bike";
let currentGear = 5;
const numberOfGears = 12;
//reassignment
myBike = "Penny Farthing"; // this works
currentGear = 1; // so does this
numberOfGears = 1; // error
以上,myBike
并且currentGear
在重新分配值时没有问题。但是,当尝试这样做时numberOfGears
,会出现错误。
当 工作与对象(而不是原语),请记住,const
只有防止重新分配您的变量设置不同的对象。仍然可以更改对象本身(其属性,函数等),如下所示。
// call constructor, new object, assign it to bike
const bike = new Bike();
//Change internal state by calling a function
bike.changeGear("front", "Up");
// add a new member to bike that did not exist before
bike.type = "Penny Farthing";
// check for success
console.log(bike.calculateGearRatio()); // 4.0909...
console.log(bike.type); // "Penny Farthing"
// attempt to point bike to new instance of Bike
bike = new Bike(1,2); // error
在这里,我们从Bike
构造函数创建一个对象,并将其分配给bike
变量(记住,区分大小写)。然后,我们可以更改所需的任何内容,例如调用更改其状态甚至添加新成员的函数。但是,当我们尝试将Bike重新分配给其他东西时,在这种情况下,通过再次调用构造函数,就会出现错误。
隐式强制
当大多数JavaScript运算符遇到无效类型时,它们会尝试将值转换为有效类型。这种隐式转换类型的过程称为隐式类型强制。考虑以下:
let num1 = 9 * "3";
console.log(num1); // 27 (a number)
let num2 = 9 + "3";
console.log(num2); // "93" (a string)
在第一个示例中,*
运算符只能用于数学运算,将字符串强制"3"
为数字。结果为27。在第二个+
运算符中,该运算符将字符串视为"3"
一元运算符(并置)。这将强制9以字符串"9"
的结果强制输出到字符串"93"
。
乍一看,这似乎很方便,但实际上可能导致令人困惑的结果。
不要使用隐式强制
隐式类型强制的许多实例令人困惑。例如布尔比较。C系列语言常用的==
and !=
比较运算符将尝试将任何内容转换为布尔值。有确定性的规则,但是它们太复杂而无法实际应用。这是一些有趣的例子。
false == ""; // true
false == "0"; // true
"" == "0"; // false
[0] == 0; // true
“为什么这样做?”
究竟。
对于布尔比较,最佳实践是使用===
和!==
。使用这些运算符,基本类型仅在类型和值都匹配时才等效,并且对象比较仅在它们各自的指针指向相同的内存地址时才为真。尝试与上述相同的比较:
false === ""; // false
false === "0"; // false
"" === "0"; // false
[0] === 0; // false
真假
您知道我们刚刚针对隐式类型强制发出警告的规则吗?好吧,这是个例外。
当表达式期望布尔值时,以下值始终被视为false。
false
(当然)0
(数字零)""
或''
(一个空字符串)null
undefined
NaN
(数学运算失败的结果)
false
是false
(当然!)。这些值的其余部分被强制为false
。作为一个整体,他们被称为虚假。
毫无疑问,true
仅此而已true
。但是任何其他类型的值都被强制转换为true
。我们称这些价值观是真实的。
这有一个方便的副作用。假设您要通过简单地将变量传递到if表达式中来测试几种类型的无效数据。
const myRecord = response.getReturnValue();
if (myRecord) {
//now process the record
}
如果由于某种原因上述分配失败,并且我们最终得到一个未初始化的变量(undefined
),一个空字符串或0
,我们仅通过对该myRecord
变量进行条件检查就覆盖了所有这些基础。这是JavaScript中广泛接受的做法。
this
是棘手的
该模块包含有关如何使用this
指针的整个章节。它具有定义明确的规则,但是this
即使在同一功能内,指向的内容也可以更改。例如,在Apex类中,您可能会看到:
public class MyApexClass {
private String subject;
public MyApexClass(String subject) {
this.subject = subject;
}
}
在此示例Apex类中,this
仅引用的当前实例MyApexClass
。在JavaScript中,this
指向的内容不取决于定义函数的位置,而是取决于该函数的调用位置。更多关于this
以后。
功能就是价值
在JavaScript中,一切都是对象。这也适用于功能。与其他对象一样,可以将函数分配给变量,将其传递给其他函数的参数,并使用与其他任何对象相同的方式。
如果您未使用函数是一等公民或使用lambda的语言,这可能会有些令人震惊。稍后,我们将整个单元用于功能上。
此后,还有更多其他事情。
现在,您已经对JavaScript概念有了很好的基本介绍。接下来,我们来看一下JavaScript在浏览器中的工作方式。