Skip to content

JavaScript

JavaScript(JS)是一种具有函数优先特性的轻量级、解释型或者说即时编译型的编程语言。虽然作为 Web 页面中的脚本语言被人所熟知,但是它也被用到了很多非浏览器环境中,例如 Node.js、Apache CouchDB、Adobe Acrobat 等。进一步说,JavaScript 是一种基于原型、多范式、单线程的动态语言,并且支持面向对象、命令式和声明式(如函数式编程)风格。

JavaScript 内置了一些对象的标准库,比如数组(Array),日期(Date),数学(Math)和一套核心语句,包括运算符、流程控制符以及声明方式等。JavaScript 的核心部分可以通过添加对象来扩展语言以适应不同用途;例如:

  • 客户端的 JavaScript 通过提供对象,控制浏览器及其文档对象模型(DOM),来扩展语言核心。例如:客户端的拓展代码允许应用程序将元素放在某个 HTML 表单中,并且支持响应用户事件,比如鼠标点击、表单提交和页面导航。
  • 服务端的 JavaScript 则通过提供有关在服务器上运行 JavaScript 的对象来可扩展语言核心。例如:服务端版本直接支持应用和数据库通信,提供应用不同调用间的信息连续性,或者在服务器上执行文件操作。

这意味着,在浏览器中,JavaScript 可以改变网页(DOM)的外观与样式。同样地,在服务器上,Node.js 中的 JavaScript 可以对浏览器上编写的代码发出的客户端请求做出响应。

基本概念

程序

JavaScript 程序是 JavaScript 语言中用户编写的代码。

  • JavaScript 程序是单个语句的集合。
  • JavaScript 程序是单个变量的集合。

语句

JavaScript 程序是由一条条的语句组成的,每条语句就是一个命令。

  • JavaScript 语句是 JavaScript 解释器对代码的文本。
  • JavaScript 语句可以是单个语句也可以是多行语句。

注释

JavaScript 注释用于解释 JavaScript 代码,提高代码的可读性。

  • 单行注释以两个正斜杠开头:// 这是单行注释。
  • 多行注释以/_ 开头,以_/结束。
javascript
// 单行注释

/* 这是一个更长的,
   多行注释
*/

/* 然而,你不能,/* 嵌套注释 */ 语法错误 */

数据类型

最新的 ECMAScript 标准定义了 8 种数据类型:

  • 七种基本数据类型:
    • 布尔值(Boolean),有 2 个值分别是:true 和 false。
    • null,一个表明 null 值的特殊关键字。JavaScript 是大小写敏感的,因此 null 与 Null、NULL 或变体完全不同。
    • undefined,和 null 一样是一个特殊的关键字,undefined 表示变量未赋值时的属性。
    • 数字(Number),整数或浮点数,例如: 42 或者 3.14159。
    • 任意精度的整数(BigInt),可以安全地存储和操作大整数,甚至可以超过数字的安全整数限制。
    • 字符串(String),字符串是一串表示文本值的字符序列,例如:"Howdy"。
    • 代表(Symbol,在 ECMAScript 6 中新添加的类型)。一种实例是唯一且不可改变的数据类型。
  • 以及对象(Object)。

虽然这些数据类型相对来说比较少,但是通过他们你可以在程序中开发有用的功能。对象和函数是这门语言的另外两个基本元素。你可以把对象当作存放值的一个命名容器,然后将函数当作你的程序能够执行的步骤。

变量

在应用程序中,使用变量来作为值的符号名。变量的名字又叫做标识符,其需要遵守一定的规则。

一个 JavaScript 标识符必须以字母、下划线(_)或者美元符号($)开头;后续的字符也可以是数字(0-9)。因为 JavaScript 语言是区分大小写的,所以字母可以是从“A”到“Z”的大写字母和从“a”到“z”的小写字母。

合法的标识符示例:Number_hits、temp99、$credit 和 _name。

不合法的标识符示例:99balloons、-name 和 100steaks。

声明变量

你可以用以下三种方式声明变量:

  • 使用关键词 var 。例如 var x = 42。这个语法可以用来声明局部变量和全局变量。
  • 直接赋值。例如 x = 42。在函数外使用这种形式赋值,会产生一个全局变量。在严格模式下会产生错误。因此你不应该使用这种方式来声明变量。
  • 使用关键词 let、const。例如 let y = 13。这个语法可以用来声明块作用域的局部变量。

声明函数

一个函数定义(也称为函数声明,或函数语句)由 function 关键字,并跟随以下部分组成:

  • 函数名称。
  • 函数参数列表,包围在括号中并由逗号分隔。
  • 定义函数的 JavaScript 语句,用大括号括起来,{ /_ … _/ }。
javascript
function myFunction(a, b) {
  return a * b;
}

