Skip to content

Javascript基础

快速开始

  1. 新建文件夹js_demo

  2. 在js_demo文件夹下新建文件test.html、test.js

  3. 向test.html中添加代码:

    html
    <head>
        <script type="text/javascript" src="test.js"></script>
    </head>
  4. 向test.js中添加代码:

    javascript
    alert("Hi, JavaScript!");
  5. 双击运行test.html, 显示效果:

JavaScript 程序不能独立运行,只能在宿主环境中执行。一般情况下可以把JavaScript代码放在网页中,借助浏览器环境来运行。

语法和数据类型

语法基础

  • JavaScript 是区分大小写的
  • 在Javascript中,指令被称为语句,并使用分号;分隔。
  • Javascript是按块执行的,但是不同块都属于同一个作用域(全局作用域),后面块中的代码可以访问前面块中的变量。

注释

js
// 单行注释

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

/* 你可以通过转义正斜杠 \/ */

变量

JavaScript 有三种变量声明方式。

  • var:声明一个变量,可选择将其初始化为一个值。

  • let:声明一个块级作用域的局部变量,可选择将其初始化为一个值。

  • const:声明一个块级作用域的只读命名常量。

  • 变量

    JavaScript 变量的名字又叫做标识符,标识符通常以字母、下划线(_)或者美元符号($)开头;后续的字符也可以是数字(0-9)。因为 JavaScript 是区分大小写的,所以字母包含从 A 到 Z 的大写字母和从 a 到 z 的小写字母。

    合法的标识符示例:Number_hitstemp99$credit_name

  • 声明变量

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

    • 使用关键字 var。例如 var x = 42。这个语法可以用来声明局部变量全局变量,具体取决于执行上下文。
    • 使用关键字 constlet 。例如 let y = 13。这个语法可以用来声明块级作用域的局部变量
    js
    var a;
    var a, b, c;
    var b = 1;

    JavaScript引擎的解析方式:先解析代码,获取所有被声明的变量,提升到代码的头部,然后再一行一行地运行,这成为变量提升。

  • 变量作用域

    一个变量可能属于下列作用域之一:

    • 全局作用域:在脚本模式中运行的所有代码的默认作用域。

    • 模块作用域:在模块模式中运行的代码的作用域。

    • 函数作用域:由函数创建的作用域。

    • 块级作用域:用一对花括号创建的作用域(块)。

    当你在函数的外部声明变量时,该变量被称作全局变量,因为当前文档中任何其他代码都能使用它。当你在函数内声明变量时,该变量被称作局部变量,因为它仅在那个函数内可用。

  • 变量提升

    var 声明的变量会被提升,意味着你可以在该变量所在的作用域的任意地方引用该变量,即使还没有到达变量声明的地方。你可以看见 var 声明好像被提升到该变量的函数或全局作用域的顶部。然而,如果你在声明变量之前访问该变量,其值总是 undefined,因为只有该变量的声明和默认初始化(为 undefined)被提升,而不是它的赋值。

  • 全局变量

    • 使用var在任何函数、模块或代码块({})之外声明的变量会成为全局变量。

    • 使用 var 声明的全局变量会成为全局对象(如 window)的不可配置属性

    js
    // 在脚本的最顶层声明
    var globalVar = "我是一个全局变量 (var)";
    
    function myFunction() {
        console.log(globalVar); // 可以访问
    }
    
    console.log(window.globalVar); // "我是一个全局变量 (var)"
  • 常量

    • 可以用 const 关键字创建一个只读命名常量。常量标识符的语法和任何变量标识符的语法相同

    • 常量必须为其初始化一个值,并且不能后续修改值。

    • 常量的作用域规则和 let 块级作用域变量的一致。

数据结构与类型

Javascript定义了6种基本的数据类型:

数据类型含义说明
null空值表示非对象
undefined未定义的值已声明,未赋值的变量的默认值
Number数字数学运算的值
String字符串表示字符串信息
Boolean布尔值true 、false
BigInt整数任意精度的整数
object对象包括狭义的对象、数组、函数

