JavaScript高级程序设计——读书笔记

第1章 JavaScript简介

想要全面理解和掌握JavaScript,关键在于弄清楚它的本质、历史、局限性。

在这里插入图片描述
ECMAScript
ECMAScript(简称ES)是一种用于编写Web浏览器脚本语言的标准,它定义了JavaScript语言的核心规范。它由Ecma International 标准化组织负责管理和更新。JavaScript是ECMAScript标准的一种实现,其他还有JScript和ActionScript等。ECMAScript的目标是提供一种可以跨平台、可靠、高效、强大的脚本语言。目前最新的版本是ECMAScript 2022。

ECMA-262
ECMA-262是一种标准,定义了ECMAScript语言的规范,也称为JavaScript语言的规范。它是由欧洲计算机制造商协会(European Computer Manufacturers Association,ECMA)委员会制定的,旨在为跨平台、跨供应商的脚本语言提供标准化。ECMA-262规范包括语法、类型、语句、运算符、对象等方面的规定,以及处理器执行ECMAScript代码的标准规则。所有的现代Web浏览器都支持ECMAScript规范,并且通过不断地更新,使其成为当今最流行的编程语言之一。

Web浏览器只是ECMAScript实现可能的宿主环境之一

DOM

在 Web 开发中,DOM(文档对象模型,Document Object Model)是指用于操作 HTML 和 XML 文档的编程接口,它将文档内容表示为一个树形结构,允许开发者通过编程方式来操作和修改文档的内容、结构和样式。

DOM 树是由节点(Node)组成的层级结构,节点可以是元素(Element)、属性(Attribute)、文本(Text)、注释(Comment)等等。通过 DOM 接口,开发者可以使用 JavaScript 来访问和操作文档中的这些节点,例如添加、删除、移动和修改节点,以及事件监听和响应。

DOM 是由 W3C 组织制定的标准,几乎所有现代浏览器都支持 DOM 接口。在前端开发中,熟练掌握 DOM 编程技巧是非常重要的。
在这里插入图片描述

BOM
BOM (Browser Object Model) 指浏览器对象模型,它是指浏览器提供的 JavaScript 对象集合,用于操作浏览器窗口和框架。BOM 并不是 W3C 标准的一部分,不同浏览器对 BOM 的实现有所差异,但 BOM 中的一些对象和方法已经得到了广泛的支持和应用。BOM 包括 window、navigator、location、screen、history、document 等对象。

  • 小结: JavaScript是一种专为与网页交互而设计的脚本语言。
  • ECMAScript,由ECMA-262定义,提供核心语言功能。
  • 文档对象模型DOM:提供访问和操作网页的方法和接口。
  • 浏览器对象模型BOM:提供与浏览器交互的方法和接口。

第2章 在HTML中使用JavaScript

script元素
在JavaScript中,一个

  • async:布尔类型的属性,表示该脚本是否应该异步加载,仅适用于外部脚本。
  • charset:表示使用的字符集,仅适用于外部脚本。
  • defer:布尔类型的属性,表示该脚本是否应该延迟到页面解析完毕再执行,仅适用于外部脚本。
  • src:表示外部脚本文件的URL。
  • type:表示脚本的MIME类型,默认为"text/javascript"。
<script src="script.js" type="text/javascript" async></script>

script放在head还是body?

在HTML页面中,script标签可以放在或中。但是放在中的脚本会在页面渲染之前执行,因此可能会导致页面加载变慢。而将脚本放在底部会使得页面加载较快,因为浏览器会先渲染页面内容,再加载和执行JavaScript。但是在一些情况下,必须在中引入脚本,比如在脚本中使用了document.write()来直接在页面中输出内容。因此,放置

head标签
在HTML中,head元素包含了文档的元数据,这些元数据通常不会在页面中直接显示,而是提供给浏览器和搜索引擎等外部应用程序使用。

以下是head标签中可能包含的一些标签及其含义:

  • title:指定文档的标题,会显示在浏览器的标题栏上和书签中。
  • meta:提供文档的元数据,如描述、关键词、字符集、作者、视口等。
  • link:引入外部资源,如样式表、图标等
  • style:内部样式表,用于定义文档的样式。
  • script:脚本标签,用于引入或嵌入JavaScript代码。

需要注意的是,元素中的标签并不会直接呈现在页面上,而是在页面加载时被浏览器读取和处理。因此,head标签通常被放置在body标签的前面,以确保页面加载时先处理元数据等信息,再加载和呈现页面的主要内容。

XHTML
XHTML (eXtensible HyperText Markup Language) 是一种基于 XML 的 HTML 扩展语言,它的语法更加严格和规范化,更符合 XML 的语法要求,而且可以和其他的 XML 语言(如 SVG、MathML)一起使用。

XHTML 被设计用来取代 HTML 4.01 和之前的版本,提供更加一致和可靠的标记语言,同时兼容所有的 Web 浏览器和设备。XHTML 可以充分利用 XML 的优点,如可扩展性、可重用性、可维护性和可读性,并且更好地支持 Web 服务、数据交换和语义 Web。

总的来说,XHTML 的出现可以说是对 HTML 标准化和 Web 语义化发展的重要里程碑,为 Web 技术的发展带来了更加规范和健康的方向。

js嵌入代码还是引用外部文件
在选择将JavaScript代码嵌入到HTML页面还是将其作为外部文件引用时,通常需要考虑以下因素:

  • 代码的复用性:将代码作为外部文件引用可以在多个页面之间共享代码,提高代码复用性和可维护性。
  • 缓存:浏览器会缓存外部JavaScript文件,如果多个页面使用相同的文件,则可以从缓存中快速加载文件,提高页面加载速度。
  • 可维护性:将JavaScript代码与HTML代码分开,可以使代码更易于维护和管理。
  • 加载顺序:将JavaScript代码放在页面的头部可以确保代码在页面其他元素加载之前执行,但这可能会导致页面加载速度变慢。将代码放在页面的底部可以加快页面加载速度,但可能会导致页面在代码加载之前出现一些问题。

综合考虑以上因素,通常建议将JavaScript代码作为外部文件引用,可以提高代码复用性和可维护性,同时通过优化缓存和加载顺序,也可以加快页面加载速度。但在某些情况下,如只有一小段JavaScript代码需要使用,或者需要动态生成代码,嵌入到HTML页面中可能更为适合。

js标准模式
JavaScript标准模式(Strict Mode)是 ECMAScript 5 引入的一种限制 JavaScript 变体的方式。通过指定 “use strict”,可以选择性地启用该模式。在该模式下,编写代码的方式更严格,某些在早期版本中可以容忍的代码错误将被抛出错误或警告。

在标准模式下,JavaScript 引擎会执行以下操作:

  • 防止使用未声明的变量;
  • 禁止删除变量、函数、arguments 等;
  • 防止重复属性名;
  • 禁止使用八进制数字;
  • 防止使用保留字作为变量名;
  • 禁止使用 eval() 等函数的隐式调用;
  • 严格模式下 this 的值不再指向全局对象;
  • 严格模式下禁止在函数外部使用 this。
  • 使用 JavaScript 标准模式可以帮助开发者更好地编写可读性更高、可维护性更好、更安全的代码。

小结:

  • 要求把type属性设置成text/javascript,以表明使用的脚本语言是javascript。
  • 包含外部js文件时,必须src属性执行文件的URL。 所有script元素会按照他们在页面中出现的先后顺序依次解析。
  • 浏览器呈现后面的页面内容之前,必须先解析完前面的script元素中的代码。为此,一般要把script元素放在页面的末尾,内容之后和结束/body之前。

第3章 基本概念

ECMA-262第三版中定义的ECMAScript是各浏览器实现最多的版本。
语法:

  • 区分大小写
  • 标识符:第一个字符必须是字母、下划线、美元符号;
  • 标识符:使用小驼峰大小写格式,第一个字母小写,剩下的每个有意义的单词首字母大写。
  • 注释:单行// 多行/**/
  • 语句:单条语句分号结尾;多条语句{}
  • 关键字:不能作为标识符。
  • 保留字:不能作为标识符。