对象

JavaScript 的设计是一个简单的基于对象的范式。一个对象就是一系列属性的集合,一个属性包含一个名和一个值。一个属性的值可以是函数,这种情况下属性也被称为方法。除了浏览器里面预定义的那些对象之外,你也可以定义你自己的对象。

运算符

JavaScript 运算符用于执行数据运算,例如:加、减、乘、除等。

  • 算术运算符:+ - * / % ++ --
  • 比较运算符:> < >= <= == === != !==
  • 逻辑运算符:&& || !
  • 位运算符:& | ^ ~ << >> >>>
  • 赋值运算符:= += -= *= /= %= <<= >>= >>>=
  • 字符串运算符:+
  • 条件运算符:? :
  • 逗号运算符:,

字面量

在 JavaScript 中,你可以使用各种字面量。这些字面量是脚本中按字面意思给出的固定的值,而不是变量。JS 有多种类型的字面量:

  • 数组字面量:数组字面值是一个封闭在方括号对 ([]) 中的包含有零个或多个表达式的列表,其中每个表达式代表数组的一个元素。当你使用数组字面值创建一个数组时,该数组将会以指定的值作为其元素进行初始化,而其长度被设定为元素的个数。若在顶层(全局)脚本里用字面值创建数组,JavaScript 语言将会在每次对包含该数组字面值的表达式求值时解释该数组。另一方面,在函数中使用的数组,将在每次调用函数时都会被创建一次。
  • 布尔字面量:布尔类型有两种字面量:true 和 false。不要混淆作为布尔对象的真和假与布尔类型的原始值 true 和 false。布尔对象是原始布尔数据类型的一个包装器。
  • 数字字面量:JavaScript 数字字面量包括多种基数的整数字面量和以 10 为基数的浮点数字面量。值得一提的是,语言标准要求数字字面量必须是无符号的。但是像-123.4 这样的代码片段还是没有问题的,会被解释为一元操作符-应用于数字字面量 123.4
  • 对象字面量:对象字面值是封闭在花括号对({})中的一个对象的零个或多个“属性名—值”对的(元素)列表。对象属性名字可以是任意字符串,包括空串。如果对象属性名字不是合法的 javascript 标识符,它必须用引号包裹。属性的名字不合法,那么便不能用点(.)访问属性值。
  • RegExp 字面量:一个正则表达式是字符被斜线(译注:正斜杠“/”)围成的表达式。
  • 字符串字面量:字符串字面量是由双引号(")对或单引号(')括起来的零个或多个字符。字符串被限定在同种引号之间;也即,必须是成对单引号或成对双引号。

流程控制

JavaScript 流程控制语句用于控制程序的执行路径。

  • if 语句:根据条件是否为真来决定是否执行代码块。
  • switch 语句:根据多个分支选择一个执行。
  • for 循环:重复执行一段代码直到条件不满足为止。
  • while 循环:只要条件为真就执行一段代码。
  • do...while 循环:先执行一次,然后判断条件是否为真。
  • break 语句:跳出循环。
  • continue 语句:跳出当前循环,继续执行下一次循环。

错误处理

JavaScript 错误处理机制包括:

  • try...catch 语句:用于捕获异常,并执行错误处理代码。
  • throw 语句:抛出异常,允许你创建自定义的错误。
  • finally 语句:无论是否发生异常,finally 语句都会执行。

函数

函数是 JavaScript 中的基本组件之一。JavaScript 中的函数类似于过程——一组执行任务或计算值的语句。但要成为函数,这个过程应该接受输入并返回与输入存在某些明显关系的输出。要使用一个函数,你必须将其定义在你希望调用它的作用域内。

函数表达式

函数表达式(function expression)非常类似于函数声明(function statement)(详情查看函数声明),并且两者拥有几乎相同的语法。函数表达式与函数声明的最主要区别是函数名称(function name),在函数表达式中可省略它,从而创建匿名函数(anonymous functions)。一个函数表达式可以被用作一个 IIFE(Immediately Invoked Function Expression,即时调用的函数表达式),它一旦定义就运行。

javascript
let function_expression = function [name]([param1[, param2[, ..., paramN]]]) {
   statements
};

调用函数

定义的函数并不会自动执行它。定义了函数仅仅是赋予函数以名称并明确函数被调用时该做些什么。

调用函数才会以给定的参数真正执行这些动作。

函数一定要处于调用它们的作用域中,但是函数的声明可以被提升(出现在调用语句之后)。函数声明的范围是声明它的函数(或者,如果它是在顶层声明的,则为整个程序)之内。

参数

参数本质上是按值传递给函数的——因此,即使函数体的代码为传递给函数的参数赋了新值,这个改变也不会反映到全局或调用该函数的代码中。

函数作用域

函数作用域是指函数内声明的变量和函数的可见性范围。JavaScript 中的函数作用域是词法作用域,即函数的作用域由函数定义时确定,而不是执行时确定。

基本特性

闭包

闭包是一个函数及其相关引用环境组合而成的实体。闭包使得函数可以访问到其外部作用域的变量。

原型

在 JavaScript 中,每个对象都有一个原型(prototype)对象。对象原型对象和构造函数(constructor)属性指向另一个对象。该对象称为原型对象。

原型链

每个对象(object)都有一个私有属性指向另一个名为原型(prototype)的对象。原型对象也有一个自己的原型,层层向上直到一个对象的原型为 null。根据定义,null 没有原型,并作为这个原型链(prototype chain)中的最后一个环节。可以改变原型链中的任何成员,甚至可以在运行时换出原型,因此 JavaScript 中不存在静态分派的概念。

JavaScript 对象是动态的属性(指其自有属性)“包”。JavaScript 对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。

继承

JavaScript 的继承是一种对象基于其他对象的属性和方法来构建新对象的机制。继承主要通过原型链(prototype chain)来实现。

属性的可枚举性和所有权

JavaScript 对象的属性分为两种:自有属性和继承属性。自有属性是直接在对象上定义的属性,而继承属性则是通过原型链从其他对象继承而来的属性。

可枚举属性是指那些内部“可枚举”标志设置为 true 的属性,对于通过直接的赋值和属性初始化的属性,该标识值默认为即为 true,对于通过 Object.defineProperty 等定义的属性,该标识值默认为 false。可枚举的属性可以通过 for...in 循环进行遍历(除非该属性名是一个 Symbol)。属性的所有权是通过判断该属性是否直接属于某个对象决定的,而不是通过原型链继承的。一个对象的所有的属性可以一次性的获取到。

属性特性

JavaScript 属性有四个特性:可配置性(Configurability)、可枚举性和可写性(Enumerable and Writable)和值(Value)。

JavaScript 类型化数组

JavaScript 类型化数组是一种类似数组的对象,并提供了一种用于在内存缓冲中访问原始二进制数据的机制。类型化数组由以下几种形式组成:

  • Int8Array
  • Uint8Array
  • Uint8ClampedArray
  • Int16Array
  • Uint16Array
  • Int32Array
  • Uint32Array
  • Float32Array
  • Float64Array

迭代器

在 JavaScript 中,迭代器是一个对象,它定义一个序列,并在终止时可能附带一个返回值。

更具体地说,迭代器是通过使用 next() 方法实现了迭代器协议的任何一个对象,该方法返回具有两个属性的对象:

value 迭代序列的下一个值。

done 如果已经迭代到序列中的最后一个值,则它为 true。如果 value 和 done 一起出现,则它就是迭代器的返回值。

一旦创建,迭代器对象可以通过重复调用 next() 显式地迭代。迭代一个迭代器被称为消耗了这个迭代器,因为它通常只能执行一次:在产生终值后,对 next() 的额外调用应该继续返回 {done:true}。

异步编程

JavaScript 是一种单线程的编程语言,但它可以异步执行任务。异步编程是 JavaScript 的一个重要特性,它允许在等待外部事件时继续执行代码。

事件循环

JavaScript 的事件循环机制使得异步操作得以实现。事件循环是一个程序执行模型,它将任务分为不同的阶段,并按照顺序依次执行这些任务。

优缺点

优点

  1. 跨平台性:JavaScript 可以在多种平台和环境中运行,如浏览器和 Node.js。
  2. 灵活性:作为一种多范式语言,JavaScript 支持多种编程风格。
  3. 庞大的生态系统:拥有大量的库和框架,以及丰富的开发工具。
  4. 事件驱动和非阻塞 I/O:适合开发高并发的网络应用。
  5. 统一的开发语言:前端和后端(Node.js)可以使用同一种语言开发。

缺点

  1. 单线程限制:在浏览器中,JavaScript 的单线程模型可能导致长时间运行的操作阻塞用户界面。
  2. 类型系统:动态类型系统可能导致运行时错误难以发现。
  3. 性能问题:对于计算密集型任务,JavaScript 的性能可能不如编译型语言。
  4. 安全性问题:在 Web 页面中执行 JavaScript 代码可能带来安全风险,如跨站脚本攻击(XSS)。
  5. 内存泄漏:不当的内存管理可能导致内存泄漏。

总结

JavaScript 是一种广泛使用的编程语言,它具有跨平台、灵活性和庞大的生态系统等优点。但是,也存在单线程限制、类型系统问题、性能问题和安全性等问题。在选择使用 JavaScript 进行开发时,需要权衡其优缺点并做出明智的选择。

参考