使用typeof运算符可以检测变量的类型:

js
console.log(typeof 1);      // number
console.log(typeof "abc");  // string
console.log(typeof true);   // boolean
console.log(typeof {});     // object
console.log(typeof []);     // object
console.log(typeof function() {});  // function
console.log(typeof undefined);  // undefined

比较运算符

当我们对Number做比较时,可以通过比较运算符得到一个布尔值:

js
2 > 5; // false
5 >= 2; // true
7 == 7; // true

实际上,JavaScript允许对任意数据类型做比较:

js
false == 0; // true
false === 0; // false

要特别注意相等运算符==。JavaScript在设计时,有两种比较运算符:

第一种是==比较,它会自动转换数据类型再比较,很多时候,会得到非常诡异的结果;

第二种是===比较,它不会自动转换数据类型,如果数据类型不一致,返回false,如果一致,再比较。

由于JavaScript这个设计缺陷,不要使用==比较,始终坚持使用===比较。

另一个例外是NaN这个特殊的Number与所有其他值都不相等,包括它自己:

js
NaN === NaN; // false

唯一能判断NaN的方法是通过isNaN()函数:

js
isNaN(NaN); // true

数据类型转换

JavaScript是一门动态类型语言,能够根据运算环境自动转换值的类型:

值(value)字符串操作环境数字运算环境逻辑运算环境对象操作环境
undefined"undefined"NaNfalseError
null"null"0falseError
非空字符串-字符串对应的数字值;
NaN
trueString
空字符串-0falseString
0"0"不转换falseNumber
NaN"NaN"不转换falseNumber
Infinity"Infinity"不转换trueNumber
-Infinity"-Infinity"不转换trueNumber
Number.MAX_VALUE"1.7976931348
623157e+308"
不转换trueNumber
NUMBER.MIN_VALUE"5e-324"不转换trueNumber
其他所有数字"数字的字符串值"不转换trueNumber
true"true"1不转换Boolean
false"false"0不转换Boolean
对象toString()valueOf()或toString或NaNtrue不转换

在某些情况下,需要我们手动转换类型:

转换为字符串

  • 使用 + 运算符,当值与空字符串相加时,会自动转为字符串

    js
    var n = 123;
    n = n + "";
    console.log(typeof n); // string
  • 使用 toString()

    js
    var a = 1e-4; // 1*10^-4
    
    console.log(a.toString());  // "0.0001"

转换为小数格式字符串

  • num.toFixed(num):指定小数点后num位,输出字符串

    js
    var a = 10;
    console.log(a.toFixed(2))   // "10.00"
  • num.toExponential(num):把数字转换为科学计数法,并指定小数点后保留num位(四舍五入),输出字符串

    js
    var a = 123456789;
    console.log(a.toExponential(2)) // "1.23e+8"
    console.log(a.toExponential(4)) // "1.2346e+8"
  • num.toPrecision(num): 把数字转换为科学计数法,并指定有效数字为num位(四舍五入),输出字符串

    js
    var a = 123456789;
    console.log(a.toPrecision(2)) // "1.2e+8"
    console.log(a.toPrecision(4)) // "1.235e+8"

转换为数字

常用值转换为数字说明如下:

  • true -> 1
  • false -> 0
  • "" -> 0
  • Undefined -> NaN
  • null -> 0
  • NaN -> NaN
  • Infinity -> Infinity

常用方法如下:

  • parseInt():转换为整数

  • parseFloat():转换为浮点数

js
console.log(parseInt("123abc"));    // 123
console.log(parseInt("1.73"));      // 1
console.log(parseInt(".123"));      // NaN

// 如果是以0开头的字符串,则parseInt()把它视为八进制数字,转换为十进制返回
var d = "010"
console.log(parseInt(d))    // 返回十进制 8

// 如果是以0x开头的字符串,则parseInt()把它视为十六进制数字,转换为十进制返回
var e = "0x10"
console.log(parseInt(e))    // 返回十进制 16