js中的关键字:
以下是 JavaScript 中的一些关键字:

break:用于终止循环或 switch 语句的执行。
case:在 switch 语句中用于定义不同的分支。
catch:用于捕获异常并执行相应的代码块。
class:在 ES6 中引入的关键字,用于声明类。
const:用于声明常量,创建一个只读的常量变量。 const PI=3.1415;
continue:用于跳过当前循环的剩余代码并进入下一次循环。
debugger:用于在代码中设置断点,以便在调试过程中进行暂停。
default:在 switch 语句中用于定义默认分支。
delete:用于删除对象的属性或数组中的元素。
do:用于创建一个循环,至少执行一次循环体。
else:在条件语句中表示一个替代分支。
export:在模块化 JavaScript 中用于导出变量、函数或对象。
extends:在 ES6 中引入的关键字,用于实现类的继承。
finally:在 try-catch 语句中用于定义无论是否发生异常都要执行的代码块。
for:用于创建一个循环,执行特定次数的循环体。
function:用于声明一个函数。
if:用于创建一个条件语句,根据条件执行特定的代码块。
import:在模块化 JavaScript 中用于导入变量、函数或对象。
instanceof:用于检测对象是否属于特定类或构造函数的实例。 animal instanceof Dog
let:用于声明块级作用域的变量。
new:用于创建对象实例。 let obj = new Constructor();
return:用于从函数中返回值。
switch:用于创建一个多路分支的选择语句。
this:用于引用当前对象。 this.age = age;
throw:用于抛出异常。
try:用于包含可能会抛出异常的代码块。
typeof:用于检测变量或表达式的数据类型。 console.log(typeof undefined);  // "undefined"
var:用于声明变量。
void:用于指定表达式没有返回值。
while:用于创建一个循环,根据条件重复执行循环体。
with:已被废弃,不推荐使用。

js关键字:

保留的未来使用关键字:enum, implements, interface, let, package, private, protected, public, static

保留的特定上下文关键字:await, as, async, get, set

需要注意的是,保留字和关键字的列表可能会随着 JavaScript 语言的不断发展而有所变化和更新。因此,最好在编写代码时参考最新的 JavaScript 文档和规范。

数据类型:

  1. 基本数据类型(Primitive Data Types):
    undefined: 表示未定义的值。
    null: 表示空值。
    boolean: 表示布尔值,可以是 true 或 false。
    number: 表示数字,包括整数和浮点数。
    string: 表示字符串,用单引号或双引号括起来的字符序列。

  2. 引用数据类型(Reference Data Types):
    object: 表示对象,可以是由键值对组成的集合,也可以是数组、函数等。
    array: 表示数组,是一种特殊的对象,用于存储多个值。
    function: 表示函数,可以执行特定的操作。

  3. 除了以上基本数据类型和引用数据类型,还有一种特殊的数据类型:
    symbol: 表示唯一的标识符,用于创建对象的唯一属性键。

JavaScript 是一种动态类型的语言,变量的数据类型可以在运行时自动推断或改变。可以使用typeof运算符来检测变量的数据类型。

undefined类型:
undefined 是 JavaScript 中的一种特殊值,表示一个未定义或未赋值的变量或属性

当一个变量被声明但没有被赋值时,它的默认值是 undefined。

let myVariable;
console.log(myVariable); // 输出: undefined

当访问一个对象的属性时,如果该属性不存在,则返回 undefined。

let myObj = { name: "John", age: 30 };
console.log(myObj.address); // 输出: undefined

需要注意的是,undefined 是一个全局变量,它的值本身就是 undefined。但是,不推荐将其作为变量的值来赋值,应该使用 null 来表示一个空值

null类型:
null 是 JavaScript 中的一种特殊值,表示为空值或缺少值。它被视为一个空的对象引用。

let myVariable = null;
console.log(myVariable); // 输出: null

null 是一个字面量,不是一个对象。因此,尝试访问 null 的属性或方法会导致错误。

let obj = null;
console.log(obj.property); // 抛出 TypeError: Cannot read property 'property' of null

需要注意的是,null 是一种数据类型,不同于 undefined。undefined 表示一个未定义的值,而 null 表示一个空值

数值转换:
在 JavaScript 中,可以使用一些内置的方法和操作符来进行数值转换。下面是一些常用的数值转换方法:

  1. parseInt(): 用于将字符串转换为整数。它解析一个字符串中的数字部分,忽略前导空格,并返回整数值。
let str = "123";
let num = parseInt(str);
console.log(num); // 输出: 123

// 可以指定基数
let binaryStr = "1010"; // 二进制数
let decimal = parseInt(binaryStr, 2);
console.log(decimal); // 输出: 10

let octalStr = "17"; // 八进制数
let decimal = parseInt(octalStr, 8);
console.log(decimal); // 输出: 15

let hexStr = "FF"; // 十六进制数
let decimal = parseInt(hexStr, 16);
console.log(decimal); // 输出: 255
  1. parseFloat(): 用于将字符串转换为浮点数。它解析一个字符串中的数字部分,并返回浮点数值。
let str = "3.14";
let num = parseFloat(str);
console.log(num); // 输出: 3.14

// toFixed() 
//指定小数位数,可以使用 toFixed() 方法对浮点数进行格式化,以获得特定位数的小数部分。
//toFixed() 方法将返回一个字符串,表示指定小数位数的浮点数。
//返回的是一个字符串,而不是浮点数。如果需要执行数学计算,使用 parseFloat() 转换回浮点数。
let num = 3.14159;
let fixedNum = num.toFixed(2); // 保留两位小数
console.log(fixedNum); // 输出: "3.14"

需要注意的是,如果字符串不能完全转换为有效的数字,则上述方法的返回结果可能是 NaN(Not a Number)。在进行数值转换时,始终要确保输入的字符串符合预期的格式,以避免意外的结果

Object类型:

  1. constructor: 表示创建对象的构造函数。
const person = { name: "John" };
console.log(person.constructor); // 输出: Object
  1. prototype: 表示对象的原型,可以用于添加共享的属性和方法。
const person = { name: "John" };
console.log(person.prototype); // 输出: undefined
  1. hasOwnProperty(propertyName): 检查对象是否具有指定名称的属性,而不是从原型链继承而来的。
const person = { name: "John" };
console.log(person.hasOwnProperty("name")); // 输出: true
console.log(person.hasOwnProperty("age")); // 输出: false
  1. isPrototypeOf(object): 检查对象是否是指定对象的原型。
const person = { name: "John" };
const obj = Object.create(person);
console.log(person.isPrototypeOf(obj)); // 输出: true
  1. propertyIsEnumerable(propertyName): 检查对象是否具有指定名称的可枚举属性。
const person = { name: "John" };
console.log(person.propertyIsEnumerable("name")); // 输出: true
console.log(person.propertyIsEnumerable("toString")); // 输出: false

操作符:

  1. 算术操作符(Arithmetic Operators):
+:加法
-:减法
*:乘法
/:除法
%:取模(求余)
++:自增
--:自减
  1. 赋值操作符(Assignment Operators):
=:赋值
+=:加法赋值
-=:减法赋值
*=:乘法赋值
/=:除法赋值
%=:取模赋值
  1. 比较操作符(Comparison Operators):
==:等于(值相等)
===:严格等于(值和类型均相等)
!=:不等于
!==:严格不等于
>:大于
<:小于
>=:大于等于
<=:小于等于
  1. 逻辑操作符(Logical Operators):
&&:逻辑与(and)
||:逻辑或(or)
!:逻辑非(not)
  1. 位操作符(Bitwise Operators):
&:按位与
|:按位或
^:按位异或
~:按位非
<<:左移
>>:右移
>>>:无符号右移
  1. 条件(三元)操作符(Conditional (Ternary) Operator):
condition ? expression1 : expression2:如果条件为真,则返回表达式1,否则返回表达式2

语句:

  1. 表达式语句(Expression Statement)

    由一个或多个表达式组成,以分号结尾。例如:x = 5;、console.log(“Hello!”);

  2. 声明语句(Declaration Statement): 用于声明变量、函数和类等。常见的声明语句有:

