JavaScript - 基础 面试题总结
1. JS的数据类型有哪些,区别是什么?
- 基本数据类型:
number、string、boolean、undefined、null、symbol、bigint
;symbol
:创建对象的唯一属性名,因此可以用来防止属性名冲突,保证属性名的唯一;bigint
:大整数相加;
- 引用数据类型:
- Object、Array、Function;
- 按照对我的理解,要说引用数据类型,JS中只有一种引用数据类型,就是Object,JS中万物皆对象;
- 区别:
- 基本数据类型是存储在栈中的简单数据段,占据空间小,属于背被频繁使用的数据;
- 引用数据类型是存储在堆内存中,占据空间大,引用数据类型在栈中存储了地址,该地址指向堆中该实体的起始地址,通过检索栈中的地址,取得地址后在堆中获得实体;
2. undefined 和 null 的区别
- 数据类型不同;
- 意义不同:
- undefined:
- 声明变量未赋值;
- null:
- 声明了一个变量,并且赋值了,但赋的值是一个null;
- 表示准备用来保存对象,还没有真正保存对象的值,从逻辑角度看,null值表示一个空对象指针;
- undefined:
- 转数字结果不同(Number()):
- undefined:NaN;
- null:0;
- 产生场景不同:
- undefined:
- 声明变量未赋值;
- 数组没有某个元素;
- 对象没有某个属性;
- 函数没有返回值;
- 函数调用的时候没有传递参数并且声明函数的时候没有设置默认值;
- null:
- 作为原型链的终点;
- undefined:
js
null == undefined // true
null === undefined // false
3. 如何判断JS的数据类型?
typeof
:- 可以判断除null之外的基本数据类型;
- 可以判断函数;
instanceof
:- 依据:判断构造函数的原型是否出现在实例的原型链上;
- ❗ 注意:
-
不能判断基本数据类型;
jsconst num = 11; num instanceof Number; // false const str = '11'; str instanceof String; // false
-
可以判断引用数据类型;
jsconst num = new Number(); num instanceof Number; // true const str = new String(); str instanceof String; // true
-
使用方法得到的结果并不一定是可靠的,因为在
ES7
规范中可以通过自定义的Symbol.hasInstane
方法来覆盖默认行为;
-
Object.prototype.toString.call() / Object.prototype.toString.apply()
:- 需要让toString函数内部的this指向当前检测的变量;
- 返回值:
[object 类型]
- ❗ 注意:
- 该方法不能区分数字类型和数字对象类型,字符串类型和字符串对象类型,布尔类型和布尔对象类型;
- 数字类型:通过
const、let、var(❌)
声明得到得; - 数字对象类型:通过
new Number()
构造函数的得到的;
4. 创建对象的方式
-
简单对象的创建,使用对象字面量方式(
{}
)创建; -
Object构造函数;
jsconst obj = new Object(); obj.name = '禁止摆烂_才浅';
-
用函数来模拟Class;
-
方式一:
- 无参构造函数;
jsfunction Obj() { this.name = '禁止摆烂_才浅'; } const obj = new Obj(); console.log(obj.name); // 禁止摆烂_才浅
-
方式二:
- 有参构造函数;
jsfunction Obj(name, age) { this.name = name; this.age = age; } const obj = new Obj('禁止摆烂_才浅', 24); console.log(obj.anme); // 禁止摆烂_才浅
-
-
使用对象原型的关键字创建;
jsfunction Obj() {} Obj.prototype.name = '禁止摆烂_才浅'; const obj = new Obj(); // 访问的时候,首先去obj本身上查找,此时,obj本身上没有该属性,就会去obj的原型上查找 console.log(obj.name); // 禁止摆烂_才浅
5. 创建函数的方式
-
声明式函数;
jsfunction add(num1, num2) { return num1 + num2 }
-
函数表达式;
jsconst add = (num1, num2) => (num1 + num2);
-
构造函数(
new Function()
);jsconst add = new Function('num1', 'num2', 'return num1 + num2');
6. JS常用的内置对象,并列举该对象常用的方法
-
Number
➡ 数值对象:方法或属性 描述 Number.toFixed()
采用定点计数法格式化数字(保留几位小数) Number.toString()
将一个数字转换成字符串 Number.isInterger()
判断指定数字是否为整数 Number.MAX_VALUE
最大数值 Number.MIN_VALUE
最小数值 Number.NaN
特殊的非数字值(NaN属于数字) Number.NEGATIVE_INFINITY
负无穷大 Number.POSITIVE_INFINITY
正无穷达 Number.valueOf()
返回原始数值 -
String
➡ 字符串对象:方法或属性 描述 length
获取字符串的长度 substring()
截取字符串,返回指定部分的字符串 split()
将字符串分隔成数组 startsWith()
检测是否以某段字符开头 endsWith()
检测是否以某段字符结尾 includes()
判断一个字符串是否包含在另一个字符串中 replace()
替换字符串中指定的字符 toUpperCase()
将字符串转换为大写 toLowerCase()
将字符串转换为小写 -
Boolean
➡ 布尔对象:方法或属性 描述 Boolean.toString()
将布尔值转换成字符串 Boolean.valueOf()
Boolean对象的布尔值 -
Math
➡ 数学对象:方法或属性 描述 Math.PI
圆周率 Math.abs()
绝对值 Math.floor()
向下取整 Math.ceil()
向上取整 Math.round()
四舍五入 Math.random()
0~1的随机数 Math.pow(x, y)
求x的y次方 Math.sqrt()
求平方根 -
Array
➡ 数组:方法或属性 描述 length
数组的长度 shift()
删除数组的第一个元素,并返回被删除的元素,并将长度-1 pop()
删除数组的最后一个元素,返回被删除的元素,并将长度-1 unshift()
给数组的最前面插入一个或多个元素,返回插入元素之后的数组长度 push()
给数组的最后面添加一个或多个元素,返回添加元素之后的数组长度 splice()
插入、删除、替换数组的元素,以数组的形式返回被修改的内容 sort()
对数组元素进行排序,并返回排序好的数组 reverse()
将数组中元素的位置颠倒,并返回颠倒元素之后的数组 forEach()
遍历数组,无返回值 map()
迭代数组,遍历原数组,把原数组中的每一项数据加工改造,形成一个新数组返回 filter()
过滤原数组中的数据,把满足条件的数据放在一个新数组中返回 reduce()
累加器,返回函数累计处理的结果 find()
返回数组中满足条件的第一个元素 join()
数组转换成字符串,返回一个字符串 some()
判断数组中是否至少有一个元素满足条件,返回布尔值 every()
判断数组中的所有元素是否都满足条件,返回布尔值 concat()
合并数组,返回一个新数组 slice()
获取指定的元素,返回一个含有指定元素的数组 includes()
查看数组中是否有每一项数据,返回布尔值 indexOf()
正向查看数组里卖弄指定数据的索引 flat()
数组降维,返回一维数组 -
Object
➡ 基础对象:方法或属性 描述 Object.constructor
对象的构造函数 Object.keys()
将对象的所有属性放到数组中 Object.values()
将对象的所有属性值放到数组中 Object.hasOwnProperty()
检查属性是否被继承 Object.isPrototypeOf()
一个对象是否是另一个对象的原型 Object.propertyIsEnumerable()
是否可以通过 for/in 循环看到属性 Object.toLocaleString()
返回对象的本地字符串表示 Object.toString()
定义一个对象的字符串表示 Object.valueOf()
指定对象的原始值 -
Date
➡ 日期对象:方法或属性 描述 Date.getDate()
返回一个月中的某一天 Date.getDay()
返回一周中的某一天 Date.getFullYear()
返回 Date 对象的年份字段 Date.getHours()
返回 Date 对象的小时字段 Date.getMilliseconds()
返回 Date 对象的毫秒字段 Date.getMinutes()
返回 Date 对象的分钟字段 Date.getMonth()
返回 Date 对象的月份字段 Date.getSeconds()
返回 Date 对象的秒字段 Date.getTime()
返回 Date 对象的毫秒表示 -
RegExp
➡ 正则表达式对象:方法或属性 描述 RegExp.exec()
通用的匹配模式 RegExp.global
正则表达式是否全局匹配 RegExp.ignoreCase
正则表达式是否区分大小写 RegExp.lastIndex
下次匹配的起始位置 RegExp.source
正则表达式的文本 RegExp.test()
检测一个字符串是否匹配某个模式 RegExp.toString()
把正则表达式转换成字符串 -
Function
➡ 函数构造器:方法或属性 描述 Function.apply()
将函数作为一个对象的方法调用 Function.arguments[]
传递给函数的参数 Function.call()
将函数作为对象的方法调用 Function.caller
调用当前函数的函数 Function.length
已声明的参数的个数 Function.prototype
对象类的原型 Function.toString()
把函数转换成字符串 -
Arguments
➡ 函数参数集合:方法或属性 描述 Arguments[]
函数参数的数组 Arguments
一个函数的参数和其他属性 Arguments.callee
当前正在运行的函数 Arguments.length
传递给函数的参数个数 -
Error
➡ 异常对象:方法或属性 描述 Error.message
可以读取的错误消息 Error.name
错误的类型 Error.toString()
把 Error 对象转换成字符串 EvalError
不正确使用 eval()时抛出 SyntaxError
抛出该错误用来通知语法错误 RangeError
在数字超出合法范围时抛出 ReferenceError
在数字超出合法范围时抛出 TypeError
当一个值的类型错误时,抛出该异常 URIError
由 URl 的编码和解码方法抛出
7. == 和 === 的区别
==
(等值符):- 只比较值;
- 数据类型相同:
- 去比较两边的值是否相等;
- 数据类型不同:
- 会尝试自动转换为相同类型,能转换就比较值,转换不了就是
false
;
- 会尝试自动转换为相同类型,能转换就比较值,转换不了就是
===
(等同符):- 既要判断值相等,也要判断数据类型相同;
- 数据类型相同:
- 直接比较值是否相等;
- 数据类型不同:
- 直接返回false;
8. 如何区分数组和对象?
-
Array.isArray()
:jsArray.isArray([]); // true Array.isArray({}); // false
-
instanceof
:- 判断构造函数的原型是否出现在实例的原型链上;
js[] instanceof Array; // true {} instanceof Array; // false
-
constructor
:- 找到创建当前实例的构造函数;
js[].constructor; // Array {}.constructor; // Object
-
Object.prototype.toString.call()
:- 通过
call、apply
去改变this
的指向;
jsObject.prototype.toString.call([]); // '[object Array]' Object.prototype.toString.call({}); // '[object Object]'
- 通过
9. 多维数组降维
-
先使用 数组的
join()
方法将数组转换为字符串,再使用 字符串的split()
方法:jsconst arr = [[1, 2, 3], [4, 5, 6]]; const str = arr.join(); // 字符串 - 1, 2, 3, 4, 5, 6 const newArr = str.split(','); // ['1', '2', '3', '4', '5', '6']
-
使用
flat()
方法:- 可以给该方法传递参数,一种是传递当前数组的维数,还有一种就是直接传递
Infinity
(不管是几维数组,最后都会降为一维);
- 可以给该方法传递参数,一种是传递当前数组的维数,还有一种就是直接传递
-
声明一个新数组,循环旧数组,判断item是否为数组,如果是数组就使用展开运算符,否则直接push进新数组:
jsconst arr = [[222, 333, 444], [55, 66, 77]]; const newArr = []; arr.forEach(item => { Array.isArray(item) ? newArr.push(...item) : newArr.push(item); }); console.log(newArr); // [222, 333, 444, 55, 66, 77]
-
声明一个新数组,循环旧数组,判断item是否为数组,如果是数组就使用
concat()
方法,否则直接push进新数组:jsconst arr = [[222, 333, 444], [55, 66, 77]]; let newArr = []; arr.forEach(item => { if (Array.isArray(item)) return newArr = newArr.concat(item); newArr.push(item); }); console.log(newArr); // [222, 333, 444, 55, 66, 77]
-
递归;
10. 怎么判断两个对象相等?
-
ES6中有一个方法判断两个对象是否相等,这个方法判断的是两个对象引用覅之是否一致
jsconst obj1 = { a: 1 }; const obj2 = { a: 1 }; const obj3 = obj1; console.log(Object.is(obj1, obj2)); // false console.log(Object.is(obj1, obj3)); //true
- 当前需求是比较两个对象内容是否一致时就没用了;
-
想要比较两个对象内容是否一致,思路是要遍历对象的所有键名和键值是否都一致:
js
const obj1 = { a: 1, b: { c: 2 } };
ocnst obj2 = { b: { c: 3 }, a: 1 };
function isObjectValueEqual(a, b) {
// 判断两个对象是否指向同一内存,指向同一内存返回 true
if (a === b) return true;
// 获取两个对象键值数组
let aProps = Object.getOwnPropertyNames(a);
let bProps = Object.getOwnPropertyNames(b);
// 判断两个对象键值数组长度是否一致,不一致返回 false
if (aProps.length !== bProps.length) return false;
// 遍历对象的键值
for (let prop in a) {
// 判断 a 的键值,在 b 中是否存在,不存在,返回 false
if (b.hasOwnProperty(prop)) {
// 判断 a 的键值是否为对象,是则递归,不是对象直接判断键值是否相等,不相等返回false
if (typeof a[prop] === 'object') {
if (!isObjectValueEqual(a[prop], b[prop])) return false;
} else if (a[prop] !== b[prop]) {
return false;
}
} else {
return false;
}
}
return true;
}
console.log(isObjectValueEqual(obj1, obj2)); // false
11. 列举 强制转换类型 和 隐式类型转换
- 强制类型转换:
-
转数字:
Number()、parseInt()、parseFloat()
;
jsNumber(null); // 0 Number(''); // 0 Number(); // 0 parseInt('11.11'); // 11 parseInt('11s1.ss'); // 11 parseFloat('11.11'); // 11.11 parseFloat('11s1.ss'); // 11
-
转字符串:
String()、toString()
;
-
转布尔:
Boolean()
;
-
- 隐式类型转换:
+、-、*、/、%、==、>=、<=
;
12. JS中获取当前日期月份
js
const date = new Date();
console.log(`当前年份 - ${date.getFullYear()}`);
// 月份是从0开始的,当前月份 = 得到的月份 + 1
console.log(`当前月份 - ${date.getMonth() + 1}`);
console.log(`当前日期 - ${date.getDate()}`);
console.log(`当前周几 - ${date.getDay()}`);
console.log(`当前小时 - ${date.getHours()}`);
console.log(`当前分钟 - ${date.getMinutes()}`);
console.log(`当前秒 - ${date.getSeconds()}`);
13. 什么是伪数组,怎么转换为真数组
- 伪数组:
- 具有索引、长度、不能使用数组方法;
- 典型的伪数组:
document.children、document.childnodes、document.uerySelectorAll()、
;
- 转换为真数组:
Array.from();
;- 展开运算符;
- 声明一个新数组,循环伪数组,将伪数组的每一项push进新数组;
14. 真数组 和 伪数组 的区别
- 共同点:
- 都可以通过索引取值,都有
length
属性;
- 都可以通过索引取值,都有
- 不同点:
- 原型不同:
- 数组原型:
Array.prototype
;
- 伪数组原型:
- 是一种集合,不具备数组的方法;
- 数组原型:
- 原型不同:
15. 谈谈对变量的理解
- 变量就是一个用来存储数据的容器;
- 本质:
- 内存里的一块空间,用来存储数据;
- 初始化:
- 声明变量并及进行赋值操作;
- 命名规则:
- 只能是数字、英文字母、美元符、下划线组成;
- 开头不能是数字;
- 声明变量:
let
; - 使用:
- 必须是先声明再使用;
16. let、const、var的区别
let / const
:- ES6新增的;
- 不存在变量提升;
- 有块作用域;
- 必须先声明再使用;
- 不能声明重复变量;
let
:- 声明变量;
- 有暂时性死区;
const
:- 声明常量;
- 声明的时候必须进行初始化;
var
:- 存在变量提升;
- 没有块作用域;
- 既可以声明变量也可以声明常量;
- 可以先声明后使用(变量提升);
17. for-in 与 for-of 的区别
for-in
:- 适用:
- 对象、数组、伪数组、字符串;
- 便利当前对西安可枚举属性及其原型链上的属性;
- 遍历的是属性或索引;
- 得到的索引是字符串类型;
- 适用:
for-of
:- 适用:
- 数组、伪数组、字符串;
- 遍历的是(可迭代的数据)数组/伪数组的元素、字符串的值
- 得到的索引是数字类型;
- 如果遍历数组的时候,要使用数组的索引,可以使用
arr.entries()
方法(该方法的返回值是一个由给定对象自有的可枚举字符串键属性的键值对组成的数组,每个键值对都是一个包含两个元素的数组,第一个元素是属性的键(始终是字符串),第二个元素是属性值);
- 适用:
18. 具名函数 和 匿名函数
- 具名函数:
- 可以先使用后声明;
- 不能作为事件处理函数;
- 可以用于构造函数;
- 匿名函数:
-
必须先声明后使用;
-
剋作为事件处理函数使用;
-
使用方式:
-
函数表达式;
-
立即执行函数;
- 立即执行函数的格式:
js!(function(形参) { 函数体 })(实参); !(function(形参) { 函数体 } (实参)); // ❗ 立即执行函数的最后面必须加分号
-
-
不能作为构造函数使用;
-
19. 手写冒泡排序
使用双重for循环 外层for循环控制排列好的个数(个数 - 1) 内存for循环控制排序好一个数字需要比较的次数
js
let arr = [2, 46, 32, 86, 45, 98]
// [(2), 46, 32, 86, 45, 98] 0 5
// [2, (32), 86, 45, 46, 98] 1 4
// [2, 32, (45), 46, 86, 98] 2 3
// [2, 32, 45, (46), 86, 98] 3 2
// [2, 32, 45, 46, (86), 98] 4 1
for (let i = 0; i < arr.length - 1; i++) {
for (let j = 0; j < arr.length - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
}
}
}
console.log(arr);
20. 请指出JavaScript 宿主对象 和 原生对象 的区别?
- 原生对象:
- "独立于宿主环境" 的 ECMAScript 实现提供的对象;
- 包含:
Object、Function、Array、String、Boolean、Number、Date、RegExp、Error、EvalError、RangeError、ReferenceError、SyntaxError、TypeError、URIError
;
- 内置对象:
- 我们不必明确实例化内置对象,它已经被内部实例化了;
- 同样是 "独立于宿主环境"。而 ECMA-262 只定义了 两个内置对象 ,即
Global、Math
;
- 宿主对象:
BOM
和DOM
都是宿主对象;- 因为其对于不同的宿主环境所展示的内容不同;
- 简单来说,ECMAScript官方未定义的对象都宿主对象,因为其未定义的对象大多数都是自己通过ECMAScript程序创建的对象;
21. 如何遍历对象的属性?
- 遍历自身可枚举属性(可枚举,非继承属性)
Object.keys()
; - 遍历自身的所有属性(可枚举、不可枚举、非继承属性)
Object.getOwnPropertyNames()
;- 该方法返回一个由指定对象的所有自身属性组成的数组(包括不可枚举属性但不包括 Symbol 值作为名称的属性)
- 遍历可枚举的自身属性和继承属性(可枚举、可继承属性)
for-in
;