# 常见js面试题
# js数据类型有哪些
基本:number、string、boolean、undefined、null、symbol、bigint
复杂:对象(object、array、function、date)
# 判断数据类型方法
- typeof 只能用于判断除了null的基础类型
typeof null // object
1
- instanceof 只能用于判断复杂类型
[] instanceof Array // true
let a = {}; a instanceof Object // true
let a = new Date(); a instanceof Date // true
let a = console.log; a instanceof Function // true
// 基础类型检查不出来
1 instanceof Number // false
1
2
3
4
5
6
7
2
3
4
5
6
7
- constructor
null、undefined没有construstor方法,因此constructor不能判断undefined和null
let a = 3; a.constructor === Number // true
let a = 0n; a.constructor === BigInt
1
2
2
- Object.prototype.toString.call() 所有类型
Object.prototype.toString.call(1) === '[object Number]'// true
1
# 如何判断一个变量是数组类型
知识点: 构造函数
- 函数名称以大写字母开头
- 所有的引用类型(对象、数组、函数)都有构造函数
- var a={ }其实是var a=new Object( )的语法糖
- var a=[ ]其实是var a=new Array( )的语法糖
- function Foo( ){ }其实是var Foo=new Function ( )
- 使用 instanceof 判断一个函数是否是一个变量的构造函数
用instanceof判断:变量 instanceof Array
var arr=[];
arr instanceof Array; //true
typeof arr; //object , typeo是无法判断是否是数组的
1
2
3
2
3
# 写一个能遍历对象和数组的通用函数
//遍历对象可以用for...in...,遍历数组可以用forEach
function forEach(obj) {
var key
if (obj instanceof Array) {
obj.forEach(item => {
console.log(item)
})
} else {
for (key in obj) {
console.log(key, obj[key])
}
}
}
var arr = [1, 2, 3, 4, 5]
forEach(arr) //输出: 1 2 3 4 5
var obj = { x: 1, y: 2, z: 3 }
forEach(obj) //输出: x 1 y 2 z 3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 解析url参数为对象
# 考点/思路
考察数组与字符串处理的知识, 常见数组和字符串方法
- 通过字符
?
分割获取参数 - 通过字符
&
将字符串参数分割为数组 - 通过字符
=
处理参数为对象格式
# 代码实现
function urlSearch(href) {
let name, value
let str = href //取得整个地址栏
let num = str.indexOf("?")
str = str.substr(num + 1) //取得所有参数
let arr = str.split("&") //各个参数放到数组里
let json = {}
for (let i = 0; i < arr.length; i++) {
num = arr[i].indexOf("=")
if (num > 0) {
name = arr[i].substring(0, num)
value = arr[i].substr(num + 1)
json[name] = value
}
}
return json
}
urlSearch('https://www.baidu.com/s?ie=UTF-8&wd=csdn') // {ie: "UTF-8", wd: "csdn"}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 获取一定范围内n个随机整数
# 知识点
math.floor(x)
返回小于参数x的最大整数,即对浮点数向下取整math.random()
取得某个范围内的随机数
# 代码实现
function getRandomNumber(start, end, n) {
var arr = []
for (var i = 0; i < n; i++) {
var number = Math.floor(Math.random() * (end-start + 1) + start)
if (arr.indexOf(number) < 0) {
arr.push(number)
} else {
i--
}
}
return arr
}
getRandomNumber(1, 20, 5) // [11, 2, 8, 16, 10]
getRandomNumber(1, 20, 5) // [2, 8, 3, 12, 11]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# for-in 和for-of 的区别
参考文章: https://www.jianshu.com/p/c43f418d6bf0 (opens new window) https://segmentfault.com/q/1010000006658882 (opens new window)
知识点
- for-of适用于拥有迭代器对象的集合,但是不能遍历普通对象,因为没有迭代器对象,与forEach()不同的是,它可以正确响应break、continue和return语句
- 推荐在循环对象属性的时候,使用fo-in,在遍历数组的时候的时候使用for-of
- for-of不能循环普通的对象,需要通过和Object.keys()搭配使用
- for-in会遍历数组和对象所有的可枚举属性,包括原型和自定义属性, 而for-of只会遍历自身可枚举的
# for-in
先看实例:
- 遍历数组
Array.prototype.method=function(){
console.log(this.length);
}
var myArray=[1,2,4]
myArray.name="数组"
for (var index in myArray) {
console.log(myArray[index]);
}
// 打印结果
1
2
4
数组
ƒ (){
console.log(this.length);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- 遍历对象
Object.prototype.method=function(){
console.log(this);
}
var myObject={
a:1,
b:2,
c:3
}
for (var key in myObject) {
console.log(key);
}
// 打印结果
a
b
c
method
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
for-in遍历的特点
- 对于数组遍历的是数组的索引(即键名),而不是value,对于对象遍历的是属性
- 会遍历数组和对象所有的可枚举属性,包括原型和自定义属性
对于对象,如果不想遍历原型方法和属性的话,可以在循环时使用hasOwnPropery方法判断某属性是否是该对象的实例属性
Object.prototype.method=function(){
console.log(this);
}
var myObject={
a:1,
b:2,
c:3
}
for (var key in myObject) {
if (myObject.hasOwnProperty(key)) {
console.log(key);
}
}
// 打印结果
a
b
c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# for-of
for-of是ES6的语法
- 数组遍历
Array.prototype.method=function(){
console.log(this.length);
}
var myArray=[1,2,4]
myArray.name="数组";
for (var value of myArray) {
console.log(value);
}
// 打印结果
1
2
4
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
- 字符串遍历
var str = 'hello'
for (var st of str) {
console.log(st)
}
// 打印结果
h
e
l
l
o
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
for-of遍历的特点
- 遍历的是数组元素值而不是索引,for-of不能循环遍历普通对象
- 不会遍历原型上的属性和方法
- 支持字符串遍历
- 定义一个对象,使之可以用 for-of 循环遍历
参考学习:ES6入门-阮一峰 (opens new window)
只有可遍历的(iterable)的对象才能使用 of 循环遍历。可以通过给对象添加iterator接口来实现普通对象的for-of循环
let obj = {
data: [ 'hello', 'world' ],
[Symbol.iterator]() {
const self = this;
let index = 0;
return {
next() {
if (index < self.data.length) {
return {
value: self.data[index++],
done: false
};
} else {
return { value: undefined, done: true };
}
}
};
}
};
for (var key of obj) {
console.log(key)
}
// 打印结果
hello
world
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
- 如果实在想用for...of来遍历普通对象的属性的话,可以通过和Object.keys()搭配使用,先获取对象的所有key的数组 然后遍历:
var obj = {
a: 1,
b: 'fmy'
}
console.log(Object.keys(obj)) // ["a", "b"]
for (var key of Object.keys(obj)) {
console.log(`${key}: ${obj[key]}`)
}
// 打印结果
a: 1
b: fmy
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 防抖与节流
概念
- 防抖: 当连续触发事件时,最后一次触发事件后,只有任务触发的间隔超过指定间隔的时候,任务才会执行
- 节流: 指定时间间隔内只会执行一次任务,当连续触发事件时,事件只按自己的时间间隔执行
应用场景
- 防抖
- 输入框搜索联动词频繁调用接口时
- 节流
- 懒加载要监听计算滚动条的位置,使用节流按一定时间的频率获取
- 用户点击提交按钮,假设我们知道接口大致的返回时间的情况下,我们使用节流,只允许一定时间内点击一次
如果对上面的概念还不理解,我们看下面的示例,手动操作一下
- 防抖
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>防抖</title>
</head>
<body>
<button id="debounce">点我防抖!</button>
<script>
window.onload = function() {
// 1、获取这个按钮,并绑定事件
var myDebounce = document.getElementById("debounce");
myDebounce.addEventListener("click", debounce(sayDebounce));
}
// 2、防抖功能函数,接受传参
function debounce(fn) {
// 4、创建一个标记用来存放定时器的返回值
let timeout = null;
return function() {
// 5、每次当用户点击/输入的时候,把前一个定时器清除
if (timeout) clearTimeout(timeout);
// 6、然后创建一个新的 setTimeout,
// 这样就能保证点击按钮后的 interval 间隔内
// 如果用户还点击了的话,就不会执行 fn 函数
timeout = setTimeout(() => {
fn()
// fn.call(this, arguments);
}, 1000);
};
}
// 3、需要进行防抖的事件处理
function sayDebounce() {
// ... 有些需要防抖的工作,在这里执行
console.log("防抖成功!");
}
</script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
- 节流
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>节流</title>
</head>
<body>
<button id="throttle">点我节流!</button>
<script>
window.onload = function() {
// 1、获取按钮,绑定点击事件
var myThrottle = document.getElementById("throttle");
myThrottle.addEventListener("click", throttle(sayThrottle));
}
// 2、节流函数体
function throttle(fn) {
// 4、通过闭包保存一个标记
let canRun = true;
return function() {
// 5、在函数开头判断标志是否为 true,不为 true 则中断函数
if(!canRun) {
return;
}
// 6、将 canRun 设置为 false,防止执行之前再被执行
canRun = false;
// 7、定时器
setTimeout( () => {
fn.call(this, arguments);
// 8、执行完事件(比如调用完接口)之后,重新将这个标志设置为 true
canRun = true;
}, 1000);
};
}
// 3、需要节流的事件
function sayThrottle() {
console.log("节流成功!");
}
</script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
← 前言 JS构造函数-原型-原型链 →