转换为布尔值

  • 使用两个感叹号!!可以将值转换为布尔类型

    js
    var a = 1;
    console.log(!!a);    //true
  • 使用一个感叹号可以将值转换为布尔类型并取反

    js
    var a = 1;
    console.log(!a);    //false
  • 使用Boolean()函数可以转换为布尔类型

    js
    var a = 1;
    console.log(Boolean(a)) //true

转换为对象

使用 new 命令调用StringNumberBoolean类型函数,可以把字符串、数字和布尔类型包装为对应类型的对象

js
var n = "123"
console.log(typeof new String(n))   // object
console.log(typeof new Number(n))   // object
console.log(typeof new Boolean(n))   // object

强制类型转换

  • Boolean(value):把参数转换为布尔类型

  • Number(value):把参数转换为数字

  • String(value):把参数值转换为字符串

流程控制与错误处理

块语句

块语句是Javascript最基本的语句。块由一对花括号界定:

js
{
  statement1;
  statement2;
  // …
  statementN;
}

备注: 在块中用 var 声明的变量不是块级作用域的,而是函数作用域或全局作用域的。

条件语句

if语句

  • if...else

    js
    if (condition) {
      statement1;
    } else {
      statement2;
    }
  • if...else if...else

    js
    if (condition1) {
      statement1;
    } else if (condition2) {
      statement2;
    } else if (conditionN) {
      statementN;
    } else {
      statementLast;
    }
  • if判断中的真值与假值

    下面这些值求值为 false(也叫做假值):

    • false
    • undefined
    • null
    • 0
    • NaN
    • 空字符串("")

    除此之外所有其他的值(包括所有的对象)在被传递给条件语句时会求值为 true

    注意:空数组[]、字符串"false"if判断中为true

switch

js
switch (expression) {
  case label1:
    statements1;
    break;
  case label2:
    statements2;
    break;
  // …
  default:
    statementsDefault;
}

异常处理

JavaScript 异常类型ECMAScript 异常DOMException

throw 语句

throw 语句的作用是主动抛出一个异常。当程序执行到 throw 时,当前函数的执行会立即停止,控制权将交给调用栈中最近的异常处理程序(catch 块)。

你可以抛出任何类型的值:字符串、数字、布尔值,但最佳实践是抛出一个 Error 对象或其子类的实例,因为它们自带堆栈跟踪等有用的调试信息。

js
// 1. 抛出 Error 对象(推荐)
throw new Error('发生了一个通用错误');

// 2. 抛出更具体的错误类型
throw new SyntaxError('语法错误');
throw new TypeError('类型错误');
throw new ReferenceError('引用错误');

// 3. 甚至可以抛出其他类型(不推荐)
throw '这是一个字符串错误'; // 不推荐
throw 404; // 不推荐
throw { message: '错误详情', code: 500 }; // 有时用于自定义错误对象

你可以创建自己的错误类,继承自 Error,以包含更多上下文信息。

js
class ValidationError extends Error {
    constructor(message, field) {
        super(message);
        this.name = 'ValidationError';
        this.field = field; // 自定义属性,记录是哪个字段出错
    }
}

// 使用自定义错误
throw new ValidationError('邮箱格式不正确', 'email');

try...catch...finally

js
try {
    // 可能会出错的代码
    riskyOperation();
    console.log('这行在出错后不会执行');
} catch (error) {
    // 如果 try 块中抛出异常,执行这里的代码
    console.error('捕获到错误:', error.message);
    // 可以在这里进行错误上报、给用户友好提示等
} finally {
    // 无论是否出错,最终都会执行
    console.log('清理工作完成');
}
// 错误被捕获后,程序会继续执行后面的代码,而不会崩溃
console.log('程序继续正常运行');

循环与迭代

JavaScript 中提供了这些循环语句:

  • for 语句
  • while 语句
  • do...while 语句
  • for...in 语句
  • for...of 语句
  • label 语句
  • break 语句
  • continue 语句

for

js
for ([初始表达式]; [条件表达式]; [增量表达式]) {
  // 要执行的代码块
}

while

js
while (条件表达式) {
  // 要执行的代码块
}

do...while

