JavaScript
Javascript Basis
basic data type
- ES5 => Null ES2020 => ??
typeof null == "object" 是因为堆中的变量地址是以 64 位的二进制进行存储,其中二进制开头数字为 000 的变量存储的变量类型是对象。null 被看作全是零的空。
ES2020 新增 Null 判断运算符,当变量的值是 null 或 undefined 时,不再需要 || 指定默认值,而是可以通过判断运算符 ??。当 Null 判断运算符左侧的值为 null 或 undefined 成立时,才会返回右侧的值。
- ES6 => Symbol
Symbol 是 ES6 新增基本数据类型,Symbol() 函数会返回 symbol 类型的值,可用作消除魔法字符与充当对象属性或私有方法。
魔法字符 => 与业务逻辑代码无关却形成强耦合的字符串字面量或数值
- ES10 => BigInt
BigInt 是 JS 中新加入的基本数据类型,支持比 Number 更大范围的整数值,以解决大整数运算时的溢出与精度问题,常见应用于时间戳和 ID,以及 bignumber 库的替代。
可以用在一个整数字面量后面加 n 的方式定义 BigInt,或者调用函数 BigInt(),并传递一个整数值或字符串值。
Number 精度安全范围
=> -9007199254740991(-(2^53-1)) - 9007199254740991(2^53-1)
最大安全整数常量 => Number.MAX_SAFE_INTEGER
最小安全整数常量 => Number.MIN_SAFE_INTEGER
Web API
- Event 事件
Event 接口表示在 DOM 中出现的事件。Event.target 表示触发事件对象的引用,可实现事件委托。
事件委托即不必为每个元素分配各自的处理程序,而是将单个处理程序放在其共同的祖先上。
冒泡和捕获 => 当事件发生在元素上时,会首先运行该元素上的处理程序,然后会运行其父元素上的处理程序,并一直向上到其他祖先的处理程序;事件发生前会首先通过祖先链向下到达元素。
DOM 事件传播阶段 => 捕获、目标、冒泡阶段
event.target => 引发事件的元素;event.currentTarget 同 this 指向 => 处理事件的当前元素;event.eventPhase => 当前阶段。
// event.eventPhase
capturing=1; target=2; bubbling=3
capture 选项若为默认 false,表示在冒泡阶段设置处理程序;若为 true,则表示在捕获阶段设置处理程序。
EventTarget.addEventListener(type, listener, useCapture)
Traversal methods
Iterable & Traversal
- 迭代通常指按照顺序反复执行一段程序多次,且会有明确的中止条件 7
Iterable object 可迭代对象是数组的泛化,简单来说是指任何可在 for..of 循环中使用的对象。ES5 新增的 Array.prototype.foreach() 虽然是通用迭代的尝试,但是存在数据结构的制约和中止迭代的标志。ES6 新增迭代器模式,实现 iterable 接口。
- 遍历通常指是访问数据结构的所有元素
loop traversal method
-
for (statement 1; statement 2; statement 3){} => 循环代码块一定的次数
-
For ... in
For ... in 遍历对象以及其原型链上的可枚举属性;若用于遍历数组,除遍历其元素外,还会遍历数组对象自定义的可枚举属性以及其原型链上的可枚举属性;遍历对象返回的属性名和遍历数组返回的索引都是字符串类型;遍历枚举属性的顺序取决于 JS 引擎,先以升序枚举数值键,然后以插入顺序枚举字符串和符号键。8.2.4
let areas = {
"24": "CS",
"30": "CA",
"23": "SZ",
"22": "TX",
"ok": "yes, ok",
"okk": "yes, okk"
};
for(let area in areas) {
alert(area); // 22, 23, 24, 30, ok, okk
}
- Object.keys
Object.keys(obj) 方法会返回一个由给定对象自身可枚举属性组成的数组;与 For ... in 相比,不会遍历对象原型链上的属性;在遍历数组时,数组中属性名的排列顺序与 For...in 循环遍历时返回的顺序一致。
function Person() { this.name = 'okk'; }
Person.prototype.getName = function() { return this.name; }
var person = new Person();
Object.defineProperty(person, 'age', {
writable: true, configurable: true,enumerable: true, value: 22
});
console.log(Object.keys(person)); // ['name', 'age']
for(item in person){console.log(item)}; // name age getName
- For ... of => ES6
For ... of 通常是在可迭代对象上创建迭代循环,调用自定义迭代钩子,为每个不同属性的值执行语句。直接支持 Array、Map、Set、String、arguments 对象等。
可迭代对象的原型链上已实现 Symbol.iterator 方法,普通对象 Object 由于没有默认的迭代行为,所有不支持迭代。
// 1. 不会遍历到对象属性及其原型属性
Array.prototype.getLength = function() { return this.length; };
let arr = ['a', 'b', 'c'];
arr.name = 'Ok';
Object.defineProperty(arr, 'prop', {
enumerable: true, value: 22, writable: true, configurable: true
});
for(let i of arr) { console.log(i); } // a,b,c
// 2. 与 Object.keys 配合遍历对象
let person = { name: 'Ok', age: 22, area: 'tx' }
for(var key of Object.keys(person)) { console.log(person[key]); } // ok 22 tx
// 3. 配合 entries 输出数组索引和值/对象的键值
let arr = ['a', 'b', 'c'];
for(let [index, value] of Object.entries(arr)) { console.log(index, ':', value); } // 0:a, 1:b, 2:c
let obj = {name: 'Ok', age: 22, city: 'tx'};
for(let [key, value] of Object.entries(obj)) { console.log(key, ':', value); } // name:Ok, age:22, city:tx
- 迭代器与生成器 => ES6
迭代器模式描述的方案是实现 iterable 接口(可迭代协议)的可迭代对象,能通过迭代器 iterator 进行消费。可迭代协议的要求在 ES 中具体通过以 Symbol.iterator 作为键的属性来实现。这个默认迭代器属性必须引用一个迭代器工厂函数,且调用这个工厂函数必须返回一个新的迭代器。也就是说迭代器提供一种方法,使得在无需暴露数据结构内部表示的情况下,能够按照一定顺序访问数据结构的元素。7.2
迭代器协议要求迭代器是一个对象。next() 方法在可迭代对象中遍历数据,且每次成功调用会返回一个 IteratorResult 对象。IteratorResult 迭代器对象包含 done 和 value 属性,分别表示是否还可以调用 next() 函数以及迭代器返回的下一个值。
class ExplicitIterator { // 显式迭代器实现 => 实现可迭代接口 iterable
[Symbol.iterator](){ return { next(){ // 调用默认迭代器工厂函数返回实现迭代器接口的迭代器对象
return { done: false, value: 'ExplicitIterator' }
} } }
}
let e = new ExplicitIterator();
console.log(e[Symbol.iterator]()); // {next: ƒ}
生成器具有在代码块内暂停和恢复代码执行的能力,形式上是函数,在函数名前加星号 * 表示,星号不受到两侧空格影响。调用生成器函数会产生一个实现 Iterator 接口的生成器对象。生成器对象开始时处于暂停执行状态,next() 方法的调用可以让生成器开始或恢复执行。next() 方法的返回值类似迭代器,具有标志位和返回值属性。生成器函数只会在初次调用 next() 方法时执行,直接调用不生效。
function * generatorFn() {}
let g = generatorFn();
console.log(g, g.next()); // generatorFn{<closed>} {value: undefined, done: true}
-
函数体为空的生成器函数中间不会停留,调用一次 next() 方法就会让生成器到达 done: true 的状态。
-
value 属性是生成器状态的返回值,默认为 undefined,可以通过生成器函数的返回值决定。
function * generatorFn01() {}
function * generatorFn02() { return 'generatorFn02' }
let g01 = generatorFn01(), g02 = generatorFn02();
console.log(g01.next(), g02.next()); // {value: undefined, done: true} {value: 'generatorFn02', done: true}
生成器函数在遇到 yield 关键字前会正常执行,当遇见这个关键字后执行停止,停止执行的生成器函数只能通过在生成器对象上调用 next() 方法恢复执行。经 yield 关键字退出的生成器函数会处于 done: false 状态,通过 return 关键字退出的生成器函数处于 done: true 状态。next() 方法返回的对象里会具有 yield 生成的值。
// ES6 斐波那契数列 => F0=0, F1=1, Fn=Fn-1+Fn-2
let fibonacci = {
[Symbol.iterator]() {
let pre = 0, cur = 1
return {
next () {
[ pre, cur ] = [ cur, pre + cur ]
return { done: false, value: cur }
}
}
}
}
for (let n of fibonacci) {
if (n > 1000) break
console.log(n);
}
for...of 与 for...in 的比较 => MDN
- 两者都是进行迭代的方式。
- for...in 语句以任意顺序迭代对象的可枚举属性。for...of 语句遍历可迭代对象定义要迭代的数据。
BOM Browser Object Model
相较于 JavaScript、DOM 有 ECMA、W3C 标准,不同浏览器之间的 BOM 存在较大差异,且具有兼容性问题。
The Window Object
window 是浏览器窗口的实例,所有全局 JavaScript 对象、函数和变量都自动成为窗口对象的成员。
-
window.onload & window.DOMContentLoaded
- onload 事件触发时,页面上所有的 DOM、CSS、JS... 都加载完毕
- DOMContentLoaded 事件触发时,仅当 DOM 加载完成
-
window 对象属性和方法
属性 or 方法 | 描述 |
---|---|
window.screen | Returns a reference to the screen object associated with the window |
window.screenX、window.screenY | 返回浏览器左边界到操作系统桌面左边界的水平距离、返回浏览器顶部距离系统桌面顶部的垂直距离 |
window.innerWidth、window.innerHeight | 返回浏览器窗口的内容区域的宽度、高度(包含水平、横向滚动条) |
window.outerWidth、window.outerHeight | 返回浏览器窗口的外部宽度、高度(包含工具栏、水平、横向滚动条、开发者面板) |
window.pageXOffset、window.pageYOffset => 别名 window.scrollX、window.scrollY | 返回文档/页面水平、垂直方向滚动的像素值 |
window.scrollTo(x-coord, y-coord ) | 滚动到文档中的某个坐标 |
window.open(URL, name, specs, replace) | 打开新的浏览器 => window.open("https://www.google.com", "_self", "width=200,height=100", true) |
- 系统对话框 => alert() 警告框、confirm() 确认框、prompt() 提示框
The Location Object
location 提供其链接到的对象的位置 URL。
- 既是 window 的属性,也是 document 的属性
// 不需要大写成 Window 和 Location => 构造函数
console.log(window.location === document.location) // true
- https://???.com:123/ok/?usr=zs#frontend => location 对象内容
属性 | 值 | 说明 |
---|---|---|
location.hash | #frontend | URL 散列值、没有则为空字符串 |
location.host | ???.com:123 | 主机名和端口 |
location.hostname | ???.com | 主机名 |
location.href | https://???.com:2366/ok/?usr=zs#frontend | 完整的 URL |
location.pathname | /ok/ | 路径或文件名 |
location.port | 123 | 端口号 |
location.protocal | https: | 协议 |
location.search | ?usr=zs | 查询字符串 |
location.origin | https://???:123 | URL 源地址、只读 |
- 函数封装 => 获取 URL 参数
// default => window.location.search = "?usr=zs&age=23";
const getQueryVariable = function (variable) {
let params = window.location.search.length > 0 ? window.location.search.substring(1) : "", args = {};
for (let item of params.split("&").map(item => item.split("="))) {
let key = decodeURIComponent(item[0]), value = decodeURIComponent(item[1]);
if (key == variable) return value
}
return false
};
getQueryVariable("usr");
getQueryVariable("age");
- URLSearchParams 实例对象提供检查、修改查询字符串的 API
// default => window.location.search = "?usr=zs&age=23";
let searchParams = new URLSearchParams(window.location.search);
console.log(searchParams.toString(), searchParams.has("usr"), searchParams.get("age")); // usr=zs&age=23 true 23
- location 方法
方法 => Location 没有继承任何方法,但实现了来自 URLUtils 的方法 | 描述 |
---|---|
location.assign() | 加载给定 URL 的内容资源到这个 Location 对象所关联的对象上 => 新页面会被保存在会话的历史 History 中 |
location.replace() | 用给定的 URL 替换掉当前的资源 => 新页面不会被保存在会话的历史 History 中 |
location.toString() | 返回一个 DOMString,包含整个 URL => 和读取 URLUtils.href 的效果相同 |
location.reload() | 重新加载来自当前 URL 的资源 |
Navigator & screen & History
Navigator 接口表示用户代理的状态和标识;Screen 接口表示一个屏幕窗口,往往指的是当前正在被渲染的 window 对象;History 接口表示会话历史记录。
- navigator.geolocation
navigator.geolocation.getCurrentPosition(position => console.log(position.coords.latitude, position.coords.longitude));
- history.go() 和 history.forward()&history.back()
history.go() 沿任意方向导航,整数参数表示前进或后退多少步;history.back() 与 history.forward() 中参数无效。
REGEXP
JavaScript 中的正则表达式是对象,是用于匹配字符串中字符组合的模式。这些模式被用于 RegExp 的 exec 和 test 方法,及 String 的 match、matchAll、replace、search 和 split 方法。
使用字面量创建的正则表达式对象会在代码编译时生成,使用构造函数生成的正则对象会在代码运行时生成。当正则表达式保持不变时,前者能取得更好的性能。
var regexp = /IcedAmericano/g, new Regexp("IcedAmericano","g");
修饰符(标记)位于表达式外,用于额外的匹配策略。元字符用于匹配正则表达式上下文中的任意匹配字符。
/simple characters 简单字符 + metacharacter 元字符/flags 修饰符
调用 exec 方法返回的数组具有两个属性 => input - 整个原待匹配的字符串、index - 整个模式匹配成功的开始位置。
let reg2 = new RegExp('zs','m'), test2 = 'okokokokokokok', test22 = 'zsokokokokokzs';
console.log(reg2.exec(test2), reg2.exec(test22)); // null, [ 'zs', index: 0, input: 'zsxzyxxxzzzyyyzs', groups: undefined ]
若正则表达式包含圆括号,则返回的数组中会包含多个元素。首先是整个匹配成功的结果,随后是圆括号匹配的内容,当有多个圆括号时,匹配成功的结果都会成为数组元素。
let reg3 = /^_(x)_(y)$/, test3 = '_x_y';
console.log(reg3.exec(test3), reg3.exec(''), kong.exec(test3)); // [ '_x_y', 'x', 'y', index: 0, input: '_x_y', groups: undefined ] 空字符串返回 null 空字符串正则匹配exec返回 [ '', index: 0, input: '_x_y', groups: undefined ]
$` => 正则表达式匹配范围之前的字符串内容,$& => 正则表达式匹配的内容,$' => 正则表达式匹配范围之后的字符串内容。$n => RegExp 对象的索引 -> 通常使用括号包裹。更多点此。
console.log('abc'.replace('b',"[$`-$&-$']")); // a[a-b-c]c
console.log('hello world'.replace(/(\w+)\s(\w+)/,"$2 $1")); // world hello
当 replace 的首个参数是整个匹配的内容时,第二个参数是组匹配。
let field = { "pos_1": "dc", "pos_2": "fullstack", "pos_3": "hardware" }, replaceInfo = '<span id="pos_1"></span><span id="pos_2"></span><span id="pos_3"></span>'.replace(/(<span id=")(\w+)(">)(<\/span>)/g, function(match, $1, $2, $3,$4) { return $1+$2+$3+field[$2]+$4 });
console.log(replaceInfo); // <span id="pos_1">dc</span><span id="pos_2">fullstack</span><span id="pos_3">hardware</span>
Check It Out
class Hello {
constructor(name) {
this.name = name;
}
sayHello() {
console.log('Hello I am ', this.name);
}
afterHello() {
setTimeout(function() {
console.log('Hi!, I am', this.name)
});
}
}
let ia = new Hello('Okk');
ia.sayHello();
ia.afterHello();
上述代码中,setTimeout
函数里的回调函数是在全局作用域中执行的,而不是在 afterHello()
函数的作用域中执行。因此,当回调函数尝试访问 this.name
时,this 指向的是全局作用域中的对象,而不是 afterHello()
函数中的对象。
为了解决这个问题,可以使用 .bind(this)
方法将当前上下文绑定到回调函数中。这将确保在回调函数中使用 this 时,它指向 afterHello()
函数中的对象。同样地,将回调函数改为箭头函数也是一种解决方法,因为箭头函数不会创建自己的 this,而是会使用被创建时所处的词法作用域中的 this。即上述代码中会使用 laterHello()
函数中的 this,而不是在 setTimeout
函数中的 this。
结束
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!