JavaScript
范例 | 多范式:事件驱动,功能性,命令性,程序性,面向对象的编程 |
---|---|
设计 | Netscape的Brendan Eich最初;其他人也为授予标准做出了贡献 |
首先出现 | 1995年12月4日 |
稳定版本 | ecmascript 2021 /2021年6月 |
预览发布 | ecmascript 2022 /22 2021年7月22日 |
打字学科 | 动态,虚弱,鸭 |
文件名扩展 |
|
网站 | ecma-international.org/publications-and-andards/standards/ecma-262/ |
主要实施 | |
V8 , JavascriptCore , Spidermonkey , Chakra | |
被影响 | |
Java ,方案,自我,尴尬,高度 | |
受影响 | |
ActionScript ,汇编, Coffeescript , Dart , Haxe , JS ++ , OPA , Typescript | |
|
JavaScript ( )通常是JS的缩写,是全球网络的编程语言和核心技术,以及HTML和CSS 。截至2023年,98.7%的网站在客户端使用JavaScript进行网页行为,通常会合并第三方库。所有主要的Web浏览器都有专用的JavaScript引擎,可以在用户设备上执行代码。
JavaScript是一种高级,通常是及时编译的语言,它符合Ecmascript标准。它具有动态键入,基于原型的对象方向和一流的功能。它是多范式,支持事件驱动,功能和命令性编程样式。它具有用于使用文本,日期,正则表达式,标准数据结构和文档对像模型(DOM)的应用程序编程接口(API)。
eCmascript标准不包括任何输入/输出(I/O),例如网络,存储或图形设施。实际上,Web浏览器或其他运行时系统为I/O提供JavaScript API。
JavaScript引擎最初仅在Web浏览器中使用,但现在是某些服务器和各种应用程序的核心组件。此用法最受欢迎的运行时系统是Node.js。
尽管Java和JavaScript的名称,语法和各自的标准库是相似的,但两种语言是不同的,并且在设计方面有很大差异。
历史
在Netscape上创建
1993年发布了具有图形用户界面Mosaic的第一个流行的Web浏览器。非技术人员可以访问,它在新生的万维世界网络的快速增长中发挥了重要作用。 Mosaic的首席开发商随后成立了Netscape Corporation,该公司于1994年发布了更加抛光的浏览器Netscape Navigator 。
在网络的这些形成年中,网页只能是静态的,在将页面加载到浏览器中后缺乏动态行为的能力。在蓬勃发展的网络开发场景中,人们渴望消除这一限制,因此1995年,Netscape决定向Navigator添加脚本语言。他们采用了两条路线来实现这一目标:与Sun Microsystems合作以嵌入Java编程语言,同时还雇用Brendan Eich嵌入了方案语言。
Netscape Management很快决定,最好的选择是Eich设计一种新语言,语法类似于Java,却不像方案或其他现存的脚本语言。尽管在1995年9月作为导航器Beta的一部分时,新的语言及其指导者的实施被称为Livescript,但该名称于12月更名为JavaScript以供正式发布。
JavaScript名称的选择引起了混乱,这意味着它与Java直接相关。当时,互联网繁荣开始,Java是一种流行的新语言,因此Eich认为JavaScript名称是Netscape的营销策略。
微软采用
Microsoft于1995年首次亮相Internet Explorer ,导致了Netscape的浏览器战争。在JavaScript方面,Microsoft对导航器解释器进行了反向工程,以创建自己的JScript 。
JavaScript于1996年首次发行,并最初支持CSS和对HTML的扩展。这些实现中的每一个都与导航器中的同行明显不同。这些差异使开发人员很难在两个浏览器中使其网站运行良好,从而广泛使用“ Netscape中的最佳观看”和“在Internet Explorer中最佳观看”徽标几年。
JScript的兴起
1996年11月, Netscape将JavaScript提交给ECMA International ,这是所有浏览器供应商都可以符合的标准规范的起点。这导致了1997年6月的第一个Ecmascript语言规范的正式发布。
标准过程持续了几年,随着Ecmascript 2于1998年6月发布,以及1999年12月的Ecmascript 3。《 Ecmascript 4的工作》于2000年开始。
同时,微软在浏览器市场中的地位越来越重要。到2000年代初,Internet Explorer的市场份额达到了95%。这意味着JScript成为网络上客户端脚本的事实上的标准。
微软最初参与了标准过程,并用其JScript语言实施了一些建议,但最终它不再在ECMA工作上进行合作。因此,ecmascript 4被封存。
增长和标准化
在2000年代初的Internet Explorer统治期间,客户端的脚本停滞不前。 2004年,当Netscape Mozilla的继任者发布Firefox浏览器时,情况开始发生变化。 Firefox受到了许多人的好评,从Internet Explorer那里获得了很大的市场份额。
2005年,Mozilla加入了ECMA International,并开始了XML(E4X)标准的Ecmascript 。这导致Mozilla与Macromedia (后来被Adobe Systems收购)共同合作,后者以其ActionScript 3语言实施E4X,该语言基于Ecmascript 4草案。该目标成为标准化ActionScript 3作为新的Ecmascript 4.为此,Adobe Systems发布了Tamarin实施为开源项目。但是,他这纳林和ActionScript 3与已建立的客户端脚本截然不同,而没有微软的合作,Ecmasipript 4从未达到实现。
同时,在不隶属ECMA工作的开源社区中发生了非常重要的发展。 2005年,杰西·詹姆斯·加勒特(Jesse James Garrett)发行了一份白皮书,他创造了Ajax一词并描述了一套技术,其中JavaScript是骨干,以创建可以将数据加载到后台的Web应用程序,避免需要完整页面重新加载。这引发了一个文艺复兴时期的JavaScript,由开源图书馆和周围形成的社区带头。创建了许多新的库,包括jQuery , Prototype , Dojo Toolkit和Mootools 。
Google在2008年首次推出了Chrome浏览器, V8 JavaScript引擎的速度快于竞争对手。关键的创新是即时汇编(JIT),因此其他浏览器供应商需要对JIT进行大修。
2008年7月,这些不同的政党在奥斯陆举行了一次会议。这导致了2009年初最终达成协议,以结合所有相关的工作并推动语言向前发展。结果是2009年12月发布的Ecmascript 5标准。
达到成熟
关于该语言的雄心勃勃的工作持续了几年,最终在2015年发表Ecmascript 6时正式进行了广泛的添加和改进。
Ryan Dahl于2009年创建Node.js ,在Web浏览器之外的JavaScript使用显著增加。节点结合了V8引擎,事件循环和I/O API ,从而提供了独立的JavaScript运行时系统。截至2018年,NODE已被数百万开发人员使用, NPM拥有世界上任何包装管理器的最多模块。
目前,Ecmascript草案规范在GitHub上公开维护,并且通过常规年度快照制作版本。通过全面的提案过程审查对语言的潜在修订。现在,开发人员分别检查即将到来的功能的状态,而不是版本的数字。
当前的JavaScript生态系统具有许多库和框架,已建立的编程实践以及在Web浏览器外的JavaScript的大量用法。另外,随着单页应用程序和其他JavaScript繁重的网站的兴起,已经创建了几个转介剂来帮助开发过程。
商标
“ JavaScript”是美国Oracle Corporation的商标。该商标最初于1997年5月6日发行给Sun Microsystems ,并于2009年收购Sun时被转移到Oracle。
网站客户端用法
JavaScript是Web的主要客户端脚本语言,为此目的,所有网站(2022年中期)中有98%。脚本嵌入或包含在HTML文档中并与DOM进行交互。所有主要的Web浏览器都具有内置的JavaScript引擎,该引擎在用户的设备上执行代码。
脚本行为的示例
- 通过AJAX或Websocket加载新的网页内容而无需重新加载页面。例如,社交媒体的用户可以在不离开当前页面的情况下发送和接收消息。
- 网页动画,例如褪色的对象进出,调整大小和移动它们。
- 玩浏览器游戏。
- 控制流媒体的播放。
- 生成弹出广告或警报框。
- 在将数据发送到Web服务器之前,验证Web表单的输入值。
- 记录有关用户行为的数据,然后将其发送到服务器。网站所有者可以将这些数据用于分析,广告跟踪和个性化。
- 将用户重定向到另一页。
- 通过存储或IndexEdDB标准在用户设备上存储和检索数据。
网络库和框架
超过80%的网站使用第三方JavaScript库或Web框架进行客户端脚本。
jQuery
JQuery是迄今为止最受欢迎的客户端库,由超过75%的网站使用。
反应
角
香草JS
相比之下,“香草JS”一词是针对不使用任何库或框架的网站创造的,而是完全依靠标准的JavaScript功能。
其他用法
JavaScript的使用已超出其Web浏览器根。现在,用于服务器端网站部署和非浏览器应用程序中的JavaScript引擎现在嵌入了许多其他软件系统中。
Netscape Enterprise Server和Microsoft的Internet信息服务的最初尝试促进服务器端JavaScript使用情况,但它们是小壁c。服务器端的使用最终在2000年代后期开始增长,并创建了Node.js和其他方法。
电子, Cordova , React本地和其他应用框架已被用来创建许多应用程序,并在JavaScript中实现了行为。其他非浏览器应用程序包括用于脚本PDF文档的Adobe Acrobat支持和用JavaScript编写的Gnome Shell Extensions。
JavaScript最近开始出现在某些嵌入式系统中,通常是通过利用Node.J。
执行系统
及时的汇编
JavaScript引擎
JavaScript引擎是执行JavaScript代码的软件组件。第一批JavaScript引擎仅仅是口译员,但是所有相关的现代引擎都使用即时编译来提高性能。
JavaScript引擎通常由Web浏览器供应商开发,每个主要浏览器都有一个。在浏览器中,JavaScript引擎通过文档对像模型与渲染引擎一起运行。
JavaScript引擎的使用不限于浏览器。例如, V8引擎是Node.js和DeNo运行时系统的核心组件。
由于Ecmascript是JavaScript的标准化规范,因此Ecmascript引擎是这些引擎的另一个名称。随着WebAssembly的出现,某些引擎也可以在与常规JavaScript代码相同的沙盒中执行此代码。运行环境
JavaScript通常依赖运行时环境(例如Web浏览器)来提供脚本可以与环境交互的对象和方法(例如,网页dom )。这些环境是单线的。 JavaScript还依靠运行时环境来提供包括/导入脚本的能力(例如, HTML<script>
元素)。这本身不是语言功能,但在大多数JavaScript实现中很常见。 JavaScript一次从一个队列中处理消息。 JavaScript调用与每个新消息关联的函数,创建使用该函数的参数和本地变量的呼叫堆栈帧。呼叫堆栈根据功能的需求收缩和生长。当函数完成后,呼叫堆栈为空时,JavaScript继续进行队列中的下一条消息。这称为事件循环,被称为“运行到完成”,因为在考虑下一个消息之前,每个消息都已完全处理。但是,语言的并发模型将事件循环描述为非阻滞:程序输入/输出是使用事件和回调功能执行的。例如,这意味着JavaScript在等待数据库查询以返回信息时可以处理鼠标单击。
例子
node.js
丹诺
特征
除非另有明确指定,否则所有符合Ecmascript实现的所有功能都是常见的。
势在必行和结构化
JavaScript支持C中的许多结构化编程语法(例如,if
陈述,while
循环,switch
陈述,do while
循环等)。一个部分例外是范围:最初JavaScript仅具有功能范围var
;在2015年的Ecmascript中添加了块范围,关键字let
和const
。像C一样,JavaScript在表达式和语句之间有所区别。 C与C的一种句法差异是自动插入,该插入允许省略半隆(终止语句)。
弱输入
JavaScript弱键入,这意味着某些类型是根据所使用的操作隐式施放的。
- 二进制
+
除非两个操作数是数字,否则操作员将这两个操作数施放到字符串上。这是因为加法运算符作为串联操作员加倍 - 二进制
-
操作员始终将两个操作数施放到一个数字 - 两个一元运营商(
+
,-
)始终将操作数施加到一个数字
值像以下内容一样铸入字符串:
- 字符串留下
- 数字转换为他们的字符串表示
- 阵列将其元素投入到弦上,之后逗号加入(逗号)(
,
) - 其他对象转换为字符串
[object Object]
在哪里Object
是对象的构造函数的名称
值通过将其施放到字符串,然后将字符串铸成数字来投射到数字。这些过程可以通过定义来修改toString
和valueOf
分别在字符串和数字铸造的原型上功能。
JavaScript因其实施这些转换的方式而受到批评,因为规则的复杂性可能是不一致的。例如,在执行串联之前将数字添加到字符串时,将数字施加到字符串中,但是当从字符串中减去数字时,在执行减法之前,将字符串铸成数字。
左操作数 | 操作员 | 正确的操作数 | 结果 |
---|---|---|---|
[] (空数组) |
+
|
[] (空数组) |
"" (空字符串) |
[] (空数组) |
+
|
{} (空对象) |
"[object Object]" (细绳) |
false (布尔) |
+
|
[] (空数组) |
"false" (细绳) |
"123" (细绳) |
+
|
1 (数字) |
"1231" (细绳) |
"123" (细绳) |
-
|
1 (数字) |
122 (数字) |
"123" (细绳) |
-
|
"abc" (细绳) |
NaN (数字) |
经常也提到的是{} + []
导致0
(数字)。这是误导的:{}
被解释为一个空代码块,而不是一个空对象,而其余的单元将空数组施加到数字+
操作员。如果将表达式包裹在括号中({} + [])
卷曲支架被解释为一个空对象,表达式的结果是"[object Object]"
正如预期的。
动态的
打字
JavaScript像大多数其他脚本语言一样动态键入。一种类型与值而不是表达式关联。例如,最初绑定到一个数字的变量可以将其重新分配到字符串。 JavaScript支持各种测试对像类型的方法,包括鸭打字。
运行时间评估
JavaScript包括一个eval
可以在运行时执行作为字符串的语句的函数。
对象取向(基于原型)
道格拉斯·克罗克福德(Douglas Crockford)将JavaScript的原型继承描述为:
您制作原型对象,然后...制作新实例。对像在JavaScript中是可变的,因此我们可以增强新实例,从而为它们提供新的字段和方法。然后,这些可以充当更新的对象的原型。我们不需要类来制作许多类似对象...对像从对象继承。有什么比这更面向对象的呢?
在JavaScript中,一个对象是一个关联数组,并用原型增强(请参见下文);每个键提供对象属性的名称,并且有两种句法方法来指定此类名称:dot note(obj.x = 10
)和括号符号(obj['x'] = 10
)。可以在运行时添加,反弹或删除属性。可以使用一个对象的大多数属性(以及属于对象原型继承链的任何属性)。for...in
环形。
原型
JavaScript使用原型,许多其他面向对象的语言都使用类来继承。可以在JavaScript中使用原型模拟许多基于类的功能。
充当对象构造函数
功能兼作对象构造函数及其典型作用。将函数调用与new进行后缀将创建一个原型的实例,从构造器继承属性和方法(包括来自Object
原型)。 ecmascript 5提供Object.create
方法,允许明确创建实例,而无需自动从Object
原型(较旧的环境可以将原型分配给null
)。构造函数prototype
属性确定用于新对象内部原型的对象。可以通过修改用作构造函数的函数的原型来添加新方法。 JavaScript的内置构造函数,例如Array
或者Object
,还具有可以修改的原型。虽然可以修改Object
原型,通常被认为是不良实践,因为JavaScript中的大多数对像都会从Object
原型,它们可能不会期望修改原型。
用作方法
与许多面向对象的语言不同,在JavaScript中,函数定义和方法定义之间没有区别。相反,区别发生在函数调用期间。当将函数称为对象的方法时,该函数的本地此关键字将与该对象绑定到该对象。
功能
JavaScript功能是一流的;函数被认为是对象。因此,函数可能具有属性和方法,例如.call()
和.bind()
.
词汇封闭
嵌套函数是在另一个函数中定义的函数。每次调用外部功能时,都会创建它。
此外,每个嵌套函数形成词汇封闭:外部函数的词汇范围(包括任何常数,局部变量或参数值)也成为每个内部功能对象的内部状态的一部分,即使执行外部函数。
匿名功能
JavaScript还支持匿名功能。
授权
JavaScript支持隐式和明确的授权。
用作角色(特质和混合物)
JavaScript本地支持特质和混合素等角色模式的各种基于功能的实现。这样的功能通过至少一种方法定义了其他行为this
其中的关键字function
身体.然后必须通过call
或者apply
对于需要特征其他行为的对象,这些行为未通过原型链共享。
对象组成和继承
尽管基于明确的函数委托确实涵盖了JavaScript中的构图,但每次行走原型链时,隐式委托就已经发生了,例如,以例如找到一种可能与对像有关但不是直接拥有的方法。一旦找到该方法,它就会在此对象的上下文中被调用。因此,JavaScript中的继承被委托自动化范围覆盖,该委托与构造函数函数的原型属性结合。
各种各样的
基于零的编号
JavaScript是一种零索引语言。
variadic函数
无限数量的参数可以传递给函数。该功能可以通过正式参数以及本地访问它们arguments
目的。也可以通过使用bind
方法。
数组和对象文字
就像在许多脚本语言中一样,可以使用简洁的快捷方式语法创建每个语言的数组和对象(其他语言的关联数组)。实际上,这些文字构成了JSON数据格式的基础。
常用表达
以类似于Perl的方式,JavaScript还支持正则表达式,该表达式为文本操作提供了一种简洁而强大的语法,该语法比内置的字符串函数更复杂。
承诺和异步/等待
承诺
内置的承诺对象为处理承诺和将处理程序与异步动作的最终结果相关联提供了功能。最近,JavaScript规范引入了Comminator方法,该方法使开发人员可以根据不同方案结合多个JavaScript承诺并进行操作。引入的方法是:Promise.race,Promise.All,Promise.AllSettled和Promise.any。
异步/等待
异步/等待允许以类似于普通同步函数的方式构造异步,非阻滞函数。可以编写异步,非阻滞代码,其顶部最小,结构类似于传统同步,阻止代码。
供应商特定的扩展
从历史上看,一些JavaScript引擎支持这些非标准功能:
- 有条件
catch
条款(例如Java) - 数组综合和发电机表达式(如Python)
- 简明函数表达式(
function(args) expr
;这个实验性语法早于箭头函数) - XML(E4X)的eCmascript ,该扩展名为Ecmascript添加本机XML支持(自版本21以来,在Firefox中不支持)
句法
简单的例子
可以使用JavaScript中的变量使用var
,let
或者const
关键字。在全局范围内定义未定义的没有关键字的变量。
// Declares a function-scoped variable named `x`, and implicitly assigns the
// special value `undefined` to it. Variables without value are automatically
// set to undefined.
// var is generally considered bad practice and let and const are usually preferred.
var x;
// Variables can be manually set to `undefined` like so
let x2 = undefined;
// Declares a block-scoped variable named `y`, and implicitly sets it to
// `undefined`. The `let` keyword was introduced in ECMAScript 2015.
let y;
// Declares a block-scoped, un-reassignable variable named `z`, and sets it to
// a string literal. The `const` keyword was also introduced in ECMAScript 2015,
// and must be explicitly assigned to.
// The keyword `const` means constant, hence the variable cannot be reassigned
// as the value is `constant`.
const z = "this value cannot be reassigned!";
// Declares a global-scoped variable and assigns 3. This is generally considered
// bad practice, and will not work if strict mode is on.
t = 3;
// Declares a variable named `myNumber`, and assigns a number literal (the value
// `2`) to it.
let myNumber = 2;
// Reassigns `myNumber`, setting it to a string literal (the value `"foo"`).
// JavaScript is a dynamically-typed language, so this is legal.
myNumber = "foo";
请注意上面的示例中的评论,所有这些评论都在两个前向斜线之前。
JavaScript中没有内置输入/输出功能,而是由运行时环境提供。第5.1版中的Ecmascript规范提到:“本规范中没有针对外部数据或计算结果输出的规定”。但是,大多数运行时环境都有console
可用于打印输出的对象。这是带有控制台对象的运行时环境中JavaScript中简约的Hello World程序:
console.log("Hello, World!");
在HTML文档中,输出需要这样的程序:
// Text nodes can be made using the "write" method.
// This is frowned upon, as it can overwrite the document if the document is fully loaded.
document.write('foo');
// Elements can be made too. First, they have to be created in the DOM.
const myElem = document.createElement('span');
// Attributes like classes and the id can be set as well
myElem.classList.add('foo');
myElem.id = 'bar';
// After setting this, the tag will look like this: `<span class="foo" id="bar" data-attr="baz"></span>`
myElem.setAttribute('data-attr', 'baz'); // Which could also be written as `myElem.dataset.attr = 'baz'`
// Finally append it as a child element to the <body> in the HTML
document.body.appendChild(myElem);
// Elements can be imperatively grabbed with querySelector for one element, or querySelectorAll for multiple elements that can be looped with forEach
document.querySelector('.class'); // Selects the first element with the "class" class
document.querySelector('#id'); // Selects the first element with an `id` of "id"
document.querySelector('[data-other]'); // Selects the first element with the "data-other" attribute
document.querySelectorAll('.multiple'); // Returns an Array-like NodeList of all elements with the "multiple" class
function factorial(n) {
// Checking the argument for legitimacy. Factorial is defined for positive integers.
if (isNaN(n)) {
console.error("Non-numerical argument not allowed.");
return NaN; // The special value: Not a Number
}
if (n === 0)
return 1; // 0! = 1
if (n < 0)
return undefined; // Factorial of negative numbers is not defined.
if (n % 1) {
console.warn(`${n} will be rounded to the closest integer. For non-integers consider using gamma function instead.`);
n = Math.round(n);
}
// The above checks need not be repeated in the recursion, hence defining the actual recursive part separately below.
// The following line is a function expression to recursively compute the factorial. It uses the arrow syntax introduced in ES6.
const recursivelyCompute = a => a > 1 ? a * recursivelyCompute(a - 1) : 1; // Note the use of the ternary operator `?`.
return recursivelyCompute(n);
}
factorial(3); // Returns 6
匿名功能(或lambda):
const counter = function() {
let count = 0;
return function() {
return ++count;
}
};
const x = counter();
x(); // Returns 1
x(); // Returns 2
x(); // Returns 3
该示例表明,在JavaScript中,函数通过参考捕获其非本地变量。
箭头功能首先在第6版-Ecmasipript 2015中引入。他们缩短了在JavaScript中编写功能的语法。箭头功能是匿名的,因此需要一个变量来引用它们以在创建后调用它们,除非被括号包围并立即执行。
箭头功能的示例:
// Arrow functions let us omit the `function` keyword.
// Here `long_example` points to an anonymous function value.
const long_example = (input1, input2) => {
console.log("Hello, World!");
const output = input1 + input2;
return output;
};
// If there are no braces, the arrow function simply returns the expression
// So here it's (input1 + input2)
const short_example = (input1, input2) => input1 + input2;
long_example(2, 3); // Prints "Hello, World!" and returns 5
short_example(2, 5); // Returns 7
// If an arrow function has only one parameter, the parentheses can be removed.
const no_parentheses = input => input + 2;
no_parentheses(3); // Returns 5
// An arrow function, like other function definitions, can be executed in the same statement as they are created.
// This is useful when writing libraries to avoid filling the global scope, and for closures.
let three = ((a, b) => a + b) (1, 2);
const generate_multiplier_function = a => (b => isNaN(b) || !b ? a : a*=b);
const five_multiples = generate_multiplier_function(5); // The supplied argument "seeds" the expression and is retained by a.
five_multiples(1); // Returns 5
five_multiples(3); // Returns 15
five_multiples(4); // Returns 60
对像类示例:
class Ball {
constructor(radius) {
this.radius = radius;
this.area = Math.PI * ( radius ** 2 );
}
// Classes (and thus objects) can contain functions known as methods
show() {
console.log(this.radius);
}
};
const myBall = new Ball(5); // Creates a new instance of the ball object with radius 5
myBall.radius++; // Object properties can usually be modified from the outside
myBall.show(); // Using the inherited "show" function logs "6"
在JavaScript中,可以直接从函数实例化对象。
对像功能示例:
function Ball(radius) {
const area = Math.PI * ( radius ** 2 );
const obj = { radius, area };
// Objects are mutable, and functions can be added as properties.
obj.show = () => console.log(obj.radius);
return obj;
};
const myBall = Ball(5); // Creates a new ball object with radius 5. No "new" keyword needed.
myBall.radius++; // The instance property can be modified.
myBall.show(); // Using the "show" function logs "6" - the new instance value.
variadic函数演示(arguments
是一个特殊变量):
function sum() {
let x = 0;
for (let i = 0; i < arguments.length; ++i)
x += arguments[i];
return x;
}
sum(1, 2); // Returns 3
sum(1, 2, 3); // Returns 6
// As of ES6, using the rest operator.
function sum(...args) {
return args.reduce((a, b) => a + b);
}
sum(1, 2); // Returns 3
sum(1, 2, 3); // Returns 6
立即弹性的功能表达式通常用于创建关闭。关闭允许在命名空间中收集属性和方法,并将其中一些私有化:
let counter = (function() {
let i = 0; // Private property
return { // Public methods
get: function() {
alert(i);
},
set: function(value) {
i = value;
},
increment: function() {
alert(++i);
}
};
})(); // Module
counter.get(); // Returns 0
counter.set(6);
counter.increment(); // Returns 7
counter.increment(); // Returns 8
生成器对象(以生成器函数的形式)提供了一个函数,可以在维护内部上下文(状态)时称为,退出和重新输入。
function* rawCounter() {
yield 1;
yield 2;
}
function* dynamicCounter() {
let count = 0;
while (true) {
// It is not recommended to utilize while true loops in most cases.
yield ++count;
}
}
// Instances
const counter1 = rawCounter();
const counter2 = dynamicCounter();
// Implementation
counter1.next(); // {value: 1, done: false}
counter1.next(); // {value: 2, done: false}
counter1.next(); // {value: undefined, done: true}
counter2.next(); // {value: 1, done: false}
counter2.next(); // {value: 2, done: false}
counter2.next(); // {value: 3, done: false}
// ...infinitely
JavaScript可以从模块导出和导入:
导出示例:
/* mymodule.js */
// This function remains private, as it is not exported
let sum = (a, b) => {
return a + b;
}
// Export variables
export let name = 'Alice';
export let age = 23;
// Export named functions
export function add(num1, num2) {
return num1 + num2;
}
// Export class
export class Multiplication {
constructor(num1, num2) {
this.num1 = num1;
this.num2 = num2;
}
add() {
return sum(this.num1, this.num2);
}
}
导入示例:
// Import one property
import { add } from './mymodule.js';
console.log(add(1, 2));
//> 3
// Import multiple properties
import { name, age } from './mymodule.js';
console.log(name, age);
//> "Alice", 23
// Import all properties from a module
import * from './module.js'
console.log(name, age);
//> "Alice", 23
console.log(add(1,2));
//> 3
更高级的示例
此示例代码显示各种JavaScript功能。
/* Finds the lowest common multiple (LCM) of two numbers */
function LCMCalculator(x, y) { // constructor function
if (isNaN(x*y)) throw new TypeError("Non-numeric arguments not allowed.");
const checkInt = function(x) { // inner function
if (x % 1 !== 0)
throw new TypeError(x + "is not an integer");
return x;
};
this.a = checkInt(x)
// semicolons ^^^^ are optional, a newline is enough
this.b = checkInt(y);
}
// The prototype of object instances created by a constructor is
// that constructor's "prototype" property.
LCMCalculator.prototype = { // object literal
constructor: LCMCalculator, // when reassigning a prototype, set the constructor property appropriately
gcd: function() { // method that calculates the greatest common divisor
// Euclidean algorithm:
let a = Math.abs(this.a), b = Math.abs(this.b), t;
if (a < b) {
// swap variables
// t = b; b = a; a = t;
[a, b] = [b, a]; // swap using destructuring assignment (ES6)
}
while (b !== 0) {
t = b;
b = a % b;
a = t;
}
// Only need to calculate GCD once, so "redefine" this method.
// (Actually not redefinition—it's defined on the instance itself,
// so that this.gcd refers to this "redefinition" instead of LCMCalculator.prototype.gcd.
// Note that this leads to a wrong result if the LCMCalculator object members "a" and/or "b" are altered afterwards.)
// Also, 'gcd' === "gcd", this['gcd'] === this.gcd
this['gcd'] = function() {
return a;
};
return a;
},
// Object property names can be specified by strings delimited by double (") or single (') quotes.
"lcm": function() {
// Variable names do not collide with object properties, e.g., |lcm| is not |this.lcm|.
// not using |this.a*this.b| to avoid FP precision issues
let lcm = this.a / this.gcd() * this.b;
// Only need to calculate lcm once, so "redefine" this method.
this.lcm = function() {
return lcm;
};
return lcm;
},
// Methods can also be declared using ES6 syntax
toString() {
// Using both ES6 template literals and the (+) operator to concatenate values
return `LCMCalculator: a = ${this.a}, b = ` + this.b;
}
};
// Define generic output function; this implementation only works for Web browsers
function output(x) {
document.body.appendChild(document.createTextNode(x));
document.body.appendChild(document.createElement('br'));
}
// Note: Array's map() and forEach() are defined in JavaScript 1.6.
// They are used here to demonstrate JavaScript's inherent functional nature.
[
[25, 55],
[21, 56],
[22, 58],
[28, 56]
].map(function(pair) { // array literal + mapping function
return new LCMCalculator(pair[0], pair[1]);
}).sort((a, b) => a.lcm() - b.lcm()) // sort with this comparative function; => is a shorthand form of a function, called "arrow function"
.forEach(printResult);
function printResult(obj) {
output(obj + ", gcd = " + obj.gcd() + ", lcm = " + obj.lcm());
}
以下输出应显示在浏览器窗口中。
LCMCalculator: a = 28, b = 56, gcd = 28, lcm = 56
LCMCalculator: a = 21, b = 56, gcd = 7, lcm = 168
LCMCalculator: a = 25, b = 55, gcd = 5, lcm = 275
LCMCalculator: a = 22, b = 58, gcd = 2, lcm = 638
安全
JavaScript和DOM为恶意作者提供了通过Web在客户端计算机上运行脚本的潜力。浏览器作者使用两个限制最大程度地降低了这种风险。首先,脚本在沙盒中运行,在该沙盒中,他们只能执行与Web相关的操作,而不是通用的编程任务,例如创建文件。其次,脚本受到相同原始策略的约束:一个网站的脚本无法访问诸如发送到另一个网站的用户名,密码或cookie之类的信息。大多数与JavaScript相关的安全错误是违反了相同的原始策略或沙盒。
有一般的JavaScript的子集(ADSAFERCRIPT,SECURE ECMASCRIPT(SES))提供了更高级别的安全性,尤其是在第三方创建的代码(例如广告)上。 CLOSURE TOOLKIT是一个安全嵌入和隔离第三方JavaScript和HTML的项目。
内容安全策略是确保仅在网页上执行可信赖的代码的主要预期方法。
跨场漏洞
跨站脚本
常见的与JavaScript相关的安全问题是跨站点脚本(XSS),这是对同基因政策的违反。 XSS漏洞发生在攻击者可以导致目标网站(例如在线银行网站)中包含一个恶意脚本的网页时,就会发生XSS漏洞。然后,此示例中的脚本可以访问受害人特权的银行申请,可能会披露秘密信息或未经受害者授权转移资金。 XSS漏洞的一种解决方案是在显示不信任数据时使用HTML逃脱。
一些浏览器包括针对反射XSS攻击的部分保护,其中攻击者提供了一个包括恶意脚本的URL。但是,即使是这些浏览器的用户也很容易受到其他XSS攻击的攻击,例如将恶意代码存储在数据库中的攻击。仅在服务器端上正确设计Web应用程序才能完全防止XSS。
XSS漏洞也可能是由于浏览器作者的实现错误而发生的。
跨场请求伪造
另一个跨点漏洞是跨站点伪造(CSRF)。在CSRF中,攻击者网站上的代码欺骗了受害者的浏览器来采取用户不打算在目标站点(例如在银行转移资金)。当目标站点仅依靠cookie进行请求身份验证时,源自攻击者网站上代码的请求可以携带启动用户的相同有效登录凭据。通常,CSRF的解决方案是在隐藏的表单字段中以及在cookie中需要一个身份验证值,以验证任何可能具有持久效果的请求。检查HTTP推荐人标头也可以提供帮助。
“ JavaScript劫持”是一种CSRF攻击,其中<script>
攻击者网站上的标记利用受害者网站上的页面,该页面返回私人信息,例如JSON或JavaScript。可能的解决方案包括:
放错了对客户的信任
客户服务器应用程序的开发人员必须认识到,不受信任的客户可能受到攻击者的控制。应用程序作者不能假定其JavaScript代码将按预期运行(或根本)运行,因为代码中嵌入的任何秘密都可以由确定的对手提取。一些含义是:
- 网站作者无法完美地隐藏其JavaScript的运行方式,因为必须将原始源代码发送给客户端。该代码可以混淆,但可以对混淆进行反向工程。
- JavaScript表单验证仅为用户而不是安全性提供便利。如果网站验证用户是否同意其服务条款,或者过滤器中只包含数字的字段中的无效字符,则必须在服务器上执行此操作,而不仅仅是客户端。
- 可以选择性地禁用脚本,因此不能依靠JavaScript来防止操作,例如右键单击图像以保存它。
- 嵌入敏感信息(例如JavaScript中的密码)被认为是非常糟糕的做法,因为它可以由攻击者提取。
对开发人员的信任放错了
NPM和Bower等软件包管理系统在JavaScript开发人员中很受欢迎。这样的系统使开发人员可以轻松管理其程序对其他开发人员计划库的依赖性。开发人员相信,库的维护者将确保他们的安全和最新,但并非总是如此。由于这种盲目的信任,出现了脆弱性。依赖的库可以具有新的版本,这些版本会导致错误或漏洞出现在所有依赖库的程序中。相反,图书馆可以在野外散发出已知漏洞的限制。在研究133,000个网站样本的一项研究中,研究人员发现37%的网站包括一个至少一个已知漏洞的图书馆。 “每个网站上使用的最古老的图书馆版本与该图书馆的最新版本之间的中位数为1,177天,几年前仍停止使用活跃使用的一些库的开发。”另一种可能性是库的维护者可以完全删除库。这发生在2016年3月,当时AzerKoçulu从NPM删除了他的存储库。这导致了数万个程序和网站,具体取决于他的图书馆。
浏览器和插件编码错误
JavaScript为各种浏览器功能提供了一个接口,其中一些可能存在诸如缓冲区溢出之类的缺陷。这些缺陷可以允许攻击者编写可以在用户系统上运行任何代码的脚本。此代码不仅限于另一个JavaScript应用程序。例如,缓冲区超支漏洞利用可以使攻击者能够访问具有超级用户特权的操作系统API 。
这些缺陷影响了包括Firefox,Internet Explorer和Safari在内的主要浏览器。
插件,例如视频播放器, Adobe Flash以及Microsoft Internet Explorer默认启用的广泛的ActiveX控件,也可能通过JavaScript利用缺陷(过去已经利用了此类缺陷)。
在Windows Vista中,微软试图通过运行有限特权的Internet Explorer进程来容纳错误的风险,例如缓冲区溢出。 Google Chrome类似地将其页面渲染器限制在其自己的“沙盒”中。
沙盒实现错误
Web浏览器能够在沙箱外运行JavaScript,具有例如创建或删除文件所需的特权。此类特权无意从网络中授予代码。
错误地从网络上授予JavaScript的特权在Internet Explorer和Firefox的漏洞中发挥了作用。在Windows XP Service Pack 2中,Microsoft在Internet Explorer中降低了JScript的特权。
Microsoft Windows允许在计算机的硬盘驱动器上以通用,非框架程序启动JavaScript源文件(请参阅: Windows脚本主机)。这使JavaScript(如VBScript )成为特洛伊木马的理论上可行的向量,尽管Javascript Trojan马在实践中并不常见。
硬件漏洞
2015年,安全研究人员在一份论文中描述了基于JavaScript的概念证明实施Rowhammer攻击。
2017年,证明了通过浏览器的基于JavaScript的攻击可以绕过ASLR 。它称为“Aslr⊕Cache”或ANC。
在2018年,宣布对英特尔和其他处理器投机执行的Spectre攻击的论文包括JavaScript实施。
开发工具
重要的工具随着语言而发展。
- 每个主要的Web浏览器都具有内置的Web开发工具,包括JavaScript调试器。
- 静态程序分析工具,例如ESLINT和JSLINT ,扫描JavaScript代码,以符合一组标准和准则。
- 一些浏览器具有内置的参考器。还创建了独立的分析库,例如Benchmark.js和JSbench。
- 许多文本编辑器都有语法突出显示对JavaScript代码的支持。
静态程序分析
eslint
JSlint
相关技术
爪哇
一个普遍的误解是JavaScript与Java相同。两者确实具有类似C的语法(C语言是他们最直接的共同祖先语言)。它们通常也会是沙盒(在浏览器中使用时),而JavaScript则是使用Java的语法和标准库设计的。特别是,所有Java关键字都保留在原始JavaScript中,JavaScript的标准库遵循Java的命名约定,JavaScript的标准库Math
和Date
对象基于Java 1.0的类。
Java和JavaScript均首次出现在1995年,但Java是由Sun Microsystems的James Gosling和Netscape Communications的Brendan Eich开发的。
两种语言之间的差异比它们的相似性更为突出。 Java具有静态打字,而JavaScript的键入是动态的。 Java是从编译字节码加载的,而JavaScript被加载为人类可读源代码。 Java的对像是基于类的对象,而JavaScript的对像是基于原型的。最后,Java直到Java 8才支持功能编程,而JavaScript从一开始就受到了计划的影响。
JSON
JSON (JavaScript对象表示法,发音为;也)是一种开放的标准文件格式和数据互换格式,该格式使用人类可读文本来存储和传输由属性 - 值对和数组(或其他可序列化值)组成的数据对象。这是一种常见的数据格式,在电子数据互换中具有多种用途,包括带服务器的Web应用程序的数据格式。
JSON是一种独立于语言的数据格式。它源自JavaScript,但许多现代编程语言都包含用于生成和解析JSON-Format数据的代码。 JSON文件名使用扩展名.json
.
打字稿
Typescript(TS)是JavaScript的严格类型变体。 TS通过将类型注释引入变量和函数,并引入类型语言来描述JS中的类型。否则,TS具有与JS相同的功能,以便轻松地将其转移到JS以进行运行客户端,并与其他JS代码相互键入。
WebAssembly
自2017年以来,Web浏览器已支持WebAssembly ,这是一种二进制格式,它使JavaScript引擎能够执行与本机速度接近的网页脚本的关键性性能部分。 WebAssembly代码与常规JavaScript代码在同一沙箱中运行。
ASM.JS是JavaScript的子集,它是WebAssembly的先驱。
转侧
JavaScript是Web的主要客户端语言,许多网站脚本繁重。因此,已经创建了转换器来转换以其他语言编写的代码,这可以帮助开发过程。
阿贾克斯
Ajax (也是Ajax ; “异步JavaScript和XML ”或“异步JavaScript传输(X-FER)”的缩写是一组Web开发技术,它在客户端端使用各种Web技术来创建异步Web应用程序。使用AJAX,Web应用程序可以从服务器异步(在后台)发送和检索数据,而不会干扰现有页面的显示和行为。通过将数据互换层与演示层解耦,AJAX允许网页,并通过扩展为Web应用程序动态更改内容,而无需重新加载整个页面。实际上,现代实现通常使用JSON而不是XML。
Ajax不是技术,而是编程概念。 HTML和CSS可以组合使用以标记和样式信息。可以通过JavaScript修改网页以动态显示,并允许用户与新信息进行交互。内置的XMLHTTPREQUEST对像用于在网页上执行Ajax,从而允许网站加载内容在屏幕上而无需刷新页面。 Ajax不是一种新技术,也不是一种新语言。相反,它是以新方式使用的现有技术。