JavaScript 重点补全
✅ 数据类型
typeof
:一元运算符- 检测基本数据类型和函数类型
- 无法区分
null
和undefined
,typeof null
返回"object"
- 通常较快,因为它是内置的类型检查机制
instanceof
:二元运算符- 检测对象是否为特定构造函数的实例
- 不能检测基本数据类型,只适用于引用类型
- 可能较慢,因为涉及原型链的搜索
Object.protptype.toString.call()
:方法- 检测所有数据类型
- 可以区分基本数据类型和引用类型
- 可能较慢,因为涉及内部
[[Class]]
属性的获取
分类 | 类型 | 说明 | 示例 | typeof | instanceof | Object.prototype.toString.call() |
---|---|---|---|---|---|---|
基本数据类型 | undefined | 未赋值 |
var a;
|
undefined | [object Undefined] | |
string | 字符串 |
var a = 'hello';
|
string | [object String] | ||
number | 数字 |
var a = 1;
|
number | [object Number] | ||
boolean | 布尔值 |
var a = true;
|
boolean | [object Boolean] | ||
null | 空值 |
var a = null;
|
null | [object Null] | ||
symbol | 符号(ES6) |
var a = Symbol();
|
symbol | [object Symbol] | ||
bigint | 大整数(ES2020) |
var a = 1n;
|
bigint | [object BigInt] | ||
引用数据类型 | object | 对象 |
var a = {};
|
object | Object | [object Object] |
array | 数组 |
var a = [];
|
object | Array | [object Array] | |
function | 函数 |
var a = function(){};
|
function | Function | [object Function] | |
date | 日期 |
var a = new Date();
|
object | Date | [object Date] | |
regexp | 正则 |
var a = /d{6}/g;
|
object | RegExp | [object RegExp] | |
自定义数据类型 | my | 自定义类 |
class My {};
|
object | My | [object Object] |
✅ 引用类型数据的内置方法
Object
-
Object.assign(target, source1, source2, ...)
:复制所有可枚举属性的值从一个或多个源对象到目标对象var Alex = { name: "Alex", age: 20 }; var Bob = { name: "Bob", age: 20, gender: "male" }; var result = Object.assign(Bob, Alex); console.log(result); // { name: 'Alex', age: 20, gender: 'male' }
-
Object.create(proto)
:创建一个新对象,使用指定的proto
对象作为原型var proto = { func: function () { console.log(this.value); }, }; var obj = Object.create(proto); obj.value = "hello world"; obj.func(); // hello world
-
Object.entries(obj)
:返回一个给定对象自身的所有可枚举属性的键值对数组var Alex = { name: "Alex", age: 20 }; console.log(Object.entries(Alex)); // [ [ 'name', 'Alex' ], [ 'age', 20 ] ]
-
Object.fromEntries(iterable)
:将可迭代的键值对数组或Map
对象转换为一个新的对象var a = [['a', 'b'], [1, 2]] console.log(Object.fromEntries(a)); // { '1': 2, a: 'b' }
-
Object.keys(obj)
:返回一个给定对象自身的所有可枚举属性键的数组var Alex = { name: "Alex", age: 20 }; console.log(Object.keys(Alex)); // [ 'name', 'age' ]
-
Object.values(obj)
:返回一个给定对象自身的所有可枚举属性值的数组var Alex = { name: "Alex", age: 20 }; console.log(Object.values(Alex)); // [ 'Alex', 20 ]
-
Object.freeze(obj)
:冻结一个对象的属性,使得属性不可配置,也不可写 -
Object.isFrozen(obj)
:判断一个对象是否被冻结var obj = { a: 1 }; Object.freeze(obj); obj.a = 2; console.log(obj); // { a: 1 } obj.b = 2; console.log(obj); // { a: 1 } console.log(Object.isFrozen(obj)); // true
-
Object.seal(obj)
:防止对象添加新属性或修改现有属性的可配置性 -
Object.isSealed(obj)
:判断一个对象是否被密封var obj = { a: 1 }; Object.seal(obj); obj.a = 2; console.log(obj); // { a: 2 } obj.b = 2; console.log(obj); // { a: 2 } console.log(Object.isSealed(obj)); // true
-
Object.defineProperty(obj, prop, descriptor)
:定义一个对象的新属性或修改现有属性的特性var obj = {}; Object.defineProperty(obj, 'a', { value: 1, writable: false, // 可写性 enumerable: true, // 可枚举性 configurable: false, // 可配置性 }); console.log(obj); // { a: 1 } obj.a = 2; console.log(obj); // { a: 1 }
-
Object.defineProperties(obj, props)
:定义一个对象的多个新属性或修改现有属性的特性var obj = {}; Object.defineProperties(obj, { a: { value: 1, writable: false, }, b: { value: 2, writable: true, }, }); console.log(obj.a, obj.b); // 1 2 obj.a = 2; obj.b = 3; console.log(obj.a, obj.b); // 1 3
-
Object.is(value1, value2)
:比较两个值是否相同,类似于===
操作符console.log(Object.is(123, 123)); // true console.log(Object.is(123, "123")); // false
Array
基本操作方法
-
concat(Array)
:连接两个或多个数组,返回一个新数组var a = [1, 2 ,3]; var b = [4, 5, 6]; var c = a.concat(b); console.log(c); // [ 1, 2, 3, 4, 5, 6 ]
-
copyWithin(target[, start[, end]])
:在数组内,将指定位置的成员复制到其他位置,并返回修改后的数组target
:从该位置开始替换数据;如果为负数,则表示倒数(下同)start
:从该位置开始读取数据,默认为 0end
:到该位置前停止读取数据(不包括该位置),默认为this.length
var a = [0, 1, 2, 3, 4, 5]; a.copyWithin(3, 0, 3); console.log(a); // [ 0, 1, 2, 0, 1, 2 ]
-
entries()
:返回数组的可迭代对象,包含数组中每个索引的键值对var a = ["a", "b", "c"]; for (var [key, value] of a.entries()) console.log(key, value); /* * 0 a * 1 b * 2 c */
-
fill(value[, start[, end]])
:填充,用一个固定值填充数组中从开始索引到结束索引内的全部元素var arr1 = new Array(5); arr1.fill(0); console.log(arr1); // [ 0, 0, 0, 0, 0 ] var arr2 = [1, 1, 1, 1]; arr2.fill(0, 1, 3); console.log(arr2); // [ 1, 0, 0, 1 ]
-
filter(fn)
:过滤器,创建一个新数组,包含通过所提供函数实现的测试的所有元素var a = [1, 2, 3]; console.log(a.filter((value) => value >= 2)); // [ 2, 3 ]
-
find(fn)
:查找值,返回数组中满足提供的测试函数的第一个元素的值var a = [1, 2, 3]; console.log(a.find((value) => value >= 2)); // 2
-
findIndex(fn)
:查找索引,返回数组中满足提供的测试函数的第一个元素的索引var a = [1, 2, 3]; console.log(a.findIndex((value) => value >= 2)); // 1
-
forEach(fn)
:遍历,对数组的每个元素执行一次给定的函数var a = [1, 2, 3]; a.forEach((value) => console.log(value)); /* * 1 * 2 * 3 */
-
includes(item)
:判断一个数组是否包含一个指定的值var a = [1, 2, 3]; console.log(a.includes(2), a.includes(4)); // true false
-
indexOf(item)
:返回在数组中可以找到一个给定元素的第一个索引var a = [1, 1, 1]; console.log(a.indexOf(1)); // 0
-
join(separator)
:将数组的所有元素连接为字符串var a = [1, 2, 3]; console.log(a.join(',')); // 1,2,3
-
keys()
:返回数组的可迭代对象,包含原始数组的键var a = ["a", "b", "c"]; for (let key of a.keys()) console.log(key); /* * 0 * 1 * 2 */
-
lastIndexOf(item)
:返回在数组中可以找到一个给定元素的最后一个索引var a = [1, 1, 1]; console.log(a.lastIndexOf(1)); // 2
-
map((currentValue[, index[, array]]) => {}[, thisArg])
:映射,创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值var a = ['a', 'b', 'c']; var b = a.map((value, index) => value + ' ' + index); console.log(b); // [ 'a 0', 'b 1', 'c 2' ]
-
pop()
:尾出栈,删除数组的最后一个元素并返回该元素的值var a = [1, 2, 3]; console.log(a.pop()); // 3 console.log(a); // [ 1, 2 ]
-
push(...items)
:尾压栈,向数组的末尾添加一个或多元素,并返回数组新的长度var a = [0, 1]; console.log(a.push(2)); // 3 console.log(a); // [ 0, 1, 2 ]
-
reduce((accumulator, currentValue[, index[, array]]) => {}[, initialValue])
:将数组元素计算为一个值(从左到右)var a = ["a", "b", "c"]; console.log(a.reduce((acc, cur) => acc + cur, "")); // abc
-
reduceRight((accumulator, currentValue[, index[, array]]) => {}[, initialValue])
:将数组元素计算为一个值(从右到左)var a = ["a", "b", "c"]; console.log(a.reduceRight((acc, cur) => acc + cur, "")); // cba
-
reverse()
:反转数组的元素顺序var a = [1, 2, 3]; console.log(a.reverse()); // [ 3, 2, 1 ]
-
shift()
:头出栈,删除并返回数组的第一个元素var a = [1, 2, 3]; console.log(a.shift()); // 1 console.log(a); // [ 2, 3 ]
-
slice([start[, end]])
:切片,选取数组的一部分,并返回一个新数组var a = [1, 2, 3]; console.log(a.slice(1, 3)); // [ 2, 3 ]
-
some(fn)
:检测数组元素中是否有元素符合指定条件var a = [1, 2, 3]; console.log(a.some((value) => value < 3)); // true console.log(a.some((value) => value > 3)); // false
-
sort(fn)
:对数组的元素进行排序var a = [2, 1, 3]; console.log(a.sort((a, b) => a - b)); // [ 1, 2, 3 ] console.log(a.sort((a, b) => b - a)); // [ 3, 2, 1 ]
-
splice(start, deleteCount[, item])
:从数组中添加或删除元素var a = [1, 2, 3]; a.splice(1, 1, 1); // [ 1, 1, 3 ] console.log(a);
-
toLocaleString()
:返回一个字符串表示数组中的元素var a = [1000, 2, 3]; console.log(a.toLocaleString()); // 1,000,2,3
-
toString()
:返回一个字符串,表示指定的数组及其元素var a = [1000, 2, 3]; console.log(a.toString()); // 1000,2,3
-
unshift(...items)
:头压栈,向数组的开头添加一个或多元素,并返回新的长度var a = [2, 3]; console.log(a.unshift(1)); // 3 console.log(a); // [ 1, 2, 3 ]
-
values()
:返回一个新的 Array Iterator 对象,该对象包含数组每个索引的值var a = ["a", "b", "c"]; for (let value of a.values()) console.log(value); /* * a * b * c */
静态方法
-
Array.from(arrayLike[, mapFn[, thisArg]])
:从类似数组或可迭代对象创建一个新的数组实例console.log( Array.from({ 1: "a", 0: "b", 2: "c", length: 3, }) ); // [ 'b', 'a', 'c' ] console.log(Array.from("abc")); // [ 'a', 'b', 'c' ] console.log(Array.from([1, 2, 3], (x) => x + x)); // [ 2, 4, 6 ]
-
Array.isArray()
:检查一个值是否为数组console.log(Array.isArray([1, 2, 3])); // true console.log(Array.isArray([])); // true console.log(Array.isArray(new Array())); // true console.log(Array.isArray(Array.prototype)); // true console.log(Array.isArray("abc")); // false console.log(Array.isArray(null)); // false console.log(Array.isArray(undefined)); // false
-
Array.of()
:创建一个具有可变数量参数的新数组实例console.log(Array.of(undefined, null, 1, "a")); // [ undefined, null, 1, 'a' ]
案例:数组去重
-
使用双重循环
var array = [1, 2, 2, 3, 4, 4, 5]; for (var i = 0; i < array.length; i++) { for (var j = i + 1; j < array.length; j++) { if (array[i] === array[j]) { array.splice(j, 1); j--; } } } console.log(array);
-
使用
Set
集合数据结构var array = [1, 2, 2, 3, 4, 4, 5]; var result = [...new Set(array)]; console.log(result);
-
使用
Map
哈希数据结构var array = [ { name: "Alex", age: 20 }, { name: "Bob", age: 25 }, { name: "Charlie", age: 20 }, ]; let map = new Map(); array.forEach((item) => { if (!map.has(item.age)) { map.set(item.age, [item]); } else { map.get(item.age).push(item); } }); let result = {}; for (let [key, value] of map) { result[key] = value; } console.log(result);
-
使用
filter
方法var array = [ { name: "Alex", age: 20 }, { name: "Bob", age: 25 }, { name: "Charlie", age: 20 }, ]; var result = array.filter((item, index, self) => { return self.findIndex((temp) => temp.name === item.name) === index; }); console.log(result);
-
使用
reduce
方法var array = [ { name: "Alex", age: 20 }, { name: "Bob", age: 25 }, { name: "Charlie", age: 20 }, ]; var result = array.reduce(function (acc, curr) { if (!acc[curr.age]) acc[curr.age] = []; acc[curr.age].push(curr); return acc; }, {}); console.log(result);
Date
getDate()
: 返回月份中的第几天(从 1 到 31)getDay()
: 返回星期几(0 表示周日,1 表示周一)getFullYear()
: 返回年份getHours()
: 返回小时(从 0 到 23 )getMilliseconds()
: 返回毫秒(0 到 999)getMinutes()
: 返回分钟(从 0 到 59)getMonth()
: 返回月份(从 0 到 11)getSeconds()
: 返回秒数(从 0 到 59)getTime()
: 返回自1970年1月1日午夜以来的毫秒数getTimezoneOffset()
: 返回UTC时间与本地时间之间的时差,以分钟为单位now()
: 返回自 1970 年 1 月 1 日午夜以来的毫秒数parse()
: 解析日期字符串并返回自 1970 年 1 月 1 日以来的毫秒数setDate()
: 设置月份中的某一天setFullYear()
: 设置日期对象的年份setHours()
: 设置日期对象的小时setMilliseconds()
: 设置日期对象的毫秒数setMinutes()
: 设置日期对象的分钟数setMonth()
: 设置日期对象的月份setSeconds()
: 设置日期对象的秒数setTime()
: 将日期设置为 1970 年 1 月 1 日之后(之前)的指定毫秒数toDateString()
: 将日期对象的日期部分转换为可读字符串toISOString()
: 使用 ISO 标准将日期作为字符串返回toJSON()
: 以字符串形式返回日期,格式为 JSON 日期toLocaleDateString()
: 使用区域设置约定将Date
对象的日期部分作为字符串返回toLocaleTimeString()
: 使用区域设置约定将Date
对象的时间部分作为字符串返回toLocaleString()
: 使用区域设置约定将Date
对象转换为字符串toString()
: 将Date
对象转换为字符串toTimeString()
: 将Date
对象的时间部分转换为字符串valueOf()
: 返回Date
对象的原始值
RegExp
-
compile()
:(ES6 开始废弃)重新编译正则表达式 -
exec()
:执行正则表达式的搜索,返回第一个匹配结果的数组var a = 123 console.log(/^\d+$/.exec(a)); // [ '123', index: 0, input: '123', groups: undefined ]
-
test()
:执行正则表达式的搜索,返回一个布尔值,指示是否找到匹配var a = 123 var b = "123" console.log (/^\d+$/.test(a)) // true console.log (/^\d+$/.test(b)) // true
-
toString()
:返回表示正则表达式的字符串console.log(/^\d+$/.toString()); // /^\d+$/
-
flags
:返回正则表达式的标志(修饰符)的字符串 -
source
:返回正则表达式的模式文本 -
lastIndex
:设置或返回正则表达式下次匹配的起始索引console.log(/^\d+$/g.flags); // g console.log(/^\d+$/g.source); // ^\d+$ console.log(/^\d+$/g.lastIndex); // 0
✅ 事件对象
event.preventDefault()
:阻止事件的默认行为event.stopPropagation()
:阻止事件继续在 DOM 树上冒泡或捕获,使事件不会传播到其他层级的元素event.stopImmediatePropagation()
:阻止同一元素上的其他事件处理程序被触发,即使它们在事件队列中等待执行event.target
:返回触发事件的元素,即事件的目标event.currentTarget
:返回绑定事件处理程序的元素,在事件处理程序内部,this
关键字通常指向event.currentTarget
event.timeStamp
:返回事件被触发的时间戳event.isTrusted
:返回一个布尔值,指示事件是否由用户而非脚本触发event.type
:返回事件的类型,如click
、keypress
等event.bubbles
:返回一个布尔值,指示事件是否会冒泡event.cancelable
:返回一个布尔值,指示事件是否可以被取消event.eventPhase
:返回一个整数,表示事件当前处于事件流的哪个阶段(捕获、目标、冒泡)
✅ 原型(proto)与原型链
-
原型分隐式原型与显示原型
var array = new Array(); var implicit = array.__proto__; // 隐式原型 var explicit = Array.prototype; // 显式原型 console.log(implicit, explicit); console.log(implicit === explicit); // true
- 此时,类实例的隐式原型全等于类的显式原型
- 隐式原型指向显式原型
-
原型链是指原型中嵌套着原型
var array = new Array(); console.log(array.__proto__); console.log(array.__proto__.__proto__);
-
可以通过
hasOwnProperty()
方法查看当前实例本身是否具有某个属性,而非去原型链上寻找class Calc { constructor(x, y) { this.x = x; this.y = y; } } var calc = new Calc(1, 2); console.log(calc.hasOwnProperty('x')); // true console.log(calc.hasOwnProperty('z')); // false
✅ 函数
函数类型
-
具名函数:通过
function
关键字声明的,具有函数名,可以在其作用域内的任何地方调用function func() {}
-
匿名函数:通常赋值给变量或者作为参数传递给其他函数
var func = function () {};
-
箭头函数:ES6 引入的一种新的函数定义方式,提供了更简洁的语法,并且在处理
this
时有所不同var func = () => {};
-
生成器函数:可以暂停执行并在之后恢复执行,通过
function*
关键字定义,并使用yield
关键字返回值function* generator() { yield 1; yield 2; yield 3; } let g = generator(); console.log(g.next().value); // 1 console.log(g.next().value); // 2 console.log(g.next().value); // 3
-
构造函数:用于创建对象实例,通常首字母大写,并通过
new
关键字调用function Person(name, age) { this.name = name; this.age = age; } var person = new Person("Alex", 20); console.log(person.name, person.age); // Alex 20
-
立即执行函数:定义后立即执行,常用于创建私有作用域或避免全局命名空间污染
(function (name) { console.log(name); // Alex })("Alex");
-
回调函数:回调函数是作为参数传递给另一个函数的函数,通常在某个异步操作完成后被调用
function func(callback) { callback("func"); } func((value) => console.log(value)); // func
-
闭包函数:闭包是指有权访问另一个函数作用域中变量的函数,通常由一个内部函数引用外部函数的变量形成
function outer(outerValue) { return function inner(innerValue) { console.log("outerValue:", outerValue); // outerValue: out console.log("innerValue:", innerValue); // innerValue: in }; } outer("out")("in");
箭头函数与普通函数的区别
箭头函数 | 普通函数 | |
---|---|---|
this 绑定 |
不绑定,捕获上下文的 this 值 |
动态绑定,取决于函数的调用方式 |
arguments 对象 |
没有自身的 arguments 对象可以使用剩余参数语法 ...args |
有自身的 arguments 对象包含函数调用时传入的所有参数 |
构造函数 | 不可用,不支持 new |
可以,支持 new |
返回值 | 单个表达式时可以直接返回表达式结果 | 必须使用 return 返回 |
super ,new.target |
不支持 | 支持 |
this.arguments |
不支持 | 支持 |
特点 | 快速、简洁 | 复杂,多场景适用 |
闭包
-
闭包 = 内层函数 + 引用外层函数的变量
-
闭包不一定要有
return
-
当外层函数需要适使用闭包中的变量时,需要
return
-
闭包应用:实现数据私有(举例:计数)
function func() { let count = 0; return function closure() { count++; console.log("count", count); }; } const res = func(); res(); // count 1 res(); // count 2
- 节流与防抖函数
- Vue 和 React 的 Hooks 钩子函数
-
-
闭包不一定有内存泄漏
- 在上述计数示例中,
count
变量在closure()
中被引用,无法被作为垃圾回收,从而引起内存泄漏 - 并非所有内存泄漏都需要手动回收
- 在上述计数示例中,
✅ 作用域
全局作用域
- 可以被局部作用域遮蔽
- 在浏览器环境中,全局作用域中的变量通常会成为
window
对象的属性
局部作用域
- 变量或函数只能在特定的代码块内部被访问
块级作用域
- ES6 引入
- 允许在代码块内部声明变量和函数,这些变量和函数的作用域被限制在相应的代码块内
- 通过
let
和const
关键字实现
作用域链
- 用于变量解析的机制
- 当在函数内部尝试访问一个变量时,如果该变量在当前函数的局部作用域中没有找到,解释器会沿着作用域链向上查找,直至找到该变量或达到全局作用域
- 确保了变量的私有性和封装性,防止了变量名称的冲突
变量提升
-
变量和函数声明在代码执行之前就被移至其作用域的顶部,但变量的初始化操作保留在原位置
-
表现为:在变量声明之前引用了变量,会得到
undefined
的值,而非报错function func() { console.log(a); // undefined var a = 1; console.log(a); // 1 console.log(b); // 报错:ReferenceError let b = 2; console.log(b); // 2 console.log(c); // 报错:ReferenceError const c = 3; console.log(c); // 3 } func();
-End-