前端全部面试题
1.基本数据类型有哪几种?(高频)了解包装对象吗?
undefined,null,boolean,string,number,Symbol(es6)
2.包装对象
定义:基础数据类型临时创建的对象
(1).哪些基本数据类型有/没有包装对象?
有:数字,字符串,布尔
没有:null,undefined
(2).包装对象和基本数据类型的转换,举例子:
// 基本数据类型转换为包装对象
var v1 = new Number(123);
var v2 = new String('abc');
var v3 = new Boolean(true);
typeof v1 // "object"
typeof v2 // "object"
typeof v3 // "object"
// 包装对象转换为基本数据类型
new Number(123).valueOf() // 123
new String('abc').valueOf() // "abc"
new Boolean(true).valueOf() // true
v1.toString(); // '123'
v1+ ''; // '123'
(4)包装对象的应用场景及优缺点
为什么null,undefined不能创建成包装对象(不用看)
在 JavaScript 中,null 和 undefined 是特殊的值,而不是对象。它们是原始类型,在内部被硬编码为特定的值。因此,它们没有对应的构造函数,也就没有包装对象。在尝试访问它们的属性或方法时,会直接抛出 TypeError 错误。
与之不同的是,JavaScript 中的其他原始类型,如字符串、数字和布尔值,都有对应的构造函数 String、Number 和 Boolean,用于创建它们的包装对象。这些包装对象与原始值之间可以进行相互转换,即可以将原始类型的值转换为包装对象,也可以将包装对象转换为原始类型的值。
但是需要注意的是,虽然 JavaScript 中 null 和 undefined 没有包装对象,但是在严格模式下('use strict'),对 null 和 undefined 进行隐式转换会抛出 TypeError 错误,这是为了防止代码不规范或出现潜在 Bug。因此,在使用 null 和 undefined 时需要特别注意它们的处理方式。
3.引用数据类型有哪些?(高频)
对象(Object):即 {}
组成的键值对集合。例如:var obj = {name: 'Lucy', age: 18};
。
数组(Array):即 []
组成的有序集合。例如:var arr = [1, 2, 3];
。
函数(Function):即通过 function
关键字声明的函数。例如:function sum(a, b) { return a + b; }
。
日期(Date):即 new Date()
创建的日期对象。例如:var date = new Date();
。
正则表达式(RegExp):即 new RegExp()
创建的正则对象。例如:var regexp = new RegExp('^abc$');
。
Map 和 Set:即 new Map()
和 new Set()
创建的集合对象。例如:let map = new Map(); map.set('name', 'Lucy');
。
4.说几条写JS的基本规范? (不用看)
- 不要在一行声明多个变量
- 少用全局函数、变量
- switch语句必须带default分支
- 用===/!==来比较true/false或数值
5.JS的typeof返回哪些数据类型(高频)
- undefined
- string
- boolean
- number
- symbol(ES6)
- Object
- Function
6.强制类型转换(高频)
- 转成字符串:1.toString() 2.拼接字符串
- 转成数字: parseInt(),parseFloat();Numbem()
- parseInt() parseFloat() 专门用来对付字符串
- 除了0、NaN、空串、null、undefined其余都是true。对象也会转换为true
// toString()
var a=1;
console.log(a.toString());//输出结果为黑色的1
// 字符串拼接
var a=1;
var b=a+'';
console.log(b);//输出结果为黑色的1
// 使用Number
var a="123";
a=Number(a);
console.log(typeof a); 结果:number
如果:
var a="abc";
a=Number(a);
console.log(a); 结果:NaN
注意:如果是纯数字的字符串,则直接转换为数字,如果字符串中有非数字的内容,则转换为NaN,如果字符串是一个空串或者是一个全是空格的字符串,则转换为0;
Number(true)="1" Number(Null)=0 Number(undefined)="NaN"
var a = "123px";
a=parseInt(a);
console.log(typeof a); 结果:number
console.log(a); 结果:123
var b=true;
b=parseInt(b);
console.log(typeof b); 结果:number
console.log(b); 结果:NaN
// 如果对非string使用parseInt()或parseFloat()它会先将其转换为string,然后再操作。 转成boolean直接就用boolean;
使用Boolean()函数
var a= 123;
a=Boolean(a);
console.log(typeof a); 结果:boolean
console.log(a); 结果:true
js强制类型转换_Baron的博客-CSDN博客_js 类型强转
JS基础之强制类型转换_每文的博客-CSDN博客_js强制类型转换
7.隐式类型转换
分为数据类型隐式类型转换
- 转换为boolean类型
- 转换为number类型
- 转换为string类型
```
console.log(1+'2') //12 string
console.log(1-'2')//-1 number
console.log(10-'kkb') //NaN
console.log(10-'10a') //NaN
console.log(10*'20') //200 number
console.log('10'*'20') //200 number
console.log(2/'1') //2 number
console.log('2'/'1') //2 number
console.log('2'/'kkb') //NaN
```
== 也会进行隐式类型的转换
```
console.log(1+'2') //12 string
console.log(1-'2')//-1 number
console.log(10-'kkb') //NaN
console.log(10-'10a') //NaN
console.log(10*'20') //200 number
console.log('10'*'20') //200 number
console.log(2/'1') //2 number
console.log('2'/'1') //2 number
console.log('2'/'kkb') //NaN
console.log(NaN==NaN) //false
console.log(undefined==null) //true
console.log(null==null) //true
console.log(null==undefined) //true
undefined == undefined //true
```
扩展:通过==比较两边的值是否相等的结果?1==’1’;null==undefined
场景:if语句选择结构
【Js】JavaScript数据类型隐式转换_js隐式类型转换_快乐水续航的博客-CSDN博客
8.“=="和“===”区别(高频)
- 前者会自动转换类型,在判断是否相等
- 后者不会自动转换类型,直接去比较
1=="1"
null==undefined;//==true
==为什么可以自动转换数据类型,===不可以 (不用说)
- ==符号在判断左右两边数据是否相等时,如果数据类型一致,直接比较值即可
- ===是直接比较值
8.逻辑运算符:与,或,非
- || : 只要其中有一个为true,整体结果是true;
- && : 只要有一个是false,整体结果是false;
- !:取 (比较:转布尔,在取反)
9.null和undefined是否相等?null和undefined的区别?(高频)
console.log(null==undefined)//true
console.log(null===undefined)//false
不同点:
- null:没有定义的时候是null值,报错是not found,null表示"无",转成数值时为0
- undefined:只是初始化,创建变量,但是没有赋值,undefined是一个表示"无"的原始值,转为数值时为NaN
- typeof null // 'object';typeof undefined // 'undefined'
相同点:
都是js的基本类型
if语句中自动转为false
js:null与undefined的区别_js undefined和null的区别_枯藤黑鸦的博客-CSDN博客
10.JS四种检测(高频)
typeOf原理:通过判断前三位的值来判断变量的数据类型
- 000:对象
- 010:浮点数
- 100:字符串
- 110:布尔
- 1:整数
- null:所有机器码均为0
- undefined:用 −2^30 整数来表示
instanceof原理:通过原型链来判断一个对象是否为某个类的实例。通过判断object的__proto__属性指向的原型对象是否等于constructor的prototype属性指向的原型对象来实现的。
constructor原理:
Object.prototype.toString.call([]) 原理:是返回一个表示对象类型的字符串,例如对于数组来说,返回的是"[object Array]"
。这个方法的原理是调用Object.prototype.toString
方法,并将需要检测类型的对象作为参数传递给它。因为该方法是原型链上的方法,所以可以直接使用。
Array.isArray
// typeOf:只能检测基本数据类型,不能区分判断 数组、null、对象
typeof 1;//'number'
typeof true;//'boolean'
typeof '';//'string'
typeof undefined;//'undefined'
typeof function (){};'function'
typeof null // Object
// instanceOf:检测当前实例是否属于某个类的方法
A instanceof B:判断A是否为B的实例对象,从而判断A是否为B类型
var arr = [];
arr instanceof Array;//true
function Person(name, age) {
this.name = name;
this.age = age;
}
const john = new Person("John", 30);
console.log(john instanceof Person); // 输出 true
// constructor:检测属性是否存在于构造函数的原型上,用_proto_在原型上
var arr = [];
arr.constructor === Array;//true
// Object.prototype.toString.call([]); 最准确的方式;用来检测数据类型的
console.log(Object.prototype.toString.call(1));//[object Number]
console.log(Object.prototype.toString.call(''));//[object String]
console.log(Object.prototype.toString.call(true));//[object Boolean]
console.log(Object.prototype.toString.call(null));// [object Null]
console.log(Object.prototype.toString.call(undefined));//[object Undefined]
console.log(Object.prototype.toString.call([]));// [object Array]
Array.isArray() 用于确定传递的值是否是一个 Array。
Array.isArray([1, 2, 3]);
// true
Array.isArray({foo: 123});
// false
Array.isArray("foobar");
// false
Array.isArray(undefined);
// false
js 判断类型的几种方法及其优缺点
js 判断类型的几种方法及其优缺点_js判断数据类型的方法及优缺点_Ji'an的博客-CSDN博客
11.看下列代码,输出什么?解释原因。
var a = null;
alert(typeof a); //object
原理,js中二进制前三位都为 0 的话会被判断为 object 类型, null 的二进制表示是全 0,自然前三位也是 0,所以执行 typeof 时会返回“ object ”。
12.看下列代码,输出什么?解释原因。
var undefined;//此时undefined这个变量的值是undefined
undefined == null; // true
1 == true; // true
此时会把布尔类型的值转换为数字类型 true=1 false=0
2 == true; // false
0 == false; // true
0 == ''; // true
NaN == NaN; // false isNaN
[] == false; // true 解释:会把[]和false都通过Number()转换为数字类型
[] == ![]; // true 解释:![]:false
[]==[];//false
13.判断空字符串,undefiend,null(不用考)
- if(undefined == null){ } //返回的是true
- if(undefined == ' '){} // 返回的是false
- if(null == ' '){} // 返回的是false
控制台
- ' '为字符串
- typeof null ==================> Object
- typeof ' ' ====================> String
14.undefiend和null字符在if语句找那个,都会自动变成false =============>让他们变成true,前面要加!(取反)
if(!null) == if(!undefined) == true
15.Array,Object放在if语句中自动转成true(高频)
为什么?
数据类型实现自动转换
16.break,continue, return区别(高频)
- break:立即结束语句,并跳出语句;主要用于循环和switch
- continue:停止当前语句,并继续执行;continue只能用于循环
- return:停止函数
// break
for(var i = 0;i <= 10; i++){
if(i == 6){
break;
}
console.log(i); //12345
}
// continue
for(var i = 0;i <= 10; i++){
if(i == 6){
continue;
}
console.log(i); //1234578910
}
17.for…in..和for…of..的区别?-----(需要完善)
for...in用于遍历数组或者对象的属性;;;;for...of遍历循环数组
for(变量 in 对象){
在此执行代码 }
- for...of遍历获取的是对象的键值, for...in获取的是对象的键名;
- for...in会遍历对象的整个原型链, 性能非常差不推荐使用,而for...of只遍历当前对象不会遍历原型链;
- 对于数组的遍历,for...in会返回数组中所有可枚举的属性(包括原型链上可枚举的属性),for...of只返回数组的下标对应的属性值;
总结:for...in循环主要是为了遍历对象而生,不适用遍历数组; for....of循环可以用来遍历数组、类数组对象、字符串、Set、Map以及Generator对象
// for ... in
let arr = [1, 2, 3, 4, 5]
Array.prototype.id = 123
arr.name = 'Jessica'
for (let index in arr) {
console.log(index, arr[index]);//遍历[1,2,3,4,5,Jessica,123]
}
// for ... of
let arr = [1, 2, 3, 4, 5]
Array.prototype.id = 321
arr.name = 'Mars'
for (let value of arr) {
console.log(value);//只能拿到[1,2,3,4,5]
}
18.map,forEach区别(高频)
相同点:
- 都是循环遍历数组中的每一项
- 每次执行匿名函数,都支持3个参数,参数分别是item(当前每一项),index(索引值),arr(原数组)
- 只能遍历数组
- 匿名函数中this都指向window
不同点:map有返回值,不会改变原数组;forEach没有返回值,对原数组数据直接进行修改
map方法返回一个新的数组,数组中的元素为原始数组调用函数处理后的值(map进行处理之后返回一个新的数组)
map方法不会改变原始数组
var arr = [0,2,4,6,8];
var str = arr.map(function(item,index,arr){
console.log(this); //Window
console.log(this);
console.log(item);
console.log('原数组arr:',arr); // 会执行五次
return item/2;},this);console.log(str); //[0,1,2,3,4]
forEach方法用于调用数组的每个元素,将元素传给回调函数
注意,forEach是不会返回有意义的值的。我们在回调函数中直接修改arr的值。
arr.forEach((value, key) => {
return arr[key] = value * value;
});
forEach,map 是怎么进行跳出循环的
for跳出循环
try {
[1, 2, 3, 4, 5].forEach(num => {
if (num === 3) {
throw new Error('BreakException');
}
console.log(num);
});
} catch (e) {
if (e.message !== 'BreakException') throw e;
}
map
const arr = [1, 2, 3, 4, 5];
let result = [];
arr.some(num => {
if (num === 3) {
return true; // 跳出循环
}
result.push(num);
});
console.log(result); // 输出 [1, 2]
数组怎么实现map
// 自定义map函数实现
Array.prototype.myMap = function(callback){
const newArray = [];
for(let i = 0; i < this.length; i++){
newArray.push(callback(this[i], i, this));
}
return newArray;
};
// 示例:将数组中的每个元素乘以二
const arr = [1, 2, 3, 4];
const newArr = arr.myMap(item => item * 2);
console.log(newArr); // [2, 4, 6, 8]
在上面的示例中,我们通过在 Array
原型对象上定义一个 myMap
函数来实现数组的map方法。该函数接收一个回调函数作为参数,然后在遍历数组的过程中,对每个元素进行回调操作并将结果存入新的数组中,最后将新的数组返回。
需要注意的是,回调函数一共可以接收三个参数:当前元素,当前元素的索引,以及原数组本身。这三个参数虽然都可以使用,但是只有第一个参数是必选的,因为它表示要操作的当前元素。另外,map方法不会改变原有的数组,而是返回一个新的数组。
map和weakMap的区别
「2021」高频前端面试题汇总之JavaScript篇(上) - 掘金
19.循环(while,do...while,for,for...in,for...of,continue,break,label)
// while 先判断--------->后执行
var i = 0;
while(i < 100){
console.log('i为' + i)
}
// do......while 先执行------------->再判断
var x = 3;
var i = 1;
do {
console.log(i);
i++;
} while (i < x)
// for循环
// for...in ---->用于对象遍历
var person = {name: "张”}
for(var key in person){
if(person.hasownproperty){
}
}
// for...of:语句在可迭代对象(包括Array,Map,Set,argument等)上创建了一个循环
let arr = [3, 5, 7];
arr.foo = "hello";
for (let i in arr) {
console.log(i); // 输出 "0", "1", "2", "foo"
}
for (let i of arr) {
console.log(i); // 输出 "3", "5", "7"
}
20.Number的方法
isNaN
: 检查数字是否非法 // console.log(isNaN(123)) // falseNumber
:将其他数据类型的值强制转换成number类型;parseInt
:经常用于字符串提取数字的方法;parseFloat
:和parseInt 用法一样;区别是多识别一位小数点toFixed
: 保留小数点位数的方法;返回值是一个字符串;
21.字符串的方法(13个)
toUpperCase
: 把小写字母转成大写toLowerCase
把大写转小写charAt
: 通过索引获取字符charCodeAt
: 通过索引获取对应字符的Unicode编码;substr
: 截取 substr(m,n) 从索引m开始,截取n个字符;substring
: substring(m,n) :从索引m开始,截取到索引n,不包含n; (不支持负数)slice(m,n)
: substring; 从索引m开始,截取到索引n,不包含n (支持负数)indexOf
: 检测字符在字符串中第一次出现的索引位置;lastIndexOf
: 检测字符在字符串中最后一次出现的索引位置;split
: 把字符串按照特定的字符分隔数组中的每一项;replace
:替换;原有字符串不变;用新字符替换旧的字符concat
: 拼接- includes:判断字符串是否包含指定值
22.字符串的运算
- * /
: 会先把字符串转换成数字,然后再进行计算
23.DOM四种类型的节点
TYPE | nodeType | nodeName | nodeValue |
元素节点 | 1 | 大写的标签名 | null |
文本节点 | 3 | text | 文本内容 |
注释节点 | 8 | comment | 注释内容 |
document | 9 | document | null |
空格和换行都是文本节点;
24.DOM节点的属性(DOM操作的常用API)
DOM操作的常用API_Doe的博客-CSDN博客_操作dom的常用api
节点查找API
1.document.getElementById :根据ID查找元素,大小写敏感,如果有多个结果,只返回第一个;
2.document.getElementByClassName:根据类名查找,多个类名用空格分隔,返回一个 HTMLCollection。
3.document.getElementsByTagName:根据标签查找元素, * 表示查询所有标签,返回一个 HTMLCollection.
4.document.getElementsByName :根据元素的name属性查找,返回一个 NodeList 。
5.document.querySelector :返回单个Node,如果匹配到多个结果,只返回第一个。
6.document.querySelectorAll :返回一个 NodeList
7.document.forms :获取当前页面所有form,返回一个 HTMLCollection
节点创建API
document.getElementById
:通过ID名来获取元素document.getElementsByTagName
: 通过标签名来获取元素document.getElementsByClassName()
; 类数组集合;document.getElementsByName
;通过name属性来获取元素;document.documentElement
获取当前的html- body :获取页面的body元素;
document.querySelector()
;如果是id名加#,如果是class名加.document.querySelectorAll()
;获取所有的元素- createDocumentFragment() // 创建一个DOM片段
- createElement() // 创建一个具体的元素
- createTextNode() // 创建一个文本节点
- appendChild(node)
- removeChild(node)
- replaceChild(new,old)
- insertBefore(new,old) // 在已有的子节点前插入一个新的子节点
- getElementsByTagName() // 通过标签名称
- getElementsByName() // 通过元素的Name属性的值(IE容错能力较强,会得到一个数组,其中包括id等于name值的)
- getElementById() // 通过元素Id,唯一性
childNodes
: 获取当前元素所有的子节点;children
: 获取当前元素的子元素节点;firstChild
: 获取第一子节点;lastChild
:获取最后一个子节点previousSibling
: 获取上一个哥哥节点nextSibling
: 获取下一个弟弟节点parentNode
: 获取当前元素的父亲节点;- instanceof
- isArray
- concat():拼接字符串
- slice():对字符串进行分割
- split():把字符串分割成字符串数组
- toLowerCase():把字符串转换为小写
- toUpperCase():把字符串转换为大写
- charAt():返回指定字符的位置
- indexOf():检索字符串
- replace:替换与正则表达式匹配的字符串
25.已知ID的Input输入框,希望获取这个输入框的输入值,怎么做?(不使用第三方框架)
document.getElementById("ID").value
26.怎么检测数组?
- Array.isArray()
instanceof
运算符或原型链检测方法来判断一个对象是否为数组- Object.prototype.toString.call(arr)
// Array.isArray
let arr = [1, 2, 3];
if (Array.isArray(arr)) {
console.log('arr是数组');
} else {
console.log('arr不是数组');
}
// instanceof
let arr = [1, 2, 3];
if (arr instanceof Array) {
console.log('arr是数组');
} else {
console.log('arr不是数组');
}
// 原型链方法
if (Object.prototype.toString.call(arr) === '[object Array]') {
console.log('arr是数组');
} else {
console.log('arr不是数组');
}
27.改变数组长度的方法有哪些?
shift、unshift、pop、push
28.数组的方法(高频)(数组能够调用的函数有哪些?
在js中,数组是一组按照顺序排列的值的集合。数组可以包含任意类型,可以是数字,字符串,对象。数组可以通过索引访问,数组有很多方法
length():返回数组的长度
push()尾部添加,返回数组长度(向数组末尾新增一项;可以传多个)
var arr = ["Lily","lucy","Tom"];
var count = arr.push("Jack","Sean");
console.log(count); // 5
console.log(arr); // ["Lily", "lucy", "Tom", "Jack", "Sean"]
pop()尾部删除,返回被删除的元素(删除数组的最后一项;不需要传参数;)
var item = arr.pop();
console.log(item); // Sean
console.log(arr); // ["Lily", "lucy", "Tom", "Jack"]
unshift()头部添加 ,返回数组长度
var arr = ["Lily","lucy","Tom"];
var count = arr.unshift("Jack","Sean");
console.log(count); // 5
console.log(arr); //["Jack", "Sean", "Lily", "lucy", "Tom"]
shift()头部删除,返回被删除的元素
var item = arr.shift();
console.log(item); // Jack
console.log(arr); // ["Sean", "Lily", "lucy", "Tom"]
reverse():反转数组项的顺序
var arr = [13, 24, 51, 3];
console.log(arr.reverse()); //[3, 51, 24, 13]
console.log(arr); //[3, 51, 24, 13](原数组改变)
concat():数组的拼接 。构建成一个新的数组,原数组并未改变.
var arr = [1,3,5,7];
var arrCopy = arr.concat(9,[11,13]);
console.log(arrCopy); //[1, 3, 5, 7, 9, 11, 13]
console.log(arr); // [1, 3, 5, 7](原数组未被修改)
slice(m,n): 数组的截取 ,从数组索引m开始,截取到索引n,但是不包含n;[前包后不包] ,原有数组不发生改变
slice(m) : 从索引m开始,截取到末尾;
slice()方法可以接受一或两个参数
var arr = [1,3,5,7,9,11];
var arrCopy = arr.slice(1);
var arrCopy2 = arr.slice(1,4);
var arrCopy3 = arr.slice(1,-2);
var arrCopy4 = arr.slice(-4,-1);
console.log(arr); //[1, 3, 5, 7, 9, 11](原数组没变)
console.log(arrCopy); //[3, 5, 7, 9, 11]
console.log(arrCopy2); //[3, 5, 7]
console.log(arrCopy3); //[3, 5, 7]
console.log(arrCopy4); //[5, 7, 9]
indexOf():接收两个参数:要查找的项和(可选的)表示查找起点位置的索引。其中, 从数组的开头(位置 0)开始向后查找。
lastIndexOf:接收两个参数:要查找的项和(可选的)表示查找起点位置的索引。其中, 从数组的末尾开始向前查找。
这两个方法都返回要查找的项在数组中的位置,或者在没找到的情况下返回-1。在比较第一个参数与数组中的每一项时,会使用全等操作符。
var arr = [1,3,5,7,7,5,3,1];
console.log(arr.indexOf(5)); //2
console.log(arr.lastIndexOf(5)); //5
console.log(arr.indexOf(5,2)); //2
console.log(arr.lastIndexOf(5,4)); //2
console.log(arr.indexOf("5")); //-1
forEach()对数组进行遍历循环,对数组中的每一项运行给定函数。这个方法没有返回值。参数都是function类型,默认有传参,参数分别为:遍历的数组内容;第对应的数组索引,数组本身。
var arr = [1, 2, 3, 4, 5];
arr.forEach(function(x, index, a){
console.log(x + '|' + index + '|' + (a === arr));
});
// 输出为:
// 1|0|true
// 2|1|true
// 3|2|true
// 4|3|true
// 5|4|true
map()指“映射”,对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组。(有返回值)
var arr = [1, 2, 3, 4, 5];
var arr2 = arr.map(function(item){
return item*item;
});
console.log(arr2); //[1, 4, 9, 16, 25]
splice(m,n): 删除数组中的某几项 .从索引开始,删除n个
splice(m) : 从索引m开始删除到末尾;
splice(0):
splice(m,x,n);替换从索引m开始,删除x个,用n替换;
原有数组发生改变
var fruits = ["Banana", "Orange", "Apple", "Mango"];
fruits.splice(2,0,"Lemon","Kiwi");
// Banana,Orange,Lemon,Kiwi,Apple,Mango
slice():方法以新的数组对象,返回数组中被选中的元素,不会改变原数组,方法选择从给定的 start 参数开始的元素,并在给定的 end 参数处结束,但不包括。
var fruits = ["Banana", "Orange", "Lemon", "Apple", "Mango"];
var citrus = fruits.slice(1, 3);
sort():按升序排列数组项——即最小的值位于最前面,最大的值排在最后面。
在排序时,sort()方法会调用每个数组项的 toString()转型方法,然后比较得到的字符串,以确定如何排序。即使数组中的每一项都是数值, sort()方法比较的也是字符串,因此会出现以下的这种情况:
var arr1 = ["a", "d", "c", "b"];
console.log(arr1.sort()); // ["a", "b", "c", "d"]
arr2 = [13, 24, 51, 3];
console.log(arr2.sort()); // [13, 24, 3, 51]
console.log(arr2); // [13, 24, 3, 51](元数组被改变)
为了解决上述问题,sort()方法可以接收一个比较函数作为参数,以便我们指定哪个值位于哪个值的前面。比较函数接收两个参数,如果第一个参数应该位于第二个之前则返回一个负数,如果两个参数相等则返回 0,如果第一个参数应该位于第二个之后则返回一个正数。以下就是一个简单的比较函数:
function compare(value1, value2) {
if (value1 < value2) {
return -1;
} else if (value1 > value2) {
return 1;
} else {
return 0;
}
}
arr2 = [13, 24, 51, 3];
console.log(arr2.sort(compare)); // [3, 13, 24, 51]
如果需要通过比较函数产生降序排序的结果,只要交换比较函数返回的值即可:
function compare(value1, value2) {
if (value1 < value2) {
return 1;
} else if (value1 > value2) {
return -1;
} else {
return 0;
}
}
arr2 = [13, 24, 51, 3];
console.log(arr2.sort(compare)); // [51, 24, 13, 3]
filter()过滤”功能,数组中的每一项运行给定函数,返回满足过滤条件组成的数组。
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var arr2 = arr.filter(function(x, index) {
return index % 3 === 0 || x >= 8;
});
console.log(arr2); //[1, 4, 7, 8, 9, 10]
every():判断数组中每一项都是否满足条件,只有所有项都满足条件,才会返回true。
var arr = [1, 2, 3, 4, 5];
var arr2 = arr.every(function(x) {
return x < 10;
});
console.log(arr2); //true
var arr3 = arr.every(function(x) {
return x < 3;
});
console.log(arr3); // false
some()
var arr = [1, 2, 3, 4, 5];
var arr2 = arr.some(function(x) {
return x < 3;
});
console.log(arr2); //true
var arr3 = arr.some(function(x) {
return x < 1;
});
console.log(arr3); // false
reduce:将一个数组或对象中多个值合并为一个值,并且可以对每个元素进行操作
使用场景:数组求和,平均值,最大值,最小值等操作
扁平化数组,数组去重,排序等操作
将对象的属性值进行合并,计算等操作
实现函数的柯里化
// 求和
const arr = [1,2,3,4]
const sum = arr.reduce((prev,cur) => prev + cur, 0)
console.log(sum) // 15
// 求平均值
const arr = [1,2,3,4]
const avg = arr.reduce((prev,cur,index,arr) =>{
prev += cur;
if(index === arr.length - 1){
return prev / arr.length
}else{
return prev
}
},0)
console.log(avg)
// 求最大值
const arr = [1, 2, 3, 4, 5];
const max = arr.reduce((prev, cur) => prev > cur ? prev : cur, arr[0]);
console.log(max); // 5
// 求最小值
const arr = [1, 2, 3, 4, 5];
const min = arr.reduce((prev, cur) => prev < cur ? prev : cur, arr[0]);
console.log(min); // 1
// 扁平化多维数组
const arr = [1, [2, [3, 4], 5], 6];
function flatten(arr) {
return arr.reduce((prev, cur) => {
if (Array.isArray(cur)) {
return prev.concat(flatten(cur));
} else {
return prev.concat(cur);
}
}, []);
}
const flatArr = flatten(arr);
console.log(flatArr); // [1, 2, 3, 4, 5, 6]
// 对数组进行去重、排序等操作
// 排序
const arr = [3, 1, 4, 2, 5];
const sortedArr = arr.reduce((prev, cur) => {
const index = prev.findIndex(item => item > cur);
if (index === -1) {
prev.push(cur);
} else {
prev.splice(index, 0, cur);
}
return prev;
}, []);
console.log(sortedArr); // [1, 2, 3, 4, 5]
// 去重
const arr = [1, 2, 3, 2, 4, 1, 5];
const uniqueArr = arr.reduce((prev, cur) => {
if (prev.indexOf(cur) === -1) {
prev.push(cur);
}
return prev;
}, []);
console.log(uniqueArr); // [1, 2, 3, 4, 5]
// 实现函数的柯里化
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
} else {
return function(...args2) {
return curried.apply(this, args.concat(args2));
}
}
}
}
function add(a, b, c) {
return a + b + c;
}
const curriedAdd = curry(add);
console.log(curriedAdd(1)(2)(3)); // 6
console.log(curriedAdd(1, 2)(3)); // 6
console.log(curriedAdd(1)(2, 3)); // 6
console.log(curriedAdd(1, 2, 3)); // 6
在curry函数中,使用reduce方法对参数列表进行累加,如果参数列表长度大于等于函数的形参个数,则直接调用原函数;
否则返回一个新的包装函数,将新的参数列表和原有的参数列表合并后再次调用curried函数。最终返回原函数的计算结果。
通过这种方式,就可以实现函数的柯里化
indexOf实现
indexOf()
方法返回数组中指定元素的第一个匹配项的索引。如果未找到该元素,则返回-1。
Array.prototype.myIndexOf = function(item) {
for (let i = 0; i < this.length; i++) {
if (this[i] === item) {
return i;
}
}
return -1;
};
数组排序
sort方法
const a = [{age:51},{age:23},{age:53},{age:12}]
// 答案
function sort(arr){
return arr.sort(({age:a},{age,b}) => a - b);
}
29.对象的方法? (高频)
js中,对象是一组无序的键值对,可以通过方法来操作这些键值对。常见方法有
// Object.keys():该方法返回一个对象的所有key值组成的数组
const obj = {a: 1, b: 2, c: 3};
const keys = Object.keys(obj);
console.log(keys); // ['a', 'b', 'c']
// Object.values():改方法返回一个对象所有的value值组成的数组
const obj = {a: 1, b: 2, c: 3};
const values = Object.values(obj);
console.log(values); // [1, 2, 3]
// Object.entries():该方法返回由一个对象的所有键值对组成的数组
const obj = {a: 1, b: 2, c: 3};
const entries = Object.entries(obj);
console.log(entries); // [['a', 1], ['b', 2], ['c', 3]]
// Object.assign():用于浅拷贝,返回目标对象
const obj1 = {a: 1};
const obj2 = {b: 2};
const mergedObj = Object.assign(obj1, obj2);
console.log(mergedObj); // {a: 1, b: 2}
// Object.hasOwnProperty():检查对象是否具有指定属性(不包括原型链上的属性)
const obj = {a: 1, b: 2, c: 3};
console.log(obj.hasOwnProperty('a')); // true
console.log(obj.hasOwnProperty('toString')); // false
// Object.freeze():该方法将对象冻结,即防止对象被修改(属性值不能被修改、添加、删除),并返回被冻结的对象。
const obj = {a: 1, b: 2, c: 3};
Object.freeze(obj);
obj.a = 4; // 不生效
console.log(obj); // {a: 1, b: 2, c: 3}
JS对象中常见的方法_要不要买菜啊的博客-CSDN博客_js对象常用方法
30.数组去重(高频)
// new Set
let arr1 = [1, 2, 3, 1, 2, 3, '1', '1'];
let newArr1 = [...new Set(arr1)];
console.log(newArr1);
let A = [1,2,3]
let B = [2,3,4]
let union = [...new Set([...a,...b])
// 双for循环:双层循环,外层循环元素,内层循环时比较值。值相同时,则删去这个值。
var arr = [1,2,3,4,5,6,54,5,67]
function unique(arr){
for(let i = 0; i< arr.length;i++){
for(let j = i + 1; j< arr.length;j++){
if(arr[i] == arr[j]){
arr.splice(j,i) // 第一个等同于第二个,splice方法删除第二个
}
}
}
return arr;
}
// 利用indexOf去重
思路:新建一个空数组,for循环原数组,判断结果数组是否存在当前元素,如果有相同的值则跳过,不相同则push进数组。
function unique(arr){
if(!Array.isArray(arr){
console.log('type error');
return;
}
var array = [];
for(var i = 0; i < arr.length;i++){
if(array.indexOf(arr[i]) === -1){
array.push(arr[i]);
}
}
return array;
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
// [1, "true", true, 15, false, undefined, null, NaN, NaN, "NaN", 0, "a", {…}, {…}] //NaN、{}没有去重
https://segmentfault.com/a/1190000016418021?utm_source=tag-newest
31.split() join() 的区别(高频)
- split():把一个字符串分割成字符串数组,并返回。例如:“hello".split("") // ["h", "e", "l", "l", "o"]
- join():把数组中的所有元素放入一个字符串。元素是通过指定的分隔符进行分隔的,并返回
在本例中,我们将创建一个数组,然后把它的所有元素放入一个字符串:
<script type="text/javascript">
var arr = new Array(3)
arr[0] = "George"
arr[1] = "John"
arr[2] = "Thomas"
console.log(arr.join())
</script>
输出:George,John,Thomas
32.js精度误差
- toFixed()方法,使用不同函数分别计算(+,-,*,/)
var a = 1;
var b = 2.344544;
consoel.log((a+b).toFixed(2))
- 小数点[(变量 * 10) / 10]) 先乘10再除以10
33. 什么是数组?什么是对象?
数组是带索引值的,对象是键值对
let arr = [1,2,3]
arr.forEach(item,index =>{console.log(index)})
let obj = {
name:'ss'
}
console.log(obj)
34.伪数组
定义:是个对象,拥有length属性且属性值必须是number类型,其他属性(索引)为非负整数;不具有数组所具有的push,pop,forEach等方法;伪数组长度不可变,真数组的长度是动态可变的
// 真数组
var obj = {}
var obj = {length:3}
// 伪数组
var obj = {0: '12', length: 1}
var obj = {99: 'abc',length: 100}
35.说一下怎么把类数组转换为数组?
说后2个即可
//通过apply调用数组的concat方法来实现转换
Array.prototype.concat.apply([],arrayLike)
//通过Array.from方法来实现转换
Array.from(arrayLike)
// 通过call调用数组的slice方法来实现转换
Array.prototype.slice.call(arrayLike)
// 通过call调用数组的splice方法来实现转换
Array.prototype.splice.call(arrayLike,0)
36.Array.from()
将一个类数组对象(伪数组)或者可遍历对象转成一个真正的数组,es6新增的
new map和new set的使用场景
// 伪数组:拥有一个length属性和若干索引的任意对象
可迭代对象:可以获取对象中元素,如Map和Set等
Array.from('foo') // ['f','o','o']
const set = new Set(['foo','bar','baz','foo'])
Array.from(set) // ['foo','bar','baz']
const map = new Map([[1,2],[2,4],[4,8]])
Array.from(map) // [[1,2],[2,4],[4,8]]
Array.from([1, 2, 3], x => x + x); //[2,4,6]
37.Object.create() 实现基本原理
以一个已有的对象为原型(prototype)创建并返回一个新对象
Object.create()是怎样建立原型链的_// object.create()将原型指向parentprovides,会改变原有的原型链ma-CSDN博客
const obj = {
a: 1,
b: 2
};
const obj2 = Object.create(obj, {
c: {
value: 3,
writable: true,
enumerable: true,
configurable: true
}
});
console.log(obj2.a); // 1
console.log(obj2.b); // 2
console.log(obj2.c); // 3
38.说一下怎么取出数组最多的一项?
- 创建一个对象,用于存储数组中各个元素出现的次数。
- 遍历数组,统计每个元素出现的次数并存储在对象中。
- 找到出现次数最多的元素及其出现次数。
- 返回出现次数最多的元素。
// froEach
function findMostFrequentElement(arr) {
let elementCount = {};
arr.forEach(element => {
if (elementCount[element]) {
elementCount[element]++;
} else {
elementCount[element] = 1;
}
});
let mostFrequentElement;
let maxCount = 0;
for (let key in elementCount) {
if (elementCount[key] > maxCount) {
mostFrequentElement = key;
maxCount = elementCount[key];
}
}
return mostFrequentElement;
}
const arr = [1, 2, 3, 2, 2, 1, 4, 2, 5, 2];
const mostFrequent = findMostFrequentElement(arr);
console.log(`数组中出现次数最多的元素是: ${mostFrequent}`);
// map
function findMostFrequentElement(arr) {
let elementCount = new Map();
arr.forEach(element => {
elementCount.set(element, (elementCount.get(element) || 0) + 1);
});
let mostFrequentElement;
let maxCount = 0;
for (let [key, value] of elementCount) {
if (value > maxCount) {
mostFrequentElement = key;
maxCount = value;
}
}
return mostFrequentElement;
}
const arr = [1, 2, 3, 2, 2, 1, 4, 2, 5, 2];
const mostFrequent = findMostFrequentElement(arr);
console.log(`数组中出现次数最多的元素是: ${mostFrequent}`);
js找出数组中出现最多的元素和次数_weixin_34074740的博客-CSDN博客
39.、如何用 JavaScript 编写“Hello World”?
document.write(“JavaScript Hello World!”);
40.如果实现一个超链接下载功能
添加download属性
<a href="url" download="filename">下载</a>
实现思路
// createElement创建节点a
// 插入节点a
// 给a节点设置href属性
// 调用click点击事件
<script>
var a=document.createElement("a");
document.body.appendChild(a);
a.setAttribute('href','openlayerexample.zip');
a.click();
</script>
// 如何实现上传图片功能
<div id="pic">
图片:<div id="headPic"><img src="../../imgs/def.jpg" ></div>
<input type="file" id="fil">
</div>
// js代码
let fil = document.querySelector('#fil') // 文件框,只不过隐藏了起来
let headPic = document.querySelector('#headPic') //图片上传框
headPic.addEventListener('click', function () {
fil.click() //点击上传图片,文件上传框打开
})
//头像上传
fil.addEventListener('change', function (e) {//注意,头像上传时用的时change事件
// console.log(this.files[0]); //文件的信息都放在files里
let fd = new FormData()
fd.append('imgurl', this.files[0])
//原生ajax实现实现图片上传
let xhr = new XMLHttpRequest() //创建ajax对象
xhr.open('post', 'http://139.9.177.51:5000' + '/book/upload')
xhr.send(fd) //将格式处理好的数据,作为参数发送
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
let data = JSON.parse(xhr.responseText);
console.log(data);
img.src = 'http://139.9.177.51:5000' + data.imgurl
}
}
})
setAttribute
给指定元素添加新属性 。设置元素的 id、class、style 等属性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>setAttribute Example</title>
</head>
<body>
<div id="myDiv">Hello, World!</div>
<script>
// 获取元素
const myDiv = document.getElementById('myDiv');
// 设置元素的新属性
myDiv.setAttribute('class', 'highlight');
myDiv.setAttribute('style', 'color: red; font-size: 20px;');
</script>
</body>
</html>
41.说一下innerHTML 与 innerText的作用与区别?
- innerHTML获取的是标签;innerText获取的是标签内容
-
<html> <head><title>innerHTML与innerText的区别</title></head> <body> <div id="div1"> <p id="p1">hello world </p> </div> <script> var div1 = document.getElementById("div1"); alert(div1.innerHTML); // <p id="p1">hello world </p> alert(div1.innerText) // hello world </script> </body> </html>
42.JavaScript 由以下三部分组成:(不用考)
- ECMAScript(语法部分):JS语言基础
- DOM(文档对象模型):规定了访问 HTML 和 XML 的方法
- BOM(浏览器对象模型):提供了浏览器窗口之间进行交互的对象和方法
43.JavaScript如何保持并发?
-
事件循环。
-
微和宏队列。
-
回调。
-
线程池和集群(多线程)。
Promise 和 async/await
// Promise
Promise.all([
fetch('/api/endpoint1'),
fetch('/api/endpoint2')
]).then(([response1, response2]) => {
// 处理两个接口的响应
});
// async/await
async function fetchData() {
const [response1, response2] = await Promise.all([
fetch('/api/endpoint1'),
fetch('/api/endpoint2')
]);
// 处理两个接口的响应
}
Web Worker
Web Worker 是一种在浏览器中运行 JavaScript 脚本的机制,可以让 JavaScript 实现多线程并发处理。Web Worker 可以在后台执行耗时的任务,不会阻塞主线程,
从而提高程序的并发性能。
// 创建 worker 对象
const worker = new Worker('worker.js');
// 发送消息
worker.postMessage({ task: 'task1' });
worker.postMessage({ task: 'task2' });
// 监听消息
worker.onmessage = event => {
// 处理任务结果
};
44.DOM 元素e的 e.getAttribute(propName)和 e.propName 有什么区别和联系(不用考)
- e.getAttribute:获取的是标签上属性;可以通过e.setAttribute(propName, propValue)设置标签上属性
- e.propName:获取的是元素对象上属性
form控件中<input id="input" value="hello"/>
document.getElementById("input").getAttribute("value") //hellow
document.getElementById("input").value //"" 用户输入的值
45.offsetWidth/offsetHeight,clientWidth/clientHeight,scrollWidth/scrollHeight 的区别?
- offsetWidth/offsetHeight (布局宽度)返回值包含 content + padding + border + 包含滚动条
- clientWidth/clientHeight (元素内部宽度)返回值只包含 content + padding,如果有滚动条,也不包含滚动条
- scrollWidth/scrollHeight (元素实际宽度)返回值包含 content + padding + 溢出内容的尺寸
怎么获取鼠标位置
当鼠标在页面中移动时,会触发mousemove事件,在事件处理中,可以通过event.clientX和event.clientY属性来获取鼠标在浏览器中的坐标位置
document.addEventListener('mousemove', function(event) {
var x = event.clientX;
var y = event.clientY;
console.log('鼠标位置:x=' + x + ', y=' + y);
});
给一个div获取距离上面和左面的距离
46.什么是回调,并提供一个简单的例子()可以先不看--后面整理
回调函数(callback)是什么?一文理解回调函数(callback) - 知乎
回调函数是作为参数传递给另一个函数并在某些操作完成后执行的函数。
function modifyArray(arr, callback) {
// do something to arr here
arr.push(100);
// then execute the callback function that was passed
callback();
}
var arr = [1, 2, 3, 4, 5];
modifyArray(arr, function() {
console.log("array has been modified", arr);
});
47.for和forEach谁更快,为什么?(不用看)
for 更快。
- forEach每次都要创建一个函数来调用,而for不会创建函数,函数需要独立的作用域,会有额外的开销
- for循环可以使用break跳出循环,但forEach不能。
- for循环可以控制循环起点(i初始化的数字决定循环的起点),forEach只能默认从索引0开始。
48.如何阻止冒泡 和 默认事件
停止冒泡:
window.event ? window.event.cancelBubble = true : e.stopPropagation();
阻止默认事件:
window.event ? window.event.returnValue = false : e.preventDefault();
阻止默认事件:return false ; e.preventDefault();
停止冒泡事件:e.stopPropagation();
49.documen.write 和 innerHTML 的区别(不用考)
- document.write 只能重绘整个页面
- innerHTML 可以重绘页面的某一部分
50.如何判断一个对象是不是空对象?
方法一:将对象转成字符串,再判断是否等于“{}”
let obj = {}
console.log(JSON.stringify(obj) === "{}");
方法二:for in循环
let result = function(obj){
for(let key in obj){
return false; // 若不为空,可遍历,返回false
}
return true;
}
console.log(result(obj)) // 返回true
方法三:Object.keys()方法,返回对象的属性名组成的一个数组,若长度为0,则为空对象
console.log(Object.keys(obj).length===0);//返回true
方法四:Object.getOwnPropertyNames方法获取对象的属性名,存到数组中,若长度为0,则为空对象
console.log(Object.getOwnPropertyNames(obj).length==0);//返回true
const object1 = {
a: 1,
b: 2,
c: 3,
};
console.log(Object.getOwnPropertyNames(object1));
// Expected output: Array ["a", "b", "c"]
51.for..in 和 object.keys的区别
- Object.keys():遍历对象的key,返回一个数组,数组值为对象自有的属性,不会包括继承原型的属性
- for in :以任意顺序遍历一个对象的属性,包括自身属性,以及继承自原型的属性
Object.prototype.aaa = 'aaa';
let obj = {
a: '1',
b: '2'
}
Object.keys(obj).forEach(item => {
console.log(item) // a,b
})
for (const key in obj) {
console.log(key) // a,b,aaa
}
for (const key in obj) {
if (Object.hasOwnProperty.call(obj, key)) {
console.log(key) // a,b
}
}
注意:两种方法都无法遍历到以Symbol为键(key)的属性
for in和for of区别
for...in 循环用于遍历对象的可枚举属性,包括对象自身的属性以及原型链继承的属性。
// variable 是一个变量,用来迭代对象的属性名,object 是要遍历的对象。
for (variable in object) {
// code block to be executed
}
for...of循环用于遍历可迭代对象(如数组、Set、Map、字符串等),并且只会迭代对象自身的可迭代属性。语法如下:
// variable 是一个变量,用来迭代可迭代对象中的值,iterable 是要迭代的可迭代对象。
for (variable of iterable) {
// code block to be executed
}
区别
-
适用对象类型不同:
for...in
适用于遍历对象的属性名称(键),包括继承而来的属性。for...of
适用于遍历可迭代对象(如数组、Set、Map、字符串等)的元素值。
-
遍历顺序:
for...in
遍历的顺序不是按照对象属性在对象中的顺序,而是根据属性的插入顺序和原型链的顺序。for...of
遍历的顺序是按照对象中元素的顺序进行迭代。
-
可枚举属性:
for...in
循环会遍历对象的所有可枚举属性,包括自身的和继承的。for...of
循环只能用于可迭代对象,遍历对象自身的可迭代属性。
总的来说,for...in
适用于遍历对象的属性,而 for...of
适用于遍历可迭代对象的元素。选择使用哪种取决于你想要遍历的对象类型和遍历的需求。
52.介绍js有哪些内置对象?
Object是JS中所有对象的父对象
数据封装类对象:Array、Boolean、Number、String、Object
其他对象:Function、Arguments、Math、Date、Error、RegExp
53.怎样判断2个对象相等:JSON.stringfiy == JSON.stringify(obj);Object.entries().toString()
js比较俩个对象是否相等_逩跑鍀小学生的博客-CSDN博客_js对比两个对象值是否相同
54.说下什么是IIFE函数
IIFE(立即调用函数表达式)是一个在定义时就会立即执行的函数。
语法:(function() { // 此处为代码块 })();
(function(name) {
console.log('Hello ' + name + '!');
})('World');
场景:
- 避免变量名冲突:在多人开发或引入第三方库时,可能会出现变量名冲突的情况。使用 IIFE 可以在函数内声明变量,用于避免同名变量的冲突。
- 模拟块级作用域:JavaScript 中并没有块级作用域,使用 IIFE 可以模拟块级作用域,避免变量污染全局作用域。
应用场景:
55.JS中的Array.prototype.splice()和Array.prototype.slice()方法作用与区别
Array.prototype.splice() 方法用于删除数组中的元素并用新元素替换它们,可以改变原数组,其语法为:
array.splice(start[, deleteCount[, item1[, item2[, ...]]]])
// 参数说明
start:指定删除/插入的起始位置,如果是负数,表示从数组末尾开始的位置。
deleteCount:可选参数,表示要删除的元素个数,如果省略或为0,则不删除元素,只进行插入操作。
item1, item2,...:可选参数,表示要插入的新元素。
Array.prototype.slice() 方法用于从数组中截取一段指定的元素组成一个新数组,不会改变原数组,其语法为:
array.slice([begin[, end]])
// 参数说明
begin:可选参数,表示截取起始位置的索引值,如果省略,则从头开始截取。
end:可选参数,表示截取结束位置的索引值(不包含该位置元素),如果省略,则截取到数组末尾。
区别:
- splice() 方法会改变原数组,slice() 方法不会改变原数组。
- splice() 方法可以进行删除和插入操作,而 slice() 只能进行截取操作。
- splice() 方法的第二个参数是要删除的元素个数,slice() 方法的第二个参数是要截取到的位置。
- slice() 方法返回的是一个新数组,splice() 方法返回的是被删除的元素组成的数组。
56.Math的方法
Math.abs()
: 取绝对值;Math.floor()
: 向下取整Math.ceil()
: 向上取整 console.log(Math.ceil(.95)); // 1Math.max()
: 取最大值 // console.log(Math.max(1,3,2) // 3Math.min()
: 取一组数的最小值 // console.log(Math.min(1,3,2) // 1Math.random()
取随机数,取值范围[0,1)Math.round()
: 四舍五入取整- 取m-n之间的随机整数:
Math.round(Math.random()*(n-m)+m)
Math.pow()
: 取幂次方Math.sqrt()
: 开平方;
如何获取js中三个数的最大值和最小值
- Math.max(a,b,c) // 最大值
- Math.min(a,b,c) // 最小值
var arr = [22,13,6,55,30];
console.log(Math.max(...arr)); // 55
57.在JS中编码与解码URL
- encodeURI() 可以对字符串编码
- .decodeURI() 可以对字符串解码
应用场景
2022最全最新前端面试题(附加解答)_心如天然薄荷清凉的博客-CSDN博客_前端面试题
58.0.1 + 0.2 === 0.3 嘛?为什么?
不等于
在两数相加时,会先转换成二进制,0.1 和 0.2 转换成二进制的时候尾数会发生无限循环,JS 引擎对二进制进行截断,所以造成精度丢失。
59.Number() 的存储空间是多大?如果后台发送了一个超过最大自己的数字怎么办 (不用看)
Math.pow(2, 53) ,53 为有效数字,会发生截断,等于 JS 能支持的最大数字。
60.NaN 是什么,用 typeof 会输出什么?
Not a Number,表示非数字,typeof NaN === 'number' // true
61.写一个function,清除字符串前后的空格。(兼容所有浏览器)
使用自带接口trim(),考虑兼容性(IE9以下浏览器不支持):
考点:1、原型扩展 2、正则表达式 3、字符串的replace方法
if(typeof String.prototype.trim !=”function”){
String.prototype.trim=function(){
return this.replace(/^\s+|\s+$/g,”“);
}
}
var str="hello";
62.前端错误如何捕获,promise的错误是如何捕获的 ?前端监控怎么做?
1.为什么要处理异常?
异常处理是保证程序运行稳定和安全的重要机制,目的在于当程序出现错误或异常时,能够及时地处理异常信息,防止出现不可预计的错误,保证程序的健壮性和可靠性。
2.需要处理哪些异常?(前3个)
- 语法错误:比如拼写错误、不完整的语句等导致代码无法解析的错误。
- 引用错误(Reference Errors):访问未声明的变量或未定义的函数等。
- URI 错误(URI Errors):URI 相关函数使用不正确的时候抛出的错误。
- 范围错误(Range Errors):使用了不合法的索引值或长度值等,超出了合法的范围。
- 类型错误(Type Errors):操作了不合法的数据类型,比如给字符串赋值为数字等。
3.针对异常措施进行处理
- try...catch...finally语句:这种方法通过try代码块捕获可能出现异常的代码,并在catch代码块中处理异常。如果有finally块,则无论try代码块是否有异常,finally块都将被执行。
- window.onerror事件处理程序:这种方法通过设置onerror事件处理程序来捕获未捕获的异常。当JavaScript代码中出现错误时,该事件处理程序会自动触发,并将错误信息传递给处理程序。
- promise
- async/await
// 1.try...catch...finally
try {
// 可能会出现异常的代码
} catch (error) {
// 处理异常
} finally {
// 最后要执行的代码
}
// window.onerror
window.onerror = function(message, source, lineno, colno, error) {
// 处理错误
}
11.错误上报
63.什么是json:前后台交互的一种数据格式
65.描述一些常见的网页事件
- Onclick:单击事件(鼠标左键)
- Onload:加载事件
- Onchange:内容改变事件
- Onscroll:滚动事件
- Onmouseenter / onmouseover:鼠标进入事件
- Onmouseleave / onmouseout:鼠标离开事件
66.什么是window对象? 什么是document对象?
- Window是浏览器对象、Window对象是浏览器自动定义的顶层对象DOM
- Document对象是文档的根节点。Window.document属性就是指向这个对象(只要浏览器开始载入HTML文档、这个对象就开始存在了、可以直接调用)、表示窗口中显示的当前文档对象 BOM
BOM:浏览器对象模型,提供操作浏览器的相关方法
- Window窗口对象
- Location地址对象
- History历史对象
- Navigator版本信息对象
- Screen屏幕独享
- Document文档对象
- DOM:文档对象模型, 提供操作文档的相关方法
JS获取地址的方法和地址后的参数
var url = window.location.href;
console.log(url);
var url = window.location.toString();
console.log(url);
var url = window.location.protocol + "//" + window.location.hostname + ":" + window.location.port + window.location.pathname + window.location.search;
console.log(url);
其中,window.location.protocol返回当前页面所使用的协议(例如http、https),window.location.hostname返回当前页面的主机名,window.location.port返回当前页面所使用的端口号(如果是默认端口号,则返回空字符串),window.location.pathname返回当前页面的路径名(不包含查询参数),window.location.search返回当前页面的查询参数部分(以问号开头)。
67.JS中如何将页面重定向到另一个页面?
- location.href:
window.location.href=“https://www.onlineinterviewquestions.com/”
- location.replace:
window.location.replace("https://www.onlineinterviewquestions.com"
)
70.设计模式有哪些?(说发布订阅模式和工厂模式)项目中用过那种模式
-
发布订阅模式:一种消息模式,它定义了一种解耦的方法,使得多个对象间可以轻松地交换消息而不需要显式的依赖关系。在发布订阅模式中,消息的发送者称为发布者(Publisher),而消息的接收者称为订阅者(Subscriber)。发布者通知订阅者有关于特定事件的消息,而订阅者只需要监听它们感兴趣的这些事件即可。
这种设计模式可以大大降低程序模块之间的耦合度,便于更加灵活的扩展和维护。
// 定义一个发布者
class Publisher {
constructor() {
this.subscribers = [];
}
// 添加订阅者
addSubscriber(subscriber) {
this.subscribers.push(subscriber);
}
// 移除订阅者
removeSubscriber(subscriber) {
const index = this.subscribers.indexOf(subscriber);
if (index !== -1) {
this.subscribers.splice(index, 1);
}
}
// 通知所有订阅者
notify(data) {
this.subscribers.forEach(subscriber => subscriber.update(data));
}
}
// 定义一个订阅者
class Subscriber {
constructor(name) {
this.name = name;
}
update(data) {
console.log(`${this.name} received data: ${data}`);
}
}
// 测试用例
const publisher = new Publisher();
const subscriber1 = new Subscriber('subscriber1');
const subscriber2 = new Subscriber('subscriber2');
publisher.addSubscriber(subscriber1);
publisher.addSubscriber(subscriber2);
publisher.notify('Hello world!');
publisher.removeSubscriber(subscriber1);
publisher.notify('How are you?');
在上面的示例中,我们定义了两个类,Publisher和Subscriber。Publisher类用于添加和删除订阅者,并通知所有已添加的订阅者。Subscriber类用于接收并处理发布者发送的消息。
创建实例后,我们将subscriber1和subscriber2添加到publisher中,然后发送两条消息,第一条是Hello world!,第二条是How are you?,此时subscriber1已被移除。
最终的控制台输出结果为:
subscriber1 received data: Hello world!
subscriber2 received data: Hello world!
subscriber2 received data: How are you?
可以看到,subscriber1和subscriber2都收到了第一条消息。然后,subscriber1被移除,只有subscriber2在接收第二条消息。这就是发布订阅模式的基本流程。
-
单例模式 :是一个类只能实例化一次,并提供全局访问这个实例的方案。常用于管理全局状态、缓存、日志等。
-
单例模式可以有效的节省内存空间,同时避免出现多个实例导致的状态不一致问题,常用于需要在全局范围内有效共享实例等场景。
// 单例模式:只是实例化一次。每次返回的实例都是同一对象
function person(name){
this.name = name
}
var getInstance = (function(){
var instance
return function(name){
if(!instance) instance = new person(name)
return instance
}
})()
var person1 = getInstance('zhangsan')
var person2 = getInstance('lisi')
console.log(person1.name); //zhangsan
console.log(person2.name); //zhangsan
console.log(person1 === person2); //true
-
工厂模式 :一种创建对象的方式,原理是通过工厂函数(也称创建函数或构造器函数)来封装对象的创建过程,以实现灵活、高效的对象创建和管理。工厂函数可以是一个简单的对象创建函数,也可以是一个返回新对象的函数,其创建过程与构造函数类似,但不需要使用 new 关键字。
function createPerson(name, age, gender) {
return {
name: name,
age: age,
gender: gender,
sayHello: function() {
console.log('Hello, my name is ' + this.name);
}
};
}
var alice = createPerson('Alice', 25, 'female');
var bob = createPerson('Bob', 30, 'male');
console.log(alice.name); // Alice
console.log(bob.age); // 30
alice.sayHello(); // Hello, my name is Alice
bob.sayHello(); // Hello, my name is Bob
73.请描述一下 location.go(-1) 和 history.go(-1) 有什么区别
- 两个函数都可以用于页面回到上一页, location.go() 需要插件支持*
- location.go(-1)回到上一页并且刷新页面
- history.go(-1) ,回到上一页
History对象和location对象_如何获取url history-CSDN博客
7.JS代码中如何跳转页面?
跳转带参
<script language="javascript" type="text/javascript">
window.location.href="jingxuan.do?backurl=" rel="external nofollow" + window.location.href;
</script>
返回上一次预览页面
<script language="javascript">
alert("返回");
window.history.back(-1);
</script>
JS怎么获取鼠标的坐标
// 监听鼠标移动事件
document.addEventListener('mousemove', (event) => {
// 获取鼠标相对于整个文档的坐标位置
const mouseX = event.pageX;
const mouseY = event.pageY;
console.log('Mouse X:', mouseX, 'Mouse Y:', mouseY);
});
如何刷新当前页面?
reload() 方法:用于刷新当前文档;类似于你浏览器上的刷新页面按钮。
location.replace(URL)
<script language="JavaScript">
function myrefresh()
{
window.location.reload();
}
setTimeout('myrefresh()',1000); //指定1秒刷新一次
</script>
74.数组的方法concat、slice是深拷贝还是浅拷贝
对一维数组来说是深拷贝,对多维数组来说是浅拷贝
多维数组:连接两个或多个数组。( 该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。)
75.事件可以绑定,那么事件可以取消吗
1.对象属性绑定的事件,取消
<script>
// 绑定事件
_btn.onclick = function() {}
// 取消事件
_btn.onclick = null
</script>
2.标准语法绑定事件,取消
<script>
function handler(){
// 事件处理函数
}
// 绑定事件
_btn.addEventListener("click", handler)
// 取消指定事件
_btn.removeEventListener("click", handler)
</script>
76.白屏和首屏时间(如何计算白屏和首屏时间?onload事件发生了什么?)
白屏时间:从用户输入网址并点击回车,到浏览器开始显示内容(出现第一个字符或元素)的时间。
首屏时间:从用户输入网址并点击回车,到首屏内容(第一个页面)渲染完成的时间
影响白屏和首屏的时间:
-
网络连接速度:一个网页需要下载多少数据量以及用户所处的网络带宽都会影响到页面的加载速度。
-
网页的体积:网页中含有多少图片、视频、脚本、样式等都会影响到页面的加载速度。
-
服务器响应速度:网站服务器的响应速度也会影响页面的加载速度。
解决方法:
为了减少白屏和首屏时间,一些前端技术被提出。其中,代码分割技术是提高页面加载速度的重要手段之一。webpack 中的代码分割技术就是通过把代码分割成小块,按需加载而实现的。
具体来说,webpack 对JavaScript 文件进行代码分割的方式有两种:
-
同步分割:使用 import 语句,将代码拆分成多个文件,并可以根据需要进行加载。
-
异步分割:采用 import() 函数动态地从服务器中加载代码块,按需进行加载。
这样,通过代码分割,webpack 可以让页面加载时仅加载必需的代码,而其余代码通过按需加载的方式进行加载,从而提升加载速度,减少页面的白屏和首屏时间。
白屏结束时间 = FP事件触发时间
首屏结束时间 = FCP事件触发时间
FP(First Paint)表示渲染出第一个像素点的时间。FP一般在HTML解析完成或者解析一部分时候触发。
FCP(First Contentful Paint)表示渲染出第一个内容的时间,这里的“内容”可以是文本、图片、canvas。
Onload Event,它代表页面中依赖的所有资源:DOM、图片、CSS、Flash等都加载完。
// 指标计算方法
// FP
const fp = performance.getEntries('paint').filter(entry => entry.name == 'first-paint')[0].startTime;
// FCP
const fcp = performance.getEntries('paint').filter(entry => entry.name == 'first-contentful-paint')[0].startTime;
// Onload Event
const l = performance.timing.loadEventEnd - performance.timing.navigationStart;
77.表单可以跨域吗 ()不用看都是跨域问题
可以跨域,在被提交的接口所在的服务器上进行相应的设置,以允许跨域请求,设置有几种方式:
- 在请求的接口上设置允许跨域请求的header头,如允许跨域的域名,请求方法等信息
- 在被提交的接口响应头中加Access-Control-Allow-Origin字段,指定允许的跨域请求来源域名
router.all('*', function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*"); // 允许所有来源访问
res.header("Access-Control-Allow-Headers", "X-Requested-With");
res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS"); // 允许跨域请求的方法
next();
});
注意:跨域的表单提交可能会带来一些安全问题,例如被跨域访问的接口可能会被非法访问、CSRF 等攻击,因此在开发中需要谨慎处理。
78.搜索请求如何处理(防抖)
搜索请求防抖可以通过以下步骤进行处理:
- 监听搜索框的输入事件,例如input事件。
- 事件处理函数中,设置一个定时器(通常为300ms - 500ms),定时器回调函数中执行搜索操作。
- 每次输入事件发生时,如果定时器尚未到达设定时间,立即清除上一次的定时器,并重新设置一个新的定时器。
- 如果定时器已到达设定的时间,执行搜索操作,并清除定时器。
通过这种方式,可以将用户输入的一连串字符进行合并,并在一定时间内只发送一次搜索请求,从而减轻服务器的压力,优化搜索功能的体验。
或者失去焦点的时候请求
var hasFocus = $('input').is(':focus');
79.搜索请求中文如何请求
把中文进行URL编码,JS提供了encodeURIComponent()函数,服务器端对 URL 进行解码,用 decodeURIComponent()
函数。
let keyword = '中文关键词';
let encodedKeyword = encodeURIComponent(keyword);
console.log(encodedKeyword); // %E4%B8%AD%E6%96%87%E5%85%B3%E9%94%AE%E8%AF%8D
下面不用看=====
80.js鼠标事件,键盘事件
- keyup 按键弹起的时候触发
- keydown 按键按下的时候触发
- keypress 按键按下的时候触发,它识别不了shift、Ctrl这样的功能键
三个事件的执行顺序:keydown——keypress——keyup
鼠标:
click 单击鼠标左键时触发,右键按下则不会触发
dblclick 双击鼠标左键时触发,右键按下则不会触发
mousedown 鼠标按钮被按下时触发,左右键都可以触发
mouseout 鼠标指针位于某个元素上且将要移出元素的边界时触发
mouseover 鼠标指针移出某个元素到另一个元素上时触发
mousemove 鼠标在某个元素上触发
mouseleave 鼠标指针移出元素时触发
mouseenter 鼠标指针移动到元素上时触发
contextmenu 用户点击鼠标右键打开上下文菜单时触发
72.什么是cookie?
cookie 是网站应用开发中,服务器给浏览器客户端记录文本数据的一个对象;记录数据的时候有如下特点:
- 数据格式: key=value ,保存的数据只能是文本数据
- 有过期时间: expires=xxxx
- 不设置过期时间,称为临时 cookie ,会话结束数据删除
- 设置过期时间,称为永久 cookie ,过期时间一到立即删除
- 可以包含路径,给不同的路径保存不同的 cookie
- 一个网站,大部分浏览器限制最多保存50个 key=value 键值对
- 一个网站,大部分浏览器限制最多保存 4K 数据缺点 存储量太小,只有4KB
缺点 每次HTTP请求都会发送到服务端,影响获取资源的效率
缺点 需要自己封装获取、设置、删除cookie的方法
缺点 当超过单个域名限制之后,再设置cookie,浏览器就会清除以前设置的cookie。IE和Opera 会清理近期最少使用的cookie,FF会随机清理cookie
Cookie的工作原理和应用详解_不才Jerry的博客-CSDN博客_cookie的工作原理
73.如何使用 JS 删除 cookie
- 如果要删除cookie以便后续尝试读取cookie,则只需将过期日期设置为过去的时间。
- 咱们应该定义cookie路径以确保删除正确的cookie。
- 如果未指定路径,某些浏览器将不允许咱们删除cookie
54.getComputedStyle()和getPropertyValue()
- getComputedStyle()方法用于获取指定元素的css样式
- // 获取的样式是元素在浏览器中最终渲染效果的样式
- // getPropertyValue()方法返回指定的css属性的值
<style>
#elem-container{
width: 300px;
height: 300px;
background-color:red;
}
</style>
<div id="elem-container">测试</div>
<script>
let elem = document.getElementById("elem-container")
let theCSSprop = window.getComputedStyle(elem,null)
console.log(theCSSprop.width)
console.log(theCSSprop.backgroundColor)
console.log(theCSSprop.getPropertyValue("height"))
</script>
84.如何获取鼠标在窗口的位置?
属性及其兼容性
属性 说明 兼容性
- clientX 以浏览器窗口左上顶角为原点,定位 x 轴坐标 所有浏览器,不兼容 Safari
- clientY 以浏览器窗口左上顶角为原点,定位 y 轴坐标 所有浏览器,不兼容 Safari
- offsetX 以当前事件的目标对象左上顶角为原点,定位 x 轴坐标 所有浏览器,不兼容 Mozilla
- offsetY 以当前事件的目标对象左上顶角为原点,定位 y 轴坐标 所有浏览器,不兼容 Mozilla
- pageX 以 document 对象(即文档窗口)左上顶角为原点,定位 x 轴坐标 所有浏览器,不兼容 IE
- pageY 以 document 对象(即文档窗口)左上顶角为原点,定位 y 轴坐标 所有浏览器,不兼容 IE
- screenX 计算机屏幕左上顶角为原点,定位 x 轴坐标 所有浏览器
- screenY 计算机屏幕左上顶角为原点,定位 y 轴坐标 所有浏览器
layerX 最近的绝对定位的父元素(如果没有,则为 document 对象)左上顶角为元素,定位 x 轴坐标 Mozilla 和 Safari
layerY 最近的绝对定位的父元素(如果没有,则为 document 对象)左上顶角为元素,定位 y 轴坐标 Mozilla 和 Safari
什么是工程化?
前端工程可以定义为,将工程方法系统化地应用到前端开发中, 以系统、严谨、可量化的方法开发、运营、维护前端应用程序。
前端工程化流程:
创建项目 => 编码 => 预览/测试 => 提交 => 部署
创建项目:
在项目开发初期,我们可以实用工具自动创建一些脚手架、
模板、通用等文件;还能够创建项目结构、创建特定类型文件。
编码 :
在正式堆代码的时候,可能会有多人协同开发的场景;这时候需
要我们制定编码规范来约束开发人员的编码风格,并使用工具来
代替人为约定。除此之外,还可以使用一些自动化工具来替我们
自动构建、自动编译打包。
预览/测试:
在开发本地调试的时候,我们可以使用一些工具来模拟服务器场
景并实现热更新、热加载;即代码修改后自动编译构建,浏览器根
据变化自动刷新同时还要方便我们查看源码。
提交:
Git Hooks:可在提交前进行代码质量和风格的检查
Lint-staged 持续集成
部署:
自动化部署:CI/CD
自动化集成:Jenkins 可以调用执行脚本,集成自动化构建、打包、
部署等。
74.实现一个简单的观察者模式
可以说:react中有一种观察者模式
https://www.jb51.net/article/230975.htm
观察者模式一种设计模式
观察者模式定义了对象间的一对多的依赖关系,当一个对象的状态发送改变时,所有依赖它的对象都将得到状态改变的通知
73.JS有三类的错误:( JS 中的主要有哪几类错误)
- 加载时错误:加载web页面时出现的错误(如语法错误)称为加载时错误,它会动态生成错误。
- 运行时错误:由于滥用HTML语言中的命令而导致的错误。
- 逻辑错误:这些错误是由于对具有不同操作的函数执行了错误的逻辑而导致的
说一下slice splice split 的区别?
// slice(start,[end]) // slice(start,[end])方法:该方法是对数组进行部分截取,该方法返回一个新数组 // 参数start是截取的开始数组索引,end参数等于你要取的最后一个字符的位置值加上1(可选)。 // 包含了源函数从start到 end 所指定的元素,但是不包括end元素,比如a.slice(0,3); // 如果出现负数就把负数与长度相加后再划分。 // slice中的负数的绝对值若大于数组长度就会显示所有数组 // 若参数只有一个,并且参数大于length,则为空。 // 如果结束位置小于起始位置,则返回空数组 // 返回的个数是end-start的个数 // 不会改变原数组 var arr = [1,2,3,4,5,6] /*console.log(arr.slice(3))//[4,5,6] 从下标为0的到3,截取3之后的数 console.log(arr.slice(0,3))//[1,2,3] 从下标为0的地方截取到下标为3之前的数 console.log(arr.slice(0,-2))//[1,2,3,4] console.log(arr.slice(-4,4))//[3,4] console.log(arr.slice(-7))//[1,2,3,4,5,6] console.log(arr.slice(-3,-3))// [] console.log(arr.slice(8))//[]*/ // 个人总结:slice的参数如果是正数就从左往右数,如果是负数的话就从右往左边数, // 截取的数组与数的方向一致,如果是2个参数则截取的是数的交集,没有交集则返回空数组 // ps:slice也可以切割字符串,用法和数组一样,但要注意空格也算字符 // splice(start,deletecount,item) // start:起始位置 // deletecount:删除位数 // item:替换的item // 返回值为被删除的字符串 // 如果有额外的参数,那么item会插入到被移除元素的位置上。 // splice:移除,splice方法从array中移除一个或多个数组,并用新的item替换它们。 //举一个简单的例子 var a=['a','b','c']; var b=a.splice(1,1,'e','f'); console.log(a) //['a', 'e', 'f', 'c'] console.log(b) //['b'] var a = [1, 2, 3, 4, 5, 6]; //console.log("被删除的为:",a.splice(1, 1, 8, 9)); //被删除的为:2 // console.log("a数组元素:",a); //1,8,9,3,4,5,6 // console.log("被删除的为:", a.splice(0, 2)); //被删除的为:1,2 // console.log("a数组元素:", a) //3,4,5,6 console.log("被删除的为:", a.splice(1, 0, 2, 2)) //插入 第二个数为0,表示删除0个 console.log("a数组元素:", a) //1,2,2,2,3,4,5,6 // split(字符串) // string.split(separator,limit):split方法把这个string分割成片段来创建一个字符串数组。 // 可选参数limit可以限制被分割的片段数量。 // separator参数可以是一个字符串或一个正则表达式。 // 如果separator是一个空字符,会返回一个单字符的数组,不会改变原数组。 var a="0123456"; var b=a.split("",3); console.log(b);//b=["0","1","2"] // 注意:String.split() 执行的操作与 Array.join 执行的操作是相反的。
说一下JSON.stringify有什么缺点?
1.如果obj里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式,而不是对象的形式
2.如果obj里有RegExp(正则表达式的缩写)、Error对象,则序列化的结果将只得到空对象;
3、如果obj里有函数,undefined,则序列化的结果会把函数或 undefined丢失;
4、如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null
5、JSON.stringify()只能序列化对象的可枚举的自有属性,例如 如果obj中的对象是有构造函数生成的, 则使用JSON.parse(JSON.stringify(obj))深拷贝后,会丢弃对象的constructor;
6、如果对象中存在循环引用的情况也无法正确实现深拷贝;
70. IE和标准下有哪些兼容性的写法
答案:
var ev = ev || window.event
document.documentElement.clientWidth || document.body.clientWidth
Var target = ev.srcElement||ev.target
中高级开发面试题
https://blog.csdn.net/qq_40055200/article/details/127551538?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22127551538%22%2C%22source%22%3A%22qq_40055200%22%7D