变量声明语句:var x;let y;const z = 10;
函数声明语句:function add(a, b) { return a + b; }
类声明语句:class MyClass { /* 类定义 */ }
  1. 条件语句(Conditional Statement):用于根据条件执行不同的代码块。
if语句:根据条件执行代码块,可配合elseelse if使用。
switch语句:根据表达式的值执行不同的代码块。
  1. 循环语句(Loop Statement):用于重复执行一段代码块,直到满足指定条件为止。
for循环:在循环开始前初始化变量,定义循环条件,指定每次循环后执行的操作。
while循环:在循环开始前判断条件,只要条件为真,就重复执行循环体。
do...while循环:在循环体执行后判断条件,至少会执行一次循环体。
for...in..迭代:
  1. 控制语句(Control Statement):用于控制程序的执行流程。
break语句:用于终止循环或switch语句的执行。
continue语句:用于跳过当前循环中的剩余代码,进入下一次循环迭代。
return语句:用于从函数中返回值,并终止函数的执行。

函数:
函数的定义使用function关键字,后面跟着函数名、参数列表和函数体。

function functionName(parameter1, parameter2, ...) {
  // 函数体
  // 执行特定的任务或操作
  return result; // 可选,返回一个值
}

参数:
函数可以有零个或多个参数,参数是可选的,可以根据函数的需要来定义。
函数的参数定义在函数名后的圆括号 () 内,多个参数之间使用逗号 , 分隔。

// 默认参数值
function greet(name = "Guest") {
  console.log("Hello, " + name + "!");
}
greet(); // 输出:Hello, Guest!
greet("Alice"); // 输出:Hello, Alice!

第4章 变量、作用域、内存问题

数据类型——基本类型:

  1. 字符串(String):表示文本数据,使用引号(单引号或双引号)括起来,例如 ‘Hello’ 或 “World”。
  2. 数字(Number):表示数值数据,包括整数和浮点数,例如 42 或 3.14。
  3. 布尔值(Boolean):表示逻辑值,只有两个取值:true(真)和 false(假)。
  4. 空值(Null):表示空或不存在的值,只有一个取值 null。
  5. 未定义(Undefined):表示未赋值的变量,只有一个取值 undefined。

这些基本类型是不可变的,也就是说,变量存储的值是不可改变的,对基本类型的操作总是返回一个新的值。

数据类型——引用类型:

  1. 对象(Object):表示复杂的数据结构,可以包含多个键值对(属性和对应的值)。对象可以通过字面量表示法 {} 或 new
    Object() 创建。
  2. 数组(Array):表示一组有序的数据集合,可以包含多个值。数组可以通过字面量表示法 [] 或 new Array() 创建。
  3. 函数(Function):表示可执行的代码块,可以接受参数并返回一个值。函数可以通过定义函数表达式或函数声明的方式创建。

引用类型的变量存储的是对象的引用(内存地址),而不是实际的对象本身。对于引用类型的操作,实际上是操作对象的引用,因此改变引用类型的属性会影响到所有引用该对象的变量。

传递参数——值传递:
当将基本类型的值作为参数传递给函数时,会将该值的副本传递给函数内部。在函数内部对参数进行修改不会影响到原始值。

function updateValue(value) {
  value = 42; // 修改参数值
}

var num = 10;
updateValue(num);
console.log(num); // 输出: 10,原始值未被修改

传递参数——引用传递:
当将引用类型(对象、数组等)作为参数传递给函数时,实际上传递的是对象的引用(内存地址)。在函数内部对参数进行修改会影响到原始对象。

function updateArray(arr) {
  arr.push(4); // 修改参数对象
}

var myArray = [1, 2, 3];
updateArray(myArray);
console.log(myArray); // 输出: [1, 2, 3, 4],原始数组被修改

需要注意的是,虽然引用传递会影响到原始对象,但是将参数重新赋值为一个新的对象时,不会影响到原始对象。

function updateObj(obj) {
  obj = { name: 'John', age: 25 }; // 将参数赋值为新对象
}

var myObj = { name: 'Alice', age: 30 };
updateObj(myObj);
console.log(myObj); // 输出: { name: 'Alice', age: 30 },原始对象未被修改

js检查类型:

  1. typeof 操作符:typeof 操作符返回一个字符串,表示操作数的数据类型。
typeof 42; // 返回 "number"
typeof 'Hello'; // 返回 "string"
typeof true; // 返回 "boolean"
typeof undefined; // 返回 "undefined"
typeof null; // 返回 "object"(注意:null 被错误地判断为对象类型)
typeof []; // 返回 "object"
typeof {}; // 返回 "object"
typeof function() {}; // 返回 "function"

需要注意的是,typeof 对于数组和对象会返回 “object”,对于 null 会返回 “object”,对于函数会返回 “function”。

  1. instanceof 操作符:instanceof 操作符用于检查对象是否属于某个特定类的实例。
 var arr = [1, 2, 3];
var obj = {};
var date = new Date();

arr instanceof Array; // 返回 true
obj instanceof Object; // 返回 true
date instanceof Date; // 返回 true

instanceof 操作符会检查对象的原型链,因此可以准确地判断对象是否是某个类的实例。

  1. Object.prototype.toString() 方法:toString() 方法返回一个表示对象的类型的字符串。
Object.prototype.toString.call(42); // 返回 "[object Number]"
Object.prototype.toString.call('Hello'); // 返回 "[object String]"
Object.prototype.toString.call(true); // 返回 "[object Boolean]"
Object.prototype.toString.call(undefined); // 返回 "[object Undefined]"
Object.prototype.toString.call(null); // 返回 "[object Null]"
Object.prototype.toString.call([]); // 返回 "[object Array]"
Object.prototype.toString.call({}); // 返回 "[object Object]"
Object.prototype.toString.call(function() {}); // 返回 "[object Function]"

Object.prototype.toString() 方法可以精确地返回对象的类型,并且对于特殊类型(如 null 和数组)也能正确判断。

执行环境:
执行环境(Execution Context)是 JavaScript 中执行代码的环境。每当 JavaScript 代码执行时,都会创建一个执行环境,用于管理代码的执行。

执行环境由以下三个重要的组成部分组成:

  1. 变量对象(VariableObject)
    变量对象是执行环境中的一个特殊对象,它存储了在该环境中定义的变量和函数。变量对象可以理解为一个存储变量和函数的容器。在全局执行环境中,变量对象就是全局对象(例如浏览器环境中的
    window 对象)。在函数执行环境中,变量对象是活动对象(Active Object),它包含了函数的参数、局部变量和内部函数。

  2. 作用域链(Scope Chain):
    作用域链是一个指向变量对象的链表,用于解析变量的访问权限。当访问一个变量时,JavaScript
    引擎会从当前执行环境的作用域链中查找该变量,如果找到则使用,否则继续向上级作用域链查找,直到找到变量或到达最外层的全局执行环境。

  3. this 值:
    this 值指向当前执行环境所属的对象。在全局执行环境中,this 指向全局对象。在函数执行环境中,this
    的值取决于函数的调用方式。

JavaScript 中存在多种执行环境,包括全局执行环境和函数执行环境。每当进入一个新的函数时,都会创建一个新的函数执行环境,形成一个执行环境的栈(Execution Context Stack),称为执行上下文栈。
执行上下文栈的顶部始终是当前正在执行的代码所在的执行环境。

块级作用域:
在早期的 JavaScript 版本中,确实没有块级作用域。块级作用域是指由一对花括号 {} 所包裹的代码块,在该代码块内声明的变量在代码块外部是不可访问的。

然而,在== ECMAScript 6 (ES6) 中引入了 let 和 const 关键字==,它们可以用于声明块级作用域的变量和常量。使用 let 或 const 声明的变量将在声明的块级作用域内有效,超出该作用域则无法访问。

function example() {
  if (true) {
    let x = 10;
    const y = 20;
    console.log(x); // 输出: 10
    console.log(y); // 输出: 20
  }
  
  console.log(x); // 报错: x is not defined
  console.log(y); // 报错: y is not defined
}

example();

性能问题:

在JavaScript中,性能问题可能出现在多个方面,包括代码执行效率、内存使用和网络请求等。以下是一些常见的性能问题和优化建议:

  1. 循环和迭代:避免过多的嵌套循环和迭代,尽量使用更高效的算法和数据结构来优化代码。
  2. 避免不必要的全局变量:全局变量会占用内存,并且对性能有负面影响。尽量将变量限制在局部作用域内,避免滥用全局变量。
  3. 减少DOM操作:频繁的DOM操作会导致页面重绘和重排,影响性能。可以通过缓存DOM查询结果、使用文档片段进行批量操作等方式来减少DOM操作次数。
  4. 事件处理:合理使用事件委托和事件冒泡,避免在每个元素上都绑定事件处理器,以减少事件处理器的数量和内存占用。
  5. 图片优化:对于图片资源,可以优化图片大小和格式,使用适当的压缩算法,以减少网络传输和页面加载时间。
  6. 异步编程:使用异步编程模式,如回调函数、Promise、async/await等,可以避免阻塞主线程,提高页面响应速度。
  7. 缓存和资源管理:合理使用缓存机制,避免重复请求相同的资源。对于大型的数据集合,可以考虑使用分页加载或延迟加载的方式,以优化内存使用和加载时间。
  8. 性能监测和优化:使用开发者工具进行性能分析和监测,识别代码中的性能瓶颈,并进行相应的优化。
  9. 编译和打包工具:使用优化的编译和打包工具,如Webpack、Rollup等,可以对代码进行压缩、合并和模块化管理,以提高代码执行效率和加载速度。

第5章 引用类型

  • Object类型
//创建方法一
var person=new Object();
person.name='test';
person.age=14;
console.log(person);//{name: 'test', age: 14}

//创建方法二
var person={name='test',age=11};
console.log(person);//{name: 'test', age: 14}
  • Array类型
const myList=[1,2,3,4,50];
console.log(myList);//VM542:1 (5) [1, 2, 3, 4, 50]

//栈 先进后出
//队列 先进先出
  • Date类型
let now=new Date(); // Mon Jul 03 2023 16:05:10 GMT+0800 (中国标准时间)
now.getFullYear(); // 2023
now.getMonth(); // 6  月份0-11 需要加1
now.getDay(); // 1
now.getHours();// 16
now.getMinutes();// 5
now.getSeconds();//10
  • RegExp类型
    RegExp(正则表达式)是一种内置对象类型,用于处理字符串的模式匹配和替换。
//创建RegExp对象的语法是new RegExp(pattern, flags)
//其中pattern是要匹配的模式字符串,flags是可选的修饰符字符串
const regex = new RegExp("pattern", "g"); // 使用RegExp构造函数

第6章 面向对象的程序设计

在JavaScript中,面向对象编程(Object-Oriented Programming,OOP)是一种编程范式,它通过创建对象和定义对象之间的关系来解决问题。JavaScript是一种支持面向对象编程的多范式语言,因此可以使用面向对象的方法来组织和管理代码。

在JavaScript中,对象是一组相关数据和功能的集合。对象可以通过对象字面量、构造函数、或者ES6中的类来创建。每个对象都有属性和方法。

创建对象的设计模式

在JavaScript中,创建对象的设计模式有多种形式。除了工厂模式,还有构造函数模式、原型模式、构造函数与原型模式结合的组合模式,以及ES6引入的类(class)等。以下是对每种模式的简要介绍:

1. 工厂模式(Factory Pattern)(被构造模式取代)

使用工厂函数来封装对象的创建过程,通过调用工厂函数来创建新的对象。

function createPerson(name, age) {
  return {
    name: name,
    age: age,
    greet: function() {
      console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
    }
  };
}

const person1 = createPerson("John", 30);
const person2 = createPerson("Alice", 25);

person1.greet(); // Output: Hello, my name is John and I am 30 years old.
person2.greet(); // Output: Hello, my name is Alice and I am 25 years old.

2. 构造函数模式(ConstructorPattern)

使用构造函数来创建对象,通过new关键字调用构造函数,构造函数内部使用this关键字定义对象的属性和方法。

function Person(name, age) {
  this.name = name;
  this.age = age;
}

Person.prototype.greet = function() {
  console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
};

const person = new Person("John", 30);
person.greet(); // Output: Hello, my name is John and I am 30 years old.

3. 原型模式(Prototype Pattern)

使用原型对象来共享对象的属性和方法,通过原型链实现对象的继承。
每个函数都有一个特殊的属性 prototype。

// 构造函数
function Person(name, age) {
 this.name = name;
 this.age = age;
}

// 在原型对象上定义方法
Person.prototype.greet = function() {
 console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
};

// 创建对象实例
const person1 = new Person("John", 30);
const person2 = new Person("Alice", 25);

person1.greet(); // Output: Hello, my name is John and I am 30 years old.
person2.greet(); // Output: Hello, my name is Alice and I am 25 years old.

在上面的例子中,我们将 greet 方法定义在 Person 构造函数的原型对象上,而不是在构造函数内部。这样,无论创建多少个 Person 实例,它们都会共享同一个 greet 方法,而不是每个实例都拥有独立的 greet 方法。

原型模式的优点是可以节省内存,因为共享的属性和方法只需要存储一次,而不是在每个实例中重复定义。同时,它也提供了一种更灵活的方式来管理对象的属性和方法,使得代码更加简洁和易于维护

需要注意的是,修改原型对象的属性或方法会立即影响到所有已经创建的实例,因为它们共享同一个原型对象。因此,在修改原型对象时要小心,确保不会意外地影响到其他实例。

4. 组合模式(Constructor and PrototypePattern)(使用最多)
结合构造函数模式和原型模式,构造函数用于定义对象的属性,原型用于定义对象的方法和共享属性。
// 构造函数
function Person(name, age) {
  this.name = name;
  this.age = age;
}

// 在原型对象上定义方法和共享属性
Person.prototype.greet = function() {
  console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
};
Person.prototype.species = "Human";

// 创建对象实例
const person1 = new Person("John", 30);
const person2 = new Person("Alice", 25);

person1.greet(); // Output: Hello, my name is John and I am 30 years old.
person2.greet(); // Output: Hello, my name is Alice and I am 25 years old.

console.log(person1.species); // Output: Human
console.log(person2.species); // Output: Human

组合模式结合了构造函数和原型模式的优点,提供了更灵活的对象创建方式。

5. 类(Class)

== ES6引入了类的概念==,使得创建对象更加简洁和易读。类是一种语法糖,本质上仍然是基于原型的面向对象编程。

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  greet() {
    console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
  }
}

const person = new Person("John", 30);
person.greet(); // Output: Hello, my name is John and I am 30 years old.

这些设计模式都有各自的优点和适用场景。==工厂模式适用于创建简单对象,封装对象的创建过程;==构造函数模式适用于创建多个相似的对象,定义实例特有的属性;原型模式适用于共享对象的方法和属性,节省内存;组合模式结合了构造函数和原型的优点,提供了更灵活的对象创建方式;ES6类提供了更接近传统面向对象编程的语法,使代码更加清晰易懂。

继承

在 JavaScript 中,面向对象继承是一种重要的特性,它允许一个对象(子类)继承另一个对象(父类)的属性和方法。继承可以帮助我们实现代码的重用和扩展,避免重复编写相同的代码。

JavaScript中的继承有多种方式,包括原型链继承、构造函数继承、组合继承、ES6中的类继承等。以下是对每种继承方式的简要介绍:

1. 原型链继承

原型链继承是通过将子类的原型对象指向父类的实例来实现继承。子类通过原型链访问父类的属性和方法。但是原型链继承有一个缺点,就是父类的引用类型属性会被所有子类实例共享,可能导致子类实例之间相互影响。示例代码:

function Parent() {
  this.name = "John";
}

Parent.prototype.greet = function() {
  console.log(`Hello, my name is ${this.name}.`);
};

function Child() {}

Child.prototype = new Parent();

const child1 = new Child();
child1.greet(); // Output: Hello, my name is John.

2. 构造函数继承

