script元素有8个属性 常用的async defer src type
async 和 defer 是用于异步加载 JavaScript 脚本的两个属性,它们可以在 <script> 标签中使用。
-
async属性:- 当浏览器解析到带有
async属性的<script>标签时,会开始异步加载脚本,并继续解析文档的同时。 - 异步加载的脚本不会阻塞文档的解析和其他资源的加载。
- 脚本加载完成后,立即执行,不会按照它们在文档中的顺序执行。
- 适用于独立的、不依赖其他脚本的脚本文件。
- 当浏览器解析到带有
<script src="script.js" async></script>
-
defer属性:- 当浏览器解析到带有
defer属性的<script>标签时,会开始异步加载脚本,并继续解析文档的同时。 - 异步加载的脚本不会阻塞文档的解析和其他资源的加载。
- 设置 defer 属性,相当于告诉浏览器立即下载,但延迟执行
- 脚本会按照它们在文档中的顺序执行,即在 DOM 解析完成后、
DOMContentLoaded事件触发之前执行。 - 适用于需要在 DOM 解析完成之后才执行的脚本,可以访问和操作 DOM 元素。
- HTML5 规范要求脚本应该按照它们出现的顺序执行,因此第一个推迟的脚 本会在第二个推迟的脚本之前执行,而且两者都会在 DOMContentLoaded 事件之前执行
- 当浏览器解析到带有
<script src="script.js" defer></script>
总结:
async属性用于异步加载脚本,加载完成后立即执行,不阻塞文档解析;defer属性用于异步加载脚本,按照它们在文档中的顺序执行,执行时机在 DOM 解析完成之后。
注意:
async和defer属性只适用于外部脚本,即通过src属性指定脚本文件的情况。- 如果没有指定
async或defer属性,则脚本会同步加载和执行,会阻塞文档的解析。
标签位置
过去,所有 这种做法的主要目的是把外部的 CSS 和 JavaScript 文件都集中放到一起。不过,把所有 JavaScript 文件都放在里,也就意味着必须把所有 JavaScript 代码都下载、解析和解释完成后,才能开始渲 染页面(页面在浏览器解析到的起始标签时开始渲染)。对于需要很多 JavaScript 的页面,这会 导致页面渲染的明显延迟,在此期间浏览器窗口完全空白。为解决这个问题,现代 Web 应用程序通常 将所有 JavaScript 引用放在元素中的页面内容后面,
var let const
function foo() { console.log(age); var age = 26; } foo(); // undefined
之所以不会报错,是因为 ECMAScript 运行时把它看成等价于如下代码:
function foo() { var age; console.log(age); age = 26; } foo(); // undefined
这就是所谓的“提升”(hoist),也就是把所有变量声明都拉到函数作用域的顶部。
let 声明的范围是块作用域, 而 var 声明的范围是函数作用域。
if (true) { var name = 'Matt'; console.log(name); // Matt } console.log(name); // Matt
if (true) { let age = 26; console.log(age); // 26 } console.log(age); // ReferenceError: age
没有定义 在这里,age 变量之所以不能在 if 块外部被引用,是因为它的作用域仅限于该块内部
let
- 暂时性死区
- 全局声明
- 条件声明
- for中的let声明
数据类型
ECMAScript 有 6 种简单数据类型(也称为原始类型):Undefined、Null、Boolean、Number、 String 和 Symbol。Symbol(符号)是 ECMAScript 6 新增的。还有一种复杂数据类型叫 Object(对 象) 调用typeof null 返回的是"object"。这是因为特殊值 null 被认为是一个对空对象的引用 ECMAScript 变量可以包含两种不同类型的数据:原始值和引用值。原始值(primitive value)就是 最简单的数据,引用值(reference value)则是由多个值构成的对象。
执行上下文与作用域
变量或函数的上下文决定 了它们可以访问哪些数据,以及它们的行为。每个上下文都有一个关联的变量对象(variable object), 而这个上下文中定义的所有变量和函数都存在于这个对象上。虽然无法通过代码访问变量对象,但后台 处理数据会用到它
垃圾回收
确定哪个变量不会再 使用,然后释放它占用的内存。这个过程是周期性的,即垃圾回收程序每隔一定时间(或者说在代码执 行过程中某个预定的收集时间)就会自动运行。垃圾回收过程是一个近似且不完美的方案,是否还有用,属于“不可判定的”问题,意味着靠算法是解决不了的。
1.标记清理
2.引用计数(存在问题)
内存管理:
如果数据不再必要,那么把它设置为 null,从而释放其引用。这也可以叫 作解除引用。这个建议最适合全局变量和全局对象的属性 隐藏类:解决方案就是避免 JavaScript 的“先创建再补充”(ready-fire-aim)式的动态属性赋值,并在 构造函数中一次性声明所有属性,最佳实践是把不想要的属性设置为 null。这样可以保持隐藏类不变 和继续共享,同时也能达到删除引用值供垃圾回收程序回收的效果
3.内存泄漏
意外声明全局变量是最常见但也最容易修复的内存泄漏问题。下面的代码没有使用任何关键字声明 变量: function setName() { name = 'Jake'; } 此时,解释器会把变量 name 当作 window 的属性来创建(相当于 window.name = 'Jake')。 可想而知,在 window 对象上创建的属性,只要 window 本身不被清理就不会消失。这个问题很容易 解决,只要在变量声明前头加上 var、let 或 const 关键字即可,这样变量就会在函数执行完毕后离 开作用域。
还有两种方式:一种是定时器中的回调通过闭包引用外部变量 导致的泄露 另一种是闭包 返回了函数应用了变量(闭包会导致内存泄露)
原始值的包装类型
Boolean、Number 和 String。每当用 到某个原始值的方法或属性时,后台都会创建一个相应原始包装类型的对象,从而暴露出操作原始值的 各种方法。来看下面的例子: let s1 = "some text"; let s2 = s1.substring(2);
(1) 创建一个 String 类型的实例 (2) 调用实例上的特定方法; (3) 销毁实例。
注意,使用 new 调用原始值包装类型的构造函数,与调用同名的转型函数并不一样。例如:
let value = "25"; let number = Number(value); // 转型函数
console.log(typeof number); // "number"
let obj = new Number(value); // 构造函数
console.log(typeof obj); // "object"
regExp
exec():
没有设置全局标记,因此调用 exec()只返回第一个匹配项("cat")。lastIndex 在非全局模式下始终不变。 如果在这个模式上设置了 g 标记,则每次调用 exec()都会在字符串中向前搜索下一个匹配项
字符串
字符串迭代与解构 字符串的原型上暴露了一个@@iterator 方法,表示可以迭代字符串的每个字符。可以像下面这样 手动使用迭代器:
let message = "abc"; let stringIterator = message[Symbol.iterator](); console.log(stringIterator.next()); // {value: "a", done: false} console.log(stringIterator.next()); // {value: "b", done: false} console.log(stringIterator.next()); // {value: "c", done: false} console.log(stringIterator.next()); // {value: undefined, done: true}
在 for-of 循环中可以通过这个迭代器按序访问每个字符:
for (const c of "abcde") { console.log(c); } // a // b // c // d // e
有了这个迭代器之后,字符串就可以通过解构操作符来解构了。比如,可以更方便地把字符串分割 为字符数组:
let message = "abcde"; console.log([...message]); // ["a", "b", "c", "d", "e"]
Global
Global 对象是 ECMAScript 中最特别的对象,因为代码不会显式地访问它。ECMA-262 规定 Global 对象为一种兜底对象,它所针对的是不属于任何对象的属性和方法。事实上,不存在全局变量或全局函 数这种东西。在全局作用域中定义的变量和函数都会变成 Global 对象的属性 。本书前面介绍的函数, 包括 isNaN()、isFinite()、parseInt()和 parseFloat(),实际上都是 Global 对象的方法。
uri编码
ecnodeURI()方法用于对整个 URI 进行编码,比如"www.wrox.com/illegal value.js"。而 encodeURIComponent()方法用于编码 URI 中单独的组件,比如前面 URL 中的"illegal value.js"。
这两个方法的主要区别是,encodeURI()不会编码属于 URL 组件的特殊字符,比如冒号、斜杠、问号、 井号,而 encodeURIComponent()会编码它发现的所有非标准字符。来看下面的例子:
let uri = "http://www.wrox.com/illegal value.js#start";
// "http://www.wrox.com/illegal%20value.js#start"
console.log(encodeURI(uri));
// "http%3A%2F%2Fwww.wrox.com%2Fillegal%20value.js%23start"
console.log(encodeURIComponent(uri));
这里使用 encodeURI()编码后,除空格被替换为%20 之外,没有任何变化。而 encodeURIComponent()方法将所有非字母字符都替换成了相应的编码形式。这就是使用 encodeURI()编码整个 URI,但只使用 encodeURIComponent()编码那些会追加到已有 URI 后面的字符串的原因。
注意 一般来说,使用 encodeURIComponent()应该比使用 encodeURI()的频率更高, 这是因为编码查询字符串参数比编码基准 URI 的次数更多。
与 encodeURI()和 encodeURIComponent()相对的是 decodeURI()和 decodeURIComponent()。 decodeURI()只对使用 encodeURI()编码过的字符解码. 类似地,decodeURIComponent()解码所有 被 encodeURIComponent()编码的字符,基本上就是解码所有特殊值。
eval()
最后一个方法可能是整个 ECMAScript 语言中最强大的了,它就是 eval()。这个方法就是一个完 整的 ECMAScript 解释器,它接收一个参数,即一个要执行的 ECMAScript(JavaScript)字符串
Math
舍入方法
舍入方法 接下来是用于把小数值舍入为整数的 4 个方法:Math.ceil()、Math.floor()、Math.round() 和 Math.fround()。这几个方法处理舍入的方式如下所述。 Math.ceil()方法始终向上舍入为最接近的整数。Math.floor()方法始终向下舍入为最接近的整数。 Math.round()方法执行四舍五入。 Math.fround()方法返回数值最接近的单精度(32 位)浮点值表示。
random
Math.random()方法返回一个 0~1 范围内的随机数,其中包含 0 但不包含 1。
function selectFrom(lowerValue, upperValue) {
let choices = upperValue - lowerValue + 1;
return Math.floor(Math.random() * choices + lowerValue);
}
let num = selectFrom(2,10);
console.log(num); // 2~10 范围内的值,其中包含 2 和 10
数组
- from
- of
- length
比较函数sort(): 比较函数接收两个参数,如果第一个参数应该排在第二个参数前面,就返回负值;如果两个参数相 等,就返回 0;如果第一个参数应该排在第二个参数后面,就返回正值。下面是使用简单比较函数的一 个例子:
function compare(value1, value2) {
if (value1 < value2) { return -1; }
else if (value1 > value2){ return 1; }
else { return 0; }
}
这个比较函数可以适用于大多数数据类型,可以把它当作参数传给 sort()方法,如下所示:
let values = [0, 1, 5, 10, 15];
values.sort(compare);
alert(values); // 0,1,5,10,15
降序:
function compare(value1, value2) {
if (value1 < value2) { return 1; }
else if (value1 > value2){ return -1; }
else { return 0; }
}
重点:如果第一个参数应该排在第二个参数前面,就返回负值;如果两个参数相 等,就返回 0;如果第一个参数应该排在第二个参数后面,就返回正值
weakMap()
“弱映射”(WeakMap)是一种新的集合类型,为这门语言带来了增强的键/ 值对存储机制。WeakMap 是 Map 的“兄弟”类型,其 API 也是 Map 的子集。WeakMap 中的“weak”(弱), 描述的是 JavaScript 垃圾回收程序对待“弱映射”中键的方式。 弱映射中的键只能是 Object 或者继承自 Object 的类型,尝试使用非对象设置键会抛出 TypeError。值的类型没有限制。
迭代
实现 Iterable 接口(可迭代协议)要求同时具备两种能力:支持迭代的自我识别能力和创建实现 Iterator 接口的对象的能力。在 ECMAScript 中,这意味着必须暴露一个属性作为“默认迭代器”,而 且这个属性必须使用特殊的 Symbol.iterator 作为键。这个默认迭代器属性必须引用一个迭代器工厂 函数,调用这个工厂函数必须返回一个新迭代器。 很多内置类型都实现了 Iterable 接口: 字符串 数组 映射 集合 arguments 对象 NodeList 等 DOM 集合类型
生成器
生成器是 ECMAScript 6 新增的一个极为灵活的结构,拥有在一个函数块内暂停和恢复代码执行的 能力。生成器的形式是一个函数,函数名称前面加一个星号(*)表示它是一个生成器。标识生成器函数的星号不受两侧空格的影响:
// 生成器函数声明 function* generatorFn() {}
// 生成器函数表达式 let generatorFn = function* () {} 调用生成器函数会产生一个生成器对象。生成器对象一开始处于暂停执行(suspended)的状态。与 迭代器相似,生成器对象也实现了 Iterator 接口,因此具有 next()方法。调用这个方法会让生成器 开始或恢复执行 next()方法的返回值类似于迭代器,有一个 done 属性和一个 value 属性。函数体为空的生成器 函数中间不会停留,调用一次 next()就会让生成器到达 done: true 状态。
yield 关键字可以让生成器停止和开始执行,也是生成器最有用的地方。生成器函数在遇到 yield 关键字之前会正常执行。遇到这个关键字后,执行会停止,函数作用域的状态会被保留。停止执行的生 成器函数只能通过在生成器对象上调用 next()方法来恢复执行:
此时的yield 关键字有点像函数的中间返回语句,它生成的值会出现在 next()方法返回的对象里。 通过 yield 关键字退出的生成器函数会处在 done: false 状态;通过 return 关键字退出的生成器函 数会处于 done: true 状态。
对象和类
属性:
在调用 Object.defineProperty()时,configurable、enumerable 和 writable 的值如果不 指定,则都默认为 false。
设置属性值:
获取属性值:
代理
函数
参数: ECMAScript 函数既不关心传入的参数个数,也不 关心这些参数的数据类型。定义函数时要接收两个参数,并不意味着调用时就传两个参数。你可以传一 个、三个,甚至一个也不传,解释器都不会报错。 之所以会这样,主要是因为 ECMAScript 函数的参数在内部表现为一个数组。函数被调用时总会接 收一个数组,但函数并不关心这个数组中包含什么。如果数组中什么也没有,那没问题;如果数组的元 素超出了要求,那也没问题。事实上,在使用 function 关键字定义(非箭头)函数时,可以在函数内 部访问 arguments 对象,从中取得传进来的每个参数值。 arguments 对象是一个类数组对象(但不是 Array 的实例),因此可以使用中括号语法访问其中的 元素(第一个参数是 arguments[0],第二个参数是 arguments[1])。而要确定传进来多少个参数, 可以访问 arguments.length 属性
在参数中可以使用拓展运算符
arguments: arguments 对象其实还有一个 callee 属性,是一个指向 arguments 对象所在函数的 指针。 this:定义在全局上下文中的函数 sayColor()引用了 this 对象。这个 this 到底引用哪个对象必须到 函数被调用时才能确定。如果在全局函数中调用,则 this 在非严格模式下等于 window,在严 格模式下等于 undefined。如果作为某个对象的方法调用,则 this 等于这个对象。匿名函数在这种情 况下不会绑定到某个对象,这就意味着 this 会指向 window,除非在严格模式下 this 是 undefined
this 和 arguments 都是不能直接在内部函数中访问的。如果想访问包含作用域中 的 arguments 对象,则同样需要将其引用先保存到闭包能访问的另一个变量中。
ECMAScript 6 新增了检测函数是否使用 new 关键字调用的 new.target 属性。如果函数是正常调用的,则 new.target 的值是 undefined;如果是使用 new 关键字调用的,则 new.target 将引用被调用的 构造函数。
length 属性保存函数定义的命名参数的个数
使用 call()或 apply()的好处是可以将任意对象设置为任意函数的作用域,这样对象可以不用关心方法。在前面例子最初的版本中,为切换上下文需要先把 sayColor()直接赋值为 o 的属性,然后再 调用。ECMAScript 5 出于同样的目的定义了一个新方法:bind()。bind()方法会创建一个新的函数实例, 其 this 值会被绑定到传给 bind()的对象。
代码在严格模式下执行; 外部函数的返回值是对尾调用函数的调用; 尾调用函数返回后不需要执行额外的逻辑; 尾调用函数不是引用外部函数作用域中自由变量的闭包。
闭包
匿名函数经常被人误认为是闭包(closure)。闭包指的是那些引用了另一个函数作用域中变量的函数,通常是在嵌套函数中实现的。 因为闭包会保留它们包含函数的作用域,所以比其他函数更占用内存。过度使用闭 包可能导致内存过度占用,因此建议仅在十分必要时使用。
易混:
(object.getIdentity = object.getIdentity)(); // 'The Window'
在 JavaScript 中,当一个函数被赋值给一个新的变量或属性时,它会丢失原始绑定的对象,并且默认绑定的 this 值将指向全局对象(在浏览器环境下为 window 对象)。
所以,当执行 (object.getIdentity = object.getIdentity)(); 时,object.getIdentity 丢失了原始绑定的 object 对象,this 默认绑定到全局对象 window,因此返回的值为全局对象的 identity 属性值,即 'The Window'。
解除引用,防止内存泄露:
function assignHandler() {
let element = document.getElementById('someElement');
let id = element.id;
element.onclick = () => console.log(id);
element = null;
}
立即调用的匿名函数
立即调用的匿名函数又被称作立即调用的函数表达式(IIFE,Immediately Invoked Function Expression)。它类似于函数声明,但由于被包含在括号中,所以会被解释为函数表达式。紧跟在第一组 括号后面的第二组括号会立即调用前面的函数表达式。 使用 IIFE 可以模拟块级作用域,即在一个函数表达式内部声明变量,然后立即调用这个函数。这 样位于函数体作用域的变量就像是在块级作用域中一样。ECMAScript 5 尚未支持块级作用域,使用 IIFE 模拟块级作用域是相当普遍的。
私有变量
function Person(name) {
this.getName = function() { return name; };
this.setName = function (value) { name = value; };
}
let person = new Person('Nicholas');
console.log(person.getName()); // 'Nicholas'
person.setName('Greg');
console.log(person.getName()); // 'Greg'
期约与异步
同步行为对应内存中顺序执行的处理器指令。每条指令都会严格按照它们出现的顺序来执行,而每 条指令执行后也能立即获得存储在系统本地(如寄存器或系统内存)的信息。这样的执行流程容易分析 程序在执行到代码任意位置时的状态(比如变量的值)。
异步操作经常是必 要的,因为强制进程等待一个长时间的操作通常是不可行的(同步操作则必须要等)异步代码不容易推断。
回调
BOM
top 对象始终指向最上层(最外层)窗口,即浏览器窗口本身。而 parent 对象则始终指向当前窗 口的父窗口。如果当前窗口是最上层窗口,则 parent 等于 top(都等于 window)。最上层的 window 如果不是通过 window.open()打开的,那么其 name 属性就不会包含值.还有一个 self 对象,它是终极 window 属性,始终会指向 window。实际上,self 和 window 就 是同一个对象。之所以还要暴露 self,就是为了和 top、parent 保持一致。 这些属性都是 window 对象的属性,因此访问 window.parent、window.top 和 window.self 都可以。这意味着可以把访问多个窗口的 window 对象串联起来,比如 window.parent.parent。
在嵌套的 <frame> 或 <iframe> 中,top 和 parent 的定义如下:
top表示最顶层的窗口(最外层的父窗口),即整个窗口层次结构的最上层窗口。parent表示当前窗口的直接父窗口,即当前窗口所嵌套在的父窗口。
具体来说,如果你有一个窗口(称为窗口 A),其中包含一个 <iframe>(称为窗口 B),而窗口 B 又包含了另一个 <iframe>(称为窗口 C),那么在窗口 C 中:
top将引用窗口 A,因为窗口 A 是整个窗口层次结构的最顶层窗口。parent将引用窗口 B,因为窗口 B 是窗口 C 的直接父窗口。
以下是一个示意图,说明了嵌套的窗口层次结构和 top、parent 的关系:
窗口 A (top)
└── 窗口 B (parent)
└── 窗口 C
在窗口 C 中,通过 window.top 可以访问窗口 A,而通过 window.parent 可以访问窗口 B。
需要注意的是,top 和 parent 都是 window 对象的属性,可以通过它们来访问上层或父级窗口的属性和方法。同时,跨域的限制和同源策略也会适用于这些窗口之间的通信和操作。
窗口位置
screenLeft 和 screenTop 属性,用于表示窗口相对于屏幕左侧和顶部的位置 ,返回值的单位是 CSS 像素。moveTo()和 moveBy()方法移动窗口。 window.devicePixelRatio 实际上与每英寸像素数(DPI,dots per inch)是对应的。DPI 表示单 位像素密度,而 window.devicePixelRatio 表示物理像素与逻辑像素之间的缩放系数
innerWidth、innerHeight、outerWidth 和 outerHeight。outerWidth 和 outerHeight 返回浏览器窗口自身的大小(不管是在最外层 window 上使用,还是在窗格中使用)。innerWidth 和 innerHeight 返回浏览器窗口中页面视口的大小(不包含浏览器边框和工具栏)
document.documentElement.clientHeight 和 window.innerHeight 返回的值通常是相等的。
document.documentElement.clientHeight是文档根元素(HTML 元素)的可视高度,也就是视口的高度。它表示当前浏览器窗口或容器中可见的部分的高度。window.innerHeight返回浏览器窗口的视口高度,即浏览器窗口可见区域的高度
度量文档相对于视口滚动距离的属性有两对,返回相等的值:window.pageXoffset/window. scrollX 和 window.pageYoffset/window.scrollY。当页面xy发生滚动时,会有不为零的值
window.open()方法可以用于导航到指定 URL,也可以用于打开新浏览器窗口。这个方法接收 4 个参数:要加载的 URL、目标窗口、特性字符串和表示新窗口在浏览器历史记录中是否替代当前加载页 面的布尔值。
window.open("http://www.wrox.com/",
"wroxWindow",
"height=400,width=400,top=10,left=10,resizable=yes");
返回一个对新建窗口的引用。
新创建窗口的 window 对象有一个属性 opener,指向打开它的窗口。这个属性只在弹出窗口的最 上层 window 对象(top)有定义,是指向调用 window.open()打开它的窗口或窗格的指针。例如:
let wroxWin = window.open("http://www.wrox.com/", "wroxWindow", "height=400,width=400,top=10,left=10,resizable=yes");
alert(wroxWin.opener === window); // true
wroxWin.opener = null; 把 opener 设置为 null 表示新打开的标签页不需要与打开它的标签页通信,因此可以在独立进程 中运行。这个连接一旦切断,就无法恢复了
定时器
所有超时执行的代码(函数)都会在全局作用域中的一个匿名函数中运行,因此函 数中的 this 值在非严格模式下始终指向 window,而在严格模式下是 undefined。如果 给 setTimeout()提供了一个箭头函数,那么 this 会保留为定义它时所在的词汇作用域。
setInterval(() => alert("Hello world!"), 10000);
注意 这里的关键点是,第二个参数,也就是间隔时间,指的是向队列添加新任务之前等待的时间。比如,调用 setInterval()的时间为 01:00:00,间隔时间为 3000 毫秒。这意 味着 01:00:03 时,浏览器会把任务添加到执行队列。浏览器不关心这个任务什么时候执行 或者执行要花多长时间。因此,到了 01:00:06,它会再向队列中添加一个任务。由此可看 出,执行时间短、非阻塞的回调函数比较适合 setInterval()。
setIntervale()在实践中很少会在 生产环境下使用,因为一个任务结束和下一个任务开始之间的时间间隔是无法保证的,有些循环定时任务可能会因此而被跳过。而像前面这个例子中一样使用 setTimeout()则能确保不会出现这种情况。一 般来说,最好不要使用 setInterval()。
loaction
提供了当前窗口中加载文档的信息,以及通常的导航功能。 这个对象独特的地方在于,它既是 window 的属性,也是 document 的属性。也就是说, window.location 和 document.location 指向同一个对象。
location.assign("www.wrox.com"); window.location = "www.wrox.com"; location.href = "www.wrox.com"; 在这 3 种修改浏览器地址的方法中,设置 location.href 是最常见的。
除了 hash 之外,只要修改 location 的一个属性,就会导致页面重新加载新 URL。 注意 修改 hash 的值会在浏览器历史中增加一条新记录。L 之后,浏览器历史记录中就会增加相应的记录。当用户单击“后退” 按钮时,就会导航到前一个页面。
如果不希望增加历史记录,可以使用 replace()方法。这个方法接 收一个 URL 参数,但重新加载后不会增加历史记录。调用 replace()之后,用户不能回到前一页。
reload(),它能重新加载当前显示的页面。调用 reload()而不传参 数,页面会以最有效的方式重新加载。也就是说,如果页面自上次请求以来没有修改过,浏览器可能会 从缓存中加载页面。如果想强制从服务器重新加载,可以像下面这样给 reload()传个 true
hashchange 会在页面 URL 的散列变化时被触发,开发者可以在此时执行某些操作。而状态管理 API 则可以让开发者改变浏览器 URL 而不会加载新页面。
DOM
hasChildNodes(),这个方法如果返回 true 则说明节点有一个或多个子节点
最常用的方法是 appendChild(),用于在 childNodes 列表末尾添加节点。
想把节点放到 childNodes 中的特定位置而不是末尾,则可以使用 insertBefore()方法。 这个方法接收两个参数:要插入的节点和参照节点。
replaceChild()方法接收两个参数:要插入的节点和要替换的节点。要替换的节点会被返回并从文档 树中完全移除,要插入的节点会取而代之。
要移除节点而不是替换节点,可以使用 removeChild()方法。这个方法接收一个参数,即要移除 的节点。被移除的节点会被返回
所有节点类型还共享了两个方法。第一个是 cloneNode(),会返回与调用它的节点一模一样的节 点。cloneNode()方法接收一个布尔值参数,表示是否深复制。在传入 true 参数时,会进行深复制, 即复制节点及其整个子 DOM 树。如果传入 false,则只会复制调用该方法的节点。复制返回的节点属 于文档所有,但尚未指定父节点,所以可称为孤儿节点(orphan)。可以通过 appendChild()、 insertBefore()或 replaceChild()方法把孤儿节点添加到文档中
document
// 读取文档标题
let originalTitle = document.title;
// 修改文档标题
document.title = "New page title";
// 取得完整的 URL
let url = document.URL;
// 取得域名
let domain = document.domain;
// 取得来源
let referrer = document.referrer;
getElementById()方法接收一个参数,即要获取元素的 ID,如果找到了则返回这个元素,如果 没找到则返回 null。
getElementsByTagName()是另一个常用来获取元素引用的方法。这个方法接收一个参数,即要 获取元素的标签名,返回包含零个或多个元素的 NodeList。
getElementsByName()方法最常用于单选按钮,因为同 一字段的单选按钮必须具有相同的 name 属性才能确保把正确的值发送给服务器
write()、 writeln()、open()和 close()。
element
getAttribute()主要用于取得自定义属性的值
看红宝书!
function loadStyles(url){
let link = document.createElement("link");
link.rel = "stylesheet";
link.type = "text/css";
link.href = url;
let head = document.getElementsByTagName("head")[0];
head.appendChild(link);
}
MutationObserver
MutationObserver 接口,可以在 DOM 被修改时异步执行回调。使 用 MutationObserver 可以观察整个文档、DOM 树的一部分,或某个元素。此外还可以观察元素属性、子节点、文本,或者前三者任意组合的变化。
let observer = new MutationObserver(() => console.log(' attributes changed'));
observer.observe(document.body, { attributes: true });