与 while 类似,但保证代码块至少执行一次,因为条件判断在循环之后。

js
do {
  // 要执行的代码块
} while (条件表达式);

for...in

用于遍历对象的可枚举属性(包括原型链上的)。主要用于对象,不推荐用于数组。

js

for (变量 in 对象) {
  // 使用变量的代码
}

示例:

js
const person = {
    name: 'Alice',
    age: 30,
    city: 'New York'
};

for (const key in person) {
    console.log(`${key}: ${person[key]}`);
    // 输出: name: Alice, age: 30, city: New York
}

for...of

用于遍历可迭代对象(Array, String, Map, Set, arguments等)的值。

js
for (变量 of 可迭代对象) {
  // 使用变量的代码
}

示例:

js
const colors = ['red', 'green', 'blue'];
for (const color of colors) {
    console.log(color); // 输出: 'red', 'green', 'blue'
}

循环中断

break:

  • 直接结束本次循环
  • 在嵌套循环中, break 只在它所在的循环生效

continue:

  • 中断本次循环, 进入下一次循环
  • 在嵌套循环中, continue 也只在它所在的循环生效

函数

函数定义(函数声明)

  • 传统方式

    传统方式定义函数,具有函数提升特性,即可以在定义前调用

    js
    // 函数声明
    function greet(name) {
        return `Hello, ${name}!`;
    }
    
    console.log(greet('Alice')); // "Hello, Alice!"
    
    // 函数提升:可以在定义前调用
    sayHello(); // "Hello!" (正常工作)
    
    function sayHello() {
        console.log("Hello!");
    }
  • 函数表达式

    将函数赋值给变量,此时没有函数提升特性

    js
    // 函数表达式
    let greet = function(name) {
        return `Hello, ${name}!`;
    };
    
    console.log(greet('Bob')); // "Hello, Bob!"
    
    // 没有提升,会报错
    tryHello(); // Error: tryHello is not a function
    
    let tryHello = function() {
        console.log("Trying hello...");
    };
  • 箭头函数

    更简洁的语法,没有自己的thisargumentssupernew.target

    js
    // 基本语法
    const add = (a, b) => {
        return a + b;
    };
    
    // 单执行语句可以省略花括号
    const multiply = (a, b) => a * b;
    
    // 单个参数可省略括号
    const square = x => x * x;
    
    // 无参数需要空括号
    const sayHi = () => console.log("Hi!");
    
    console.log(add(2, 3)); // 5
    console.log(multiply(2, 3)); // 6
    console.log(square(4)); // 16
  • 立即调用表达式IIFE

    定义后立即执行的函数,用于创建独立作用域。

    现代模块化中较少使用,但在旧代码中很常见

    js
    (function() {
        let privateVar = '我是私有的';
        console.log('IIFE 立即执行');
    })();
    
    // 带参数的 IIFE
    (function(name) {
        console.log(`Hello, ${name}!`);
    })('World');
  • 回调函数

函数参数

  • 默认参数

    js
    function multiply(a, b = 1) {
      return a * b;
    }
  • 剩余参数

    将多个参数收集到一个数组中

    js
    function showTags(title, ...tags) {
        console.log(`标题: ${title}`);
        console.log(`标签: ${tags.join(', ')}`);
    }
    showTags('JS教程', '编程', '前端', 'JavaScript');

返回值

如果没有 return 或 return 后无值,函数返回 undefined

作用域和闭包

作用域

  • 在函数内定义的变量不能在函数之外的任何地方访问

  • 定义在全局域中的函数可以访问所有全局变量

  • 内层函数可以访问外层函数能访问的所有变量,外层函数无法访问内层函数变量

js
const globalVar = '全局变量';

function outer() {
    const outerVar = '外部变量';
    
    function inner() {
        const innerVar = '内部变量';
        console.log(innerVar); // 可以访问
        console.log(outerVar); // 可以访问
        console.log(globalVar); // 可以访问
    }
    
    inner();
    // console.log(innerVar); // 错误:无法访问内部变量
}

outer();

闭包

Released under the MIT License.