构造函数继承是通过在子类构造函数内部调用父类构造函数来实现继承。这样可以解决原型链继承中引用类型属性共享的问题,但是父类的方法不会被子类继承。示例代码:

function Parent() {
  this.name = "John";
}

Parent.prototype.greet = function() {
  console.log(`Hello, my name is ${this.name}.`);
};

function Child() {
  Parent.call(this);
}

const child1 = new Child();
child1.greet(); // Error: child1.greet is not a function

3. 组合继承

组合继承结合了原型链继承和构造函数继承的优点,通过在子类构造函数内部调用父类构造函数,并将子类的原型对象指向父类的实例,实现同时继承父类的属性和方法。示例代码:

function Parent() {
  this.name = "John";
}

Parent.prototype.greet = function() {
  console.log(`Hello, my name is ${this.name}.`);
};

function Child() {
  Parent.call(this);
}

Child.prototype = new Parent();
Child.prototype.constructor = Child;

const child1 = new Child();
child1.greet(); // Output: Hello, my name is John.

4. ES6中的类继承

ES6引入了 class 关键字,使得继承更加简洁和易读。通过 extends 关键字可以实现类的继承。示例代码:

class Parent {
  constructor() {
    this.name = "John";
  }

  greet() {
    console.log(`Hello, my name is ${this.name}.`);
  }
}

class Child extends Parent {}

const child1 = new Child();
child1.greet(); // Output: Hello, my name is John.

在选择继承方式时,需要根据具体的需求和场景来决定。ES6中的类继承是推荐的现代方式,它更接近传统面向对象编程的语法。但是在一些特殊情况下,原型链继承、构造函数继承或组合继承也可能有用。

面向对象编程的核心概念包括封装、继承和多态。封装指的是将数据和行为封装在对象中,通过对象的接口来访问和操作数据。继承允许对象从其他对象继承属性和方法,以便共享和重用代码。多态允许对象根据上下文以不同的方式响应相同的方法调用。

使用面向对象编程的优点包括代码的可维护性、可扩展性和复用性。通过合理使用对象和类,可以更好地组织和管理代码,使其更易于理解和维护。

第7章 匿名函数

匿名函数是一种没有名称的函数,有时候也叫lambda函数。通常用于需要在特定上下文中临时定义和执行代码块的情况。匿名函数在 JavaScript 中非常常见,它们可以作为表达式传递给其他函数,也可以直接执行。
在 JavaScript 中,匿名函数有两种主要形式:

  • 函数表达式匿名函数:
const anonymousFunc = function() {
  console.log("This is an anonymous function.");
};

anonymousFunc(); // 调用匿名函数

匿名函数在许多场景中非常有用,特别是在需要封装一段代码以避免污染全局命名空间时,或者需要传递一个函数作为参数给其他函数时。例如,在事件处理程序、回调函数、定时器和闭包等地方经常使用匿名函数。

请注意,ES6 引入了箭头函数,它也可以被视为匿名函数的一种形式。箭头函数更简洁,并且自动绑定了外部上下文的 this。以下是一个箭头函数的匿名函数示例:

const arrowAnonymous = () => {
  console.log("This is an arrow anonymous function.");
};

arrowAnonymous(); // 调用箭头匿名函数

无论是传统的函数表达式还是箭头函数,匿名函数都是 JavaScript 编程中的重要组成部分,可以帮助您编写更具灵活性和可读性的代码。

递归

递归是一种在函数内部调用自身的编程技术。在递归过程中,函数通过不断将问题拆分为更小的子问题来解决复杂的任务。递归在许多算法和数据结构问题中都有广泛的应用,例如在树结构、图算法、排列组合问题等中。

递归的关键要素包括:

  • 基本情况(Base Case):递归函数必须定义一个或多个基本情况,即不再递归调用自身的情况。基本情况通常是一个简单的问题,可以直接求解。
  • 递归调用(Recursive Call):在函数内部,通过调用自身来解决更小的子问题。每次递归调用都将问题的规模减小,直到达到基本情况。

下面是一个经典的递归例子,计算阶乘(Factorial)

function factorial(n) {
  if (n === 0 || n === 1) {
    return 1; // 基本情况
  } else {
    return n * factorial(n - 1); // 递归调用
  }
}

console.log(factorial(5)); // 输出 120 (5 * 4 * 3 * 2 * 1)

在这个例子中,factorial 函数计算了给定正整数 n 的阶乘。当 n 达到基本情况(0 或 1)时,函数直接返回 1。否则,函数通过递归调用自身来计算 n 与 n - 1 的阶乘的乘积。

递归的优点是它可以将复杂问题分解为简单问题,使代码更具可读性。然而,需要小心处理递归的退出条件和递归调用的次数,以避免陷入无限递归循环。适当使用递归可以简化代码,但在某些情况下,迭代(循环)可能更高效。

闭包

闭包是指在一个函数内部创建另一个函数,并且内部函数可以访问外部函数的变量和参数,即使外部函数已经执行完毕并返回了。这使得闭包能够“捕获”外部函数的上下文,并在内部函数中保持这些上下文的状态。

闭包在 JavaScript 中是一种强大且常用的编程特性,它可以用于实现许多有趣的功能和模式,例如封装、私有变量、函数工厂、模块模式等。

以下是一个简单的闭包示例:

function outerFunction() {
  var outerVariable = 'I am from outer function';
  
  function innerFunction() {
    console.log(outerVariable); // 内部函数访问了外部函数的变量
  }
  
  return innerFunction; // 返回内部函数作为闭包
}

var closure = outerFunction(); // 创建一个闭包
closure(); // 输出:I am from outer function

在上面的示例中,innerFunction 是一个闭包,它可以访问 outerFunction 的变量 outerVariable,尽管 outerFunction 已经执行完毕。当我们调用 outerFunction 并将其返回的 innerFunction 赋值给 closure 后,closure 变成了一个可以保持 outerFunction 上下文的函数,我们可以随时调用它来访问该上下文

闭包的应用

  • 封装和数据隐藏:通过使用闭包,可以创建具有私有变量和方法的对象,从而实现封装和数据隐藏,防止外部直接访问和修改内部状态。
  • 函数工厂:通过闭包可以动态创建具有不同初始值的函数。
  • 模块模式:将一组相关的函数和变量组织成模块,只暴露公共接口,隐藏内部实现细节。
  • 事件处理程序:在事件处理程序中使用闭包可以保持事件发生时的上下文。

需要注意的是,过多地使用闭包可能导致内存泄漏,因为闭包会持有对外部函数作用域的引用,导致外部函数的变量无法被垃圾回收。因此,在使用闭包时,要注意及时释放不再需要的引用

关于this对象

this 是在 JavaScript 中非常重要的关键字,它通常用于引用当前执行代码的对象。this 的值取决于函数的调用方式,它可以在不同的上下文中引用不同的对象。

在 JavaScript 中,this 的值可能是以下几种情况之一:

  • 全局上下文下的 this:在全局作用域下(不在任何函数内部),this 引用全局对象,如浏览器环境中的 window 对象。
  • 函数中的 this:在函数内部,this 的值取决于函数的调用方式。如果函数作为普通函数调用,this
    通常指向全局对象(在非严格模式下)或者是 undefined(在严格模式下)。但是,如果函数作为对象的方法调用,this 将引用该对象
  • 构造函数中的 this:当使用== new 关键字创建对象实例==时,构造函数内部的 this 引用新创建的对象。
  • 事件处理程序中的 this:在事件处理程序中,this 通常指向触发事件的 DOM 元素
  • 箭头函数中的 this:在箭头函数中,this 由外部(包围箭头函数的函数或代码块)决定,而不是在函数内部创建时决定。

下面是一些示例,演示了不同上下文中 this 的值:

console.log(this); // 在浏览器环境中,输出全局对象 window

function myFunction() {
  console.log(this);
}

myFunction(); // 在浏览器环境中,输出全局对象 window(在非严格模式下)

const myObject = {
  prop: 'I am a property',
  printProp: function() {
    console.log(this.prop);
  }
};

myObject.printProp(); // 输出:I am a property

function MyConstructor() {
  this.prop = 'I am a property';
}

const instance = new MyConstructor();
console.log(instance.prop); // 输出:I am a property

document.getElementById('myButton').addEventListener('click', function() {
  console.log(this); // 输出点击的按钮元素
});

const arrowFunction = () => {
  console.log(this); // 根据外部上下文确定 this
};
arrowFunction();

要理解 this 的值,重要的是要考虑函数是如何被调用的,以及它所在的上下文。不同的调用方式和上下文会影响 this 的指向,因此在编写 JavaScript 代码时需要特别注意 this 的使用。

内存泄漏

内存泄漏是指程序中分配的内存没有被正确释放或回收,导致系统中的可用内存逐渐减少,最终可能导致程序性能下降甚至崩溃。内存泄漏通常是由于不正确的内存管理或编程错误引起的。

在 JavaScript 中,内存泄漏通常发生在以下情况下:

  • 无法释放不再需要的对象:如果不再需要的对象仍然被引用,它将无法被垃圾回收。这可能发生在闭包、全局对象、事件监听器或循环引用等情况下。
  • 未清理的定时器和回调:如果定时器或回调函数没有正确清理,它们可能会继续占用内存。
  • DOM 元素引用:在 JavaScript 中,保持对 DOM 元素的引用可能导致内存泄漏。例如,未删除不再需要的 DOM
    元素引用或未解除绑定的事件监听器。
  • 循环引用:当两个或多个对象彼此引用时,如果没有正确的引用释放,这可能导致内存泄漏。

避免内存泄漏的方法

  • 释放不再需要的引用:确保在不再需要对象时将其引用设置为 null,以便垃圾回收器可以回收内存。
  • 使用事件委托:对于大量 DOM 元素,使用事件委托可以减少事件监听器的数量,从而减少内存占用。
  • 注意闭包:在使用闭包时,确保正确处理引用,避免保持对不再需要的变量的引用。
  • 正确使用定时器和回调:确保在不需要定时器或回调时清除它们,避免长时间保持对它们的引用。
  • 使用现代开发工具:现代浏览器和开发工具通常具有内存分析工具,可以帮助您识别内存泄漏问题。
  • 测试和性能优化:定期进行代码审查、测试和性能优化,以及使用内存管理最佳实践,可以帮助您及早发现和解决内存泄漏问题。

总之,内存泄漏是需要认真对待的问题,特别是在长时间运行的应用程序中。遵循良好的内存管理和编程实践,可以帮助您避免内存泄漏并保持应用程序的稳定性和性能。

私有变量

在 JavaScript 中,私有变量是指仅在特定作用域内可访问的变量,而在其他作用域中无法直接访问。在许多编程语言中,可以使用块级作用域或访问修饰符来实现私有变量,但 JavaScript 并没有原生支持这种方式

然而,JavaScript 提供了一些模式和技术,可以模拟实现私有变量的效果:

  • 构造函数和闭包:通过在构造函数内部使用闭包,可以创建每个实例独有的私有变量。
function Counter() {
  var privateCount = 0;

  this.increment = function() {
    privateCount++;
  };

  this.decrement = function() {
    privateCount--;
  };

  this.value = function() {
    return privateCount;
  };
}

var counter1 = new Counter();
console.log(counter1.value()); // 输出:0
counter1.increment();
counter1.increment();
console.log(counter1.value()); // 输出:2

var counter2 = new Counter();
console.log(counter2.value()); // 输出:0

需要注意的是,上述模式只是模拟了私有变量的效果,而不是真正的私有性。在实际开发中,可以使用 ES6 引入的 class 和 Symbol 等特性来实现更加优雅和可靠的私有变量

class Counter {
  #privateCount = 0;

  increment() {
    this.#privateCount++;
  }

  decrement() {
    this.#privateCount--;
  }

  value() {
    return this.#privateCount;
  }
}

const counter = new Counter();
console.log(counter.value()); // 输出:0
counter.increment();
counter.increment();
console.log(counter.value()); // 输出:2

总之,虽然 JavaScript 没有原生支持真正的私有变量,但可以通过使用闭包、模块模式、构造函数等方式来模拟实现私有变量的效果。同时,在 ES6 中引入的一些新特性也提供了更优雅的方式来处理私有变量。

模块模式

模块模式是一种常见的 JavaScript 设计模式,用于创建具有封装性和隔离性的模块化代码。它通过使用闭包来创建一个独立的作用域,从而实现私有变量和函数,防止命名冲突和全局污染。模块模式在早期的 JavaScript 中被广泛使用,尤其是在没有模块系统的环境中。

以下是一个使用模块模式创建模块的示例:

var myModule = (function() {
  // 私有变量和函数
  var privateVar = 'I am private';
  
  function privateFunction() {
    console.log('This is a private function');
  }

  // 返回一个包含公共接口的对象
  return {
    publicVar: 'I am public',
    publicFunction: function() {
      console.log('This is a public function');
    }
  };
})();

console.log(myModule.publicVar); // 输出:I am public
myModule.publicFunction(); // 输出:This is a public function

// 以下代码会报错,因为 privateVar 和 privateFunction 是私有的
// console.log(myModule.privateVar);
// myModule.privateFunction();

在上述示例中,我们使用匿名函数创建了一个模块。在模块内部,定义了私有变量 privateVar 和私有函数 privateFunction,它们都无法在模块外部直接访问。然后,我们返回一个包含公共接口的对象,其中包含了可以在模块外部访问的公共变量和函数

模块模式的优点包括:

  • 封装性和隔离性:模块模式通过闭包实现私有变量和函数,可以将内部细节隐藏起来,防止外部直接访问。
  • 命名空间隔离:模块模式可以减少全局命名冲突,将变量和函数封装在模块内部。
  • 代码组织:模块模式可以将相关的功能组织在一起,提高代码的可读性和维护性。

然而,随着现代 JavaScript 的发展,ES6 引入了模块系统(import 和 export),使模块化变得更加简单和标准化。尽管如此,了解模块模式仍然有助于理解模块化的基本概念和原理。

第8章 BOM

BOM(Browser Object Model,浏览器对象模型)是 JavaScript 中用于与浏览器窗口和文档进行交互的一组对象的集合。BOM 提供了访问和操作浏览器窗口、页面内容、浏览器历史、位置信息等功能。BOM 并非由 JavaScript 标准规定的,不同的浏览器可能在 BOM 的实现上存在一些差异。

BOM 的核心对象包括:

  • window 对象:代表浏览器窗口,是 BOM 的顶层对象。它包含了许多方法和属性,用于操作浏览器窗口、设置定时器、导航等。
  • document 对象:代表当前载入的文档,可以用于访问和操作页面内容,如元素的增删改查、事件绑定等。
  • navigator 对象:提供浏览器相关信息,如浏览器名称、版本、用户代理字符串等。
  • screen 对象:提供关于用户屏幕的信息,如屏幕尺寸、可用窗口尺寸等。
  • location 对象:提供了对浏览器 URL 的访问和操作,可以用于获取或修改当前页面的 URL。
  • history 对象:提供浏览器历史记录的访问和操作,可以用于前进、后退、跳转等操作。
  • event 对象:代表事件,包含了触发事件的相关信息,如事件类型、目标元素等。

在 BOM 中,最常见和常用的是 window 和 document 对象。例如,你可以使用 window.alert() 来显示一个弹出对话框,使用 document.getElementById() 来获取页面中的元素。

需要注意的是,由于 BOM 不受 JavaScript 标准控制,不同的浏览器可能在实现上存在差异,因此在编写使用 BOM 的代码时,需要注意浏览器兼容性和不同浏览器之间的差异

window 对象

window 对象是 JavaScript 的顶级对象之一,它代表了浏览器窗口或浏览器的全局环境。在浏览器环境中,全局作用域中的所有变量和函数都是 window 对象的属性和方法。换句话说,你可以直接访问全局作用域中的变量和函数,就像是访问 window 对象的属性和方法一样。

以下是一些 window 对象的常见属性和方法:

  • window.alert(message):显示一个包含消息的弹出对话框。
  • window.prompt(message, default):显示一个包含输入框的对话框,用于用户输入内容。
  • window.confirm(message):显示一个包含确认和取消按钮的对话框,用于用户确认或取消一个操作。
  • window.document:指向当前窗口中显示的文档对象,可以用来访问和操作页面内容。
  • window.location:包含有关当前 URL 的信息,可以用于获取或修改当前页面的 URL。
  • window.navigator:提供关于浏览器的信息,如浏览器名称、版本、用户代理等。
  • window.screen:提供有关用户屏幕的信息,如屏幕尺寸、可用窗口尺寸等
  • window.history:提供有关浏览器历史记录的信息,可以用于前进、后退、跳转等操作。
  • window.open(url, name, specs, replace):打开一个新的浏览器窗口或标签页。
  • window.close():关闭当前浏览器窗口。

window 对象中还包含许多其他属性和方法,用于管理浏览器窗口、定时器、框架、计时器等。需要注意的是,window 对象在浏览器环境中才存在,在 Node.js 等其他环境中并不一定存在

在编写代码时,可以直接使用 window 对象来访问和操作全局作用域中的内容。例如:

window.alert('Hello, world!');
var url = window.location.href;

window间歇调用和延时调用:
在 JavaScript 中,window 对象提供了两种定时任务的方法,即间歇调用和延时调用,分别由 setInterval 和 setTimeout 函数实现。

  • 间歇调用 (setInterval):

setInterval 函数用于创建一个重复执行的定时任务,它会在指定的时间间隔内反复调用指定的函数。每个时间间隔过后,函数都会被调用一次,除非使用 clearInterval 取消了定时任务。

示例:

var intervalId = setInterval(function() {
  console.log('This will run every 1000 milliseconds');
}, 1000);

// 取消定时任务
clearInterval(intervalId);

  • 延时调用 (setTimeout):

setTimeout 函数用于创建一个一次性的定时任务,它会在指定的延迟时间过后执行指定的函数。函数只会被调用一次,除非再次使用 setTimeout 或 setInterval。

示例:

setTimeout(function() {
  console.log('This will run after 2000 milliseconds');
}, 2000);

这两种方法的主要区别在于重复执行和执行次数
setInterval 会按照指定的时间间隔无限次数地重复执行函数,直到使用 clearInterval 取消。
而 setTimeout 会在指定的延迟时间过后执行一次函数,不会自动重复。

无论是间歇调用还是延时调用,都可以用于实现定时刷新、动画效果、延迟加载等各种功能。在使用这些定时任务时,要注意避免频繁执行和定时任务叠加,同时,注意在不需要定时任务时及时使用 clearTimeout 和 clearInterval 来取消定时任务,以防止内存泄漏和不必要的性能损耗。

document 对象

在 JavaScript 中,document 对象是代表当前文档(网页)的对象,它是浏览器提供的一种访问和操作网页内容的接口。通过 document 对象,你可以获取、修改和操作 HTML 元素、文本内容、样式、事件等。

下面是一些常见的 document 对象属性和方法:

  • document.getElementById(id):通过元素的 id 属性获取对应的 HTML 元素。
  • document.getElementsByClassName(className):通过元素的类名获取对应的一组 HTML 元素。
  • document.getElementsByTagName(tagName):通过元素的标签名获取对应的一组 HTML 元素。
  • document.querySelector(selector):通过 CSS 选择器获取匹配的第一个 HTML 元素。
  • document.querySelectorAll(selector):通过 CSS 选择器获取所有匹配的 HTML 元素。
  • document.createElement(tagName):创建一个指定标签名的新 HTML 元素。
  • document.createTextNode(text):创建一个包含指定文本内容的新文本节点。
  • document.body:表示 元素,可以用来访问和操作页面主体内容。
  • document.title:获取或设置当前文档的标题。
  • document.URL:获取当前文档的 URL。
  • document.cookie:获取或设置当前文档的 Cookie。
  • document.write(content):向文档写入内容,通常在页面加载时使用。
  • document.addEventListener(event, handler):添加事件监听器,用于响应特定事件的发生。

document 对象的方法和属性使得 JavaScript 可以与网页内容进行交互,实现动态更新、用户交互等功能。通过操作 document 对象,你可以动态地修改页面内容、样式,处理用户输入,以及响应各种事件。

navigator 对象

navigator 对象是 JavaScript 中的一个内置对象,它提供了关于浏览器的相关信息。通过 navigator 对象,你可以获取浏览器的名称、版本、用户代理字符串,以及一些关于操作系统的信息。

以下是一些常见的 navigator 对象属性:

  • navigator.userAgent:返回包含用户代理字符串的字符串,它描述了浏览器的信息,包括浏览器名称、版本等。
  • navigator.appName:返回浏览器的名称,通常是 “Netscape”。
  • navigator.appVersion:返回浏览器的版本信息。
  • navigator.platform:返回运行浏览器的操作系统平台。
  • navigator.language 或 navigator.userLanguage:返回浏览器的语言设置。
  • navigator.cookieEnabled:表示浏览器是否启用了 cookie。
  • navigator.onLine:表示设备是否连接到网络。
  • navigator.geolocation:提供用于获取设备地理位置信息的方法和属性。
  • navigator.javaEnabled():检测浏览器是否支持 Java 应用程序。

需要注意的是,navigator 对象提供的信息是只读的,并且在不同浏览器之间可能存在一些差异。使用 navigator 对象可以根据浏览器的不同,进行一些特定的操作或者加载不同版本的资源,以实现更好的用户体验。

console.log(navigator.userAgent);
//  Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36
console.log(navigator.appName);
//  Netscape
console.log(navigator.appVersion);
//  5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 
console.log(navigator.platform);
// Safari/537.36 Win32
console.log(navigator.language);
//  zh-CN
console.log(navigator.cookieEnabled);
// true
console.log(navigator.onLine);
// true

需要注意的是,虽然 navigator 对象提供了许多有用的信息,但由于用户代理字符串可以被修改,因此不应完全依赖于此来判断浏览器或设备的特性。在实际应用中,最好使用 feature detection(特性检测)来判断浏览器的能力。

screen 对象

screen 对象是 JavaScript 中的一个内置对象,它提供了关于用户屏幕(显示器)的信息。通过 screen 对象,你可以获取用户屏幕的宽度、高度、色彩深度等信息,以便进行页面布局和显示适配。

以下是一些常见的 screen 对象属性:

  • screen.width:返回屏幕的宽度,以像素为单位。
  • screen.height:返回屏幕的高度,以像素为单位。
  • screen.availWidth:返回屏幕的可用宽度,不包括任务栏或其他系统界面的宽度
  • screen.availHeight:返回屏幕的可用高度,不包括任务栏或其他系统界面的高度。
  • screen.colorDepth:返回屏幕的色彩深度,表示每个像素所使用的位数。
  • screen.pixelDepth:与 screen.colorDepth 相同,返回屏幕的色彩深度。

通过这些属性,你可以根据用户屏幕的大小和分辨率,动态地调整网页的布局、字体大小、图片大小等,以适应不同的屏幕尺寸和设备。

console.log('屏幕宽度:', screen.width);
console.log('屏幕高度:', screen.height);
console.log('可用宽度:', screen.availWidth);
console.log('可用高度:', screen.availHeight);
console.log('色彩深度:', screen.colorDepth);
VM44:1 屏幕宽度: 1920
VM44:2 屏幕高度: 1200
VM44:3 可用宽度: 1857
VM44:4 可用高度: 1200
VM44:5 色彩深度: 24

需要注意的是,由于不同设备和浏览器可能会存在差异,使用 screen 对象属性时应谨慎,并考虑使用特性检测来适应不同的屏幕尺寸和分辨率。

location 对象

location 对象是 JavaScript 中的一个内置对象,它提供了与当前窗口的 URL 相关的信息和方法。通过 location 对象,你可以获取、设置和操作当前窗口的 URL,以及与 URL 相关的信息,如主机名、路径、查询参数等。

以下是一些常见的 location 对象属性和方法:

  • location.href:获取或设置完整的 URL 地址。
  • location.protocol:获取当前页面的协议(如 http: 或 https:)。
  • location.host:获取当前页面的主机名和端口。
  • location.hostname:获取当前页面的主机名。
  • location.port:获取当前页面的端口号。
  • location.pathname:获取当前页面的路径部分。
  • location.search:获取当前页面的查询参数部分(问号后的内容)。
  • location.hash:获取当前页面的锚点部分(#后的内容)。
  • location.assign(url):加载新的 URL
  • location.replace(url):加载新的 URL,但不生成历史记录。
  • location.reload():重新加载当前页面。
console.log('完整 URL:', location.href);
console.log('协议:', location.protocol);
console.log('主机名:', location.hostname);
console.log('端口:', location.port);
console.log('路径:', location.pathname);
console.log('查询参数:', location.search);
console.log('锚点:', location.hash);

VM100:1 完整 URL: https://www.bing.com/?mkt=zh-CN
VM100:2 协议: https:
VM100:3 主机名: www.bing.com
VM100:4 端口: 
VM100:5 路径: /
VM100:6 查询参数: ?mkt=zh-CN
VM100:7 锚点: 

通过 location 对象,你可以实现页面的重定向、刷新、获取 URL 参数等操作。需要注意的是,由于安全性限制,通过 JavaScript 修改 location 对象的某些属性(如 href)可能会触发浏览器的导航,因此要谨慎使用.

history 对象

history 对象是 JavaScript 中的一个内置对象,它提供了与浏览器历史记录相关的方法和属性。通过 history 对象,你可以访问和操作用户在浏览器中访问过的页面记录,以及在浏览器历史记录中进行导航

以下是一些常见的 history 对象方法和属性:

  • history.length:返回浏览器历史记录中的页面数量。
  • history.forward():向前导航到下一个页面(相当于用户点击浏览器的前进按钮)。
  • history.back() 或 history.go(-1):后退导航到上一个页面(相当于用户点击浏览器的后退按钮)。
  • history.go(n):导航到指定的相对位置,正数前进,负数后退。
  • history.pushState(state, title, url):在浏览器历史记录中添加一个新的状态(条目),并改变当前 URL。
  • history.replaceState(state, title, url):替换当前的历史记录状态,并改变当前 URL。

通过这些方法,你可以实现在不刷新页面的情况下改变 URL,以及通过浏览器的前进和后退按钮导航历史记录。需要注意的是,history 对象的能力可能会受到浏览器安全性限制,特别是对于跨域的情况。

console.log('历史记录长度:', history.length);

// 后退一步
history.back();

// 前进一步
history.forward();

// 向前导航两步
history.go(2);

// 添加新的历史记录状态并改变 URL
history.pushState({ page: 'home' }, 'Home Page', '/home');

// 替换当前历史记录状态并改变 URL
history.replaceState({ page: 'about' }, 'About Page', '/about');

使用 history 对象可以在浏览器历史记录中进行导航和操作,以实现更灵活的用户体验。

event 对象

event 对象是 JavaScript 中的一个内置对象,它在事件发生时被创建,包含了与事件相关的信息,如事件类型、目标元素、鼠标位置等。通过 event 对象,你可以获取有关事件的详细信息,并对事件进行处理和操作。

在事件处理函数中,通常会将 event 对象作为参数传递,从而可以访问事件的相关信息。

以下是一些常见的 event 对象属性和方法:

  • event.type:返回事件的类型,例如 ‘click’、‘keydown’ 等。
  • event.target 或 event.srcElement:返回触发事件的元素。
  • event.clientX 和 event.clientY:返回鼠标相对于浏览器窗口的水平和垂直坐标。
  • event.pageX 和 event.pageY:返回鼠标相对于文档页面的水平和垂直坐标。
  • event.keyCode 或 event.which:返回键盘按键的 Unicode 编码值。
  • event.preventDefault():阻止事件的默认行为。
  • event.stopPropagation() 或 event.cancelBubble:阻止事件在 DOM 树中的传播。
  • event.currentTarget:返回当前正在处理事件的元素。
  • event.preventDefault():阻止事件的默认行为。
  • event.stopPropagation():阻止事件的传播。

示例代码:

document.getElementById('myButton').addEventListener('click', function(event) {
  console.log('事件类型:', event.type);
  console.log('触发元素:', event.target);
  console.log('鼠标坐标:', event.clientX, event.clientY);
  console.log('键盘按键:', event.keyCode);
  event.preventDefault(); // 阻止默认行为
  event.stopPropagation(); // 阻止事件传播
});

通过使用 event 对象,你可以根据事件的类型和触发元素,执行特定的操作或者进行事件处理。需要注意的是,某些属性和方法可能会因浏览器的不同而有所差异,因此在使用时最好进行跨浏览器兼容性处理。

第9章 客户端检测

客户端检测是一种在 JavaScript 中判断用户浏览器、设备、操作系统等环境特性的方法。这种方法通常是为了在不同的环境中提供特定的功能、样式或行为,以达到更好的用户体验。然而,客户端检测应该谨慎使用,因为它可能导致代码复杂性增加以及不稳定的兼容性问题

特性检测

特性检测是一种判断浏览器是否支持某个特定功能或属性的方法,它是处理浏览器兼容性的推荐方式之一。与传统的客户端检测相比,特性检测更加稳定和可靠,因为它关注的是浏览器是否支持具体的功能,而不是根据浏览器名称或版本来进行判断。

以下是使用特性检测的一些示例:

  • 判断属性是否存在:
if ('localStorage' in window) {
  // 浏览器支持 localStorage
}
  • 判断方法是否可调用:
if (typeof document.querySelector === 'function') {
  // 浏览器支持 querySelector 方法
}

  • 判断 API 是否可用:
if (typeof navigator.geolocation !== 'undefined') {
  // 浏览器支持地理位置 API
}

  • 判断 CSS 属性是否可用:
var div = document.createElement('div');
if ('flex' in div.style) {
  // 浏览器支持 flex 布局
}

总之,特性检测是处理浏览器兼容性问题的首选方法,可以帮助开发人员编写更具健壮性和可维护性的代码。

怪癖检测

“怪癖检测”(quirks detection)是一种用于检测浏览器特定的怪异行为或兼容性问题的方法。它主要针对一些老旧的浏览器,这些浏览器可能会在处理一些标准或规范中存在歧义的情况下表现出不同的行为。

如果你需要处理特定浏览器的怪癖,建议首先尝试使用特性检测,只有在特定问题确实需要怪癖检测时才考虑采用。同时,最好在可能的情况下,鼓励用户升级到现代的、符合标准的浏览器,以提供更好的用户体验。

用户代理检测

用户代理检测是一种通过检查浏览器的用户代理字符串来确定用户正在使用的浏览器、操作系统、设备等信息的方法。用户代理字符串是浏览器在发送请求时包含在 HTTP 头部中的一个字符串,它通常包含有关浏览器和操作系统的信息。

虽然用户代理检测可以用于识别用户的浏览器环境,但它也存在一些问题和不确定性,因为用户代理字符串可以被修改,而且某些浏览器可能提供虚假的用户代理信息

var userAgent = navigator.userAgent;

if (userAgent.indexOf('Chrome') !== -1) {
  // 用户正在使用 Chrome 浏览器
}

if (userAgent.indexOf('Safari') !== -1) {
  // 用户正在使用 Safari 浏览器
}

if (userAgent.indexOf('MSIE') !== -1 || userAgent.indexOf('Trident') !== -1) {
  // 用户正在使用 Internet Explorer 浏览器
}

在处理浏览器兼容性问题时,推荐使用特性检测和逐渐增强的策略,而不是依赖于用户代理检测。特性检测更稳定,能够准确地判断浏览器是否支持某个功能,而不需要关心浏览器的具体类型和版本。同时,采用逐渐增强的方法,可以确保页面在不同浏览器中都能够提供基本的功能和良好的用户体验。

第10章 DOM

第11章 DOM2和DOM3

第12章 事件

第13章 表单脚本

第14章 错误处理与调试

第15章 JavaScript与XML

第16章 E4X

第17章 Ajax与JSON

第18章 高级技巧

第19章 客户端存储

第20章 最佳实践

第21章 未来API

第22章 JavaScript的未来

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

淡水瑜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值