JavaScript基础面试题理解

封在寝室的这些日子,新的知识不是很能学进去,就拉着王帅花了一部分时间把之前学习的JS复习了一遍,外加一些常见习题及题解。

1.延迟加载 JS的方式有哪些

1.async
html与js 是一起加载的,js谁先加载完毕 先执行谁
2.defer
html加载完毕后 js将进行同步加载

2.数据类型和考题

基本数据类型
string number undefined symbol null bigint boolean
对象数据类型
object
Array function
考题

console.log( true + 1 ); 2
console.log( 'name'+ true ); 'nametrue'
console.log( undefined + 1 ); undefined
console.log( typeof undefined ); undefined

console.log( typeof(number)); undefined
console.log( typeof(Number)); function
console.log( typeof(NaN)); number
console.log( typeof(null) ); object(通常我们给变量赋空值时 var a = null)

3.null与undefined 的区别

var a; 与 var a = null 的区别
1
null与undefined都表示为空 ,主要区别在于 undefined 表示尚未初始化的变量的值 (注:声明与定义不是一回事,这里指的是仅声明),而 null 表示该变量有意缺少对象指向(声明了,定义为null,有意初始化该变量值)

undefined
已声明 未定义
隐藏式(未定义) 空值

null
值声明且定义,但它并未指向任何内存中的对象(注:null是object类型)
声明式 空值

4.== 与 === 有什么不同

console.log(null === undefined)
console.log(typeof (null))
console.log(typeof (undefined))
1
2
3
== 值的校验
=== 类型与值的校验

5.JS宏任务与微任务

宏任务与微任务的定义
js是单线程语言,执行流程分为同步与异步
同步代码执行完毕后,执行异步代码,异步分宏微任务

执行顺序
js代码执行流程:同步执行完==》事件循环
同步的任务都执行完了,才会执行事件循环的内容
进入事件循环:请求、定时器、事件…
事件循环中包含:【微任务、宏任务】
要执行宏任务的前提是清空了所有的微任务
流程:同步==》事件循环【微任务和宏任务】》微任务》宏任务=》微任务…
宏任务:

微任务:

习题:

console.log('start');

function foo () {
console.log(' ');
}
foo()
setTimeout(function () {
console.log('setTimeout');
}, 1000)

new Promise(resolve => {
console.log('promise'); //注意 这里是同步任务
resolve()
})
.then(function () {

console.log('promise1');

})
.then(function () {

console.log('promise2');

})

console.log('end');
答案: start foo promise end promise1 promise2 setTimeout
1
宏任务与微任务

6.JS作用域考题

1.只有函数拥有块级作用域
2.作用域链:内部可以访问外部的变量,但是外部不能访问内部的变量。
注意:如果内部有,优先查找到内部,如果内部没有就查找外部的。

  1. 注意声明变量是用var还是没有写(window)
  2. 注意:js有变量提升的机制【变量悬挂声明】
  3. 优先级:声明变量 > 声明普通函数 > 参数 > 变量提升
    1
    2
    3
    4
    5
    6
    习题一

function c(){

var b = 1;
function a(){
    console.log( b );
    var b = 2;
    console.log( b );
}
a();
console.log( b );

}
c();
1
2
3
4
5
6
7
8
9
10
11
习题二

var name = 'a';
(function(){

if( typeof name == 'undefined' ){
    var name = 'b';
    console.log('111'+name);
}else{
    console.log('222'+name);
}

})()
1
2
3
4
5
6
7
8
9
习题三:

function fun( a ){

var a = 10;
function a(){}
console.log( a );

}
fun( 100 );
1
2
3
4
5
6

7.JS对象考题

1.对象是通过new操作符构建出来的,所以对象之间不想等(除了引用外);
2.对象注意:引用类型(共同一个地址);
3.对象的key都是字符串类型

var name = {
a: 1,
b: "11"
};
for (key in name) {
console.log(typeof (key));
}
1
2
3
4
5
6
7
对象如何找属性|方法;
查找规则:先在对象本身找 ===> 构造函数中找 ===> 对象原型中找 ===> 构造函数原型中找 ===> 对象上一层原型查找
习题1:
[1,2,3] === [1,2,3] //false
1
习题二

var obj1 = {

a:'hellow'

}
var obj2 = obj1;
obj2.a = 'world';
console.log(obj1); //{a:world}
(function(){

console.log(a);     //undefined
var a = 1;

})();
1
2
3
4
5
6
7
8
9
10
习题三:

var a = {}
var b = {

key:'a'

}
var c = {

key:'c'

}

a[b] = '123';
a[c] = '456';

console.log( a[b] ); // 456 (如果不理解,请遍历打印a的key)

8.JS作用域+this指向+原型考题

习题1:

function Foo(){

getName = function(){console.log(1)} //注意是全局的window.
return this;

}

Foo.getName = function(){console.log(2)}
Foo.prototype.getName = function(){console.log(3)}
var getName = function(){console.log(4)}
function getName(){

console.log(5)

}

Foo.getName(); //2
getName(); //4
Foo().getName(); //1
getName(); //1
new Foo().getName();//3

习题二:

var o = {

a:10,
b:{
    a:2,
    fn:function(){
        console.log( this.a ); // 2
        console.log( this );   //代表b对象
    }
}

}
o.b.fn();

习题三:

window.name = 'ByteDance';
function A(){

this.name = 123;

}
A.prototype.getA = function(){

console.log( this );
return this.name + 1;

}
let a = new A();
let funcA = a.getA;
funcA(); //this代表window

习题四

var length = 10;
function fn(){

return this.length + 1;

}
var obj = {

length:5,
test1:function(){
    return fn();
}

}
obj.test2 = fn;
console.log( obj.test1() ); //1
console.log( fn()===obj.test2() ); //false
console.log( obj.test1() == obj.test2() ); //false

迷惑文章(闭包与原型链):
https://blog.csdn.net/m0_65912225/article/details/124120364

9.JS判断变量是不是数组的多个方法

方法一:Array的isArray方法
var arr = [1,2,3];
console.log( Array.isArray( arr ) );
1
2
方法二 数组对象的构造函一定是Array
var a = {a:1,b:2}
console.log(a.constructor);
var b = [1,2]
console.log(b.constructor === Array);
1
2
3
4
方法三 instanceof (可写可不写)
运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性。

var arr = [1,2,3];
console.log( arr instanceof Array );
1
2
方法四 原型prototype 使该对象拥有object的toString方法
var arr = [1,2,3];
console.log( Object.prototype.toString.call(arr).indexOf('Array') > -1 );
1
2
方法五 isPrototypeOf()

   var arr = [1,2,3];
console.log(  Array.prototype.isPrototypeOf(arr) )
//下述代码是上述代码的解释
var arr  = [1,2]
console.log(Array.prototype);
console.log(arr.__proto__);

1
2
3
4
5
6
7

10.slice是干嘛的 splice是否会改变原数组

slice(start,end)
slice是来截取的
参数可以写slice(3)、slice(1,3)、slice(-3)
返回的是一个新的数组

splice 功能有:插入、删除、替换
返回:删除的元素
该方法会改变原数组

11.JS数组去重

方式一:new set(arr)

var arr1 = [5,1,2,3,2,4,1];
console.log(...new Set(arr1));
1
2
方式二:indexOf

var arr2 = [1,2,3,2,4,1];
function unique( arr ){

var brr = [];
for( var i=0;i<arr.length;i++){
    if(  brr.indexOf(arr[i]) == -1 ){
        brr.push( arr[i] );
    }
}
return brr;

}
console.log( unique(arr2) );
1
2
3
4
5
6
7
8
9
10
11
方式三:sort

var arr3 = [1,2,3,2,4,1];
function unique( arr ){

arr = arr.sort();
var brr = [];
for(var i=0;i<arr.length;i++){
    if( arr[i] !== arr[i-1]){
        brr.push( arr[i] );
    }
}
return brr;

}
console.log( unique(arr3) );

方式四:splice

var arr = [1, 1, 8, 8, 12, 12, 15, 15, 16, 16];

function unlink(arr) {

for (var i = 0; i < arr.length; i++) {    // 首次遍历数组
    for (var j = i + 1; j < arr.length; j++) {   // 再次遍历数组
        if (arr[i] == arr[j]) {          // 判断连个值是否相等
            arr.splice(j, 1);           // 相等删除后者
            j--;
        }
    }
}
return arr

}
console.log(unlink(arr));
1
2
3
4
5
6
7
8
9
10
11
12
13
14

12.找出多维数组最大值

function fnArr(arr){

var newArr = [];
arr.forEach((item,index)=>{
    newArr.push( Math.max(...item)  )
})
return newArr;

}
console.log(fnArr([

[4,5,1,3],
[13,27,18,26],
[32,35,37,39],
[1000,1001,857,1]

]));

13.给字符串新增方法实现功能

给字符串对象定义一个addPrefix函数,当传入一个字符串str时,它会返回新的带有指定前缀的字符串,例如:
console.log( ‘world’.addPrefix(‘hello’) ) 控制台会输出helloworld

解答:
String.prototype.addPrefix = function(str){

return str  + this;

}
console.log( 'world'.addPrefix('hello') )

14.找出字符串出现最多次的数字var str = ‘aaabbbbbccddddddddddx’;

var obj = {};
for(var i=0;i<str.length;i++){

var char = str.charAt(i);
if( obj[char] ){
    obj[char]++;
}else{
    obj[char] = 1;
}

}
console.log( obj );
//统计出来最大值
var max = 0;
for( var key in obj ){

if( max < obj[key] ){
    max = obj[key];
}

}
//拿最大值去对比
for( var key in obj ){

if( obj[key] == max ){
    console.log('最多的字符是'+key);
    console.log('出现的次数是'+max);
}

}

15.new 操作符具体做了什么

  1. 创建了一个空的对象
  2. 将空对象的原型,指向于构造函数的原型
  3. 将空对象作为构造函数的上下文(改变this指向)
  4. 对构造函数有返回值的处理判断
    1
    2
    3
    4
    function Fun( age,name ){
    this.age = age;
    this.name = name;
    }
    function create( fn , ...args ){
    //1. 创建了一个空的对象
    var obj = {}; //var obj = Object.create({})
    //2. 将空对象的原型,指向于构造函数的原型
    Object.setPrototypeOf(obj,fn.prototype);
    //3. 将空对象作为构造函数的上下文(改变this指向)
    var result = fn.apply(obj,args);
    //4. 对构造函数有返回值的处理判断
    return result instanceof Object ? result : obj;
    }
    console.log( create(Fun,18,'张三') )

16.闭包

什么是闭包
概念·:一个函数(函数内的函数)和他的周围状态(函数内函数的环境)的引用捆绑在一起的组合(晦涩)
理解:使外部能够访问函数内的自由变量

两个闭包的体现
案列一:将函数作为返回值

function fun(){

  var count = 1;
    return function (){//匿名函数
      console.log("count" + count);  //匿名函数内并没有定义count,所以会去上级作用域内查找,此时所要查找的是匿名函数定义                                 位置作用域,还是匿名函数执行位置外作用域??
    }
} 
var fun2 = fun(); //将返回值保存至fun2
var count = 3 ;
fun2 ();  // 1 由此可得知,count这个自由变量会向上一层 去定义时函数作用域内查找此变量的值,而不是执行函数的作用域内查找。

1
2
3
4
5
6
7
8
9
解析:
案列二:函数作为参数

function fun(fun2){

  var count = 1;
  fun2();  
} 
var count = 3 ;
function fun2(){        //当执行fun2这个函数时 count 是自由变量
console.log("count" + count);  //也是输出定义是作用域内变量
}
fun(fun2);

1
2
3
4
5
6
7
8
9
习题
var n = 1;

function fun1() {
  test = 10;
  var n = 999;
  nAdd = function () {
    n += 1;
    console.log(n);
  }

  function fun2() {
    console.log(n);
  }
  return fun2;
}
var result = fun1();
result();    //999
console.log(test);   //10
console.log(n);   //1
nAdd();   //1000
result();//1000

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
实际应用场景
检测一个接口的调用次数
使用闭包

function fun(){

  var count = 0;
  return function func(){
    count++;
    // 接口的操作
    console.log(count);//调用接口的次数
  }
}
var fun2  = fun()
fun2()

17.原型链

原型链可以解决什么问题?
对象共享属性和共享方法

谁有原型
函数拥有:prototype
对象拥有:proto

对象查找属性或者方法的顺序
先在对象本身查找 --> 构造函数中查找 --> 对象的原型 --> 构造函数的原型中 --> 当前原型的原型中查找

原型链
是什么?:就是把原型串联起来
原型链的顶端是null
var a = {}
console.log(a.prototype); //显示原型
console.log(a.__proto__); //underfined
function Person(name) {

this.name = name

}
console.log(Person.__proto__) //隐式原型
console.log(Person.prototype) //显示原型
1
2
3
4
5
6
7
8

18.JS继承有哪些方式

方式一:

class Parent{

constructor(){
    this.age = 18;
}

}

class Child extends Parent{

constructor(){
    super();
    this.name = '张三';
}

}
let o1 = new Child();
console.log( o1,o1.name,o1.age );
1
2
3
4
5
6
7
8
9
10
11
12
13
14
方式二:原型链继承

function Parent(){

this.age = 20;

}
function Child(){

this.name = '张三'

}
Child.prototype = new Parent();
let o2 = new Child();
console.log( o2,o2.name,o2.age );
1
2
3
4
5
6
7
8
9
方式三:借用构造函数继承

function Parent(){

this.age = 22;

}
function Child(){

this.name = '张三'
Parent.call(this);

}
let o3 = new Child();
console.log( o3,o3.name,o3.age );
1
2
3
4
5
6
7
8
9

19.说一下call 与 apply bind的 区别

共同点 功能一致
可以改变this指向

区别
call apply可以立即执行。bind不会立即执行,因为bind返回的是一个函数需要调用
参数不同:apply第二个参数是数组。call和bind有多个参数需要挨个写。
场景

  1. 用apply的情况
    var arr1 = [1,2,4,5,7,3,321];
    console.log( Math.max.apply(null,arr1) )
  2. 用bind的情况
    var btn = document.getElementById('btn');
    var h1s = document.getElementById('h1s');
    btn.onclick = function(){
    console.log( this.id );
    }.bind(h1s)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    20.sort背后原理是什么
    V8引擎sort 函数只给出了两种排序 InsertionSort 和 QuickSort,数量小于10的数组使用InsertionSort,比10大的数组则使用 QuickSort。
    之前的版本是:插入排序和快排,现在是冒泡

原理实现链接:https://github.com/v8/v8/blob/ad82a40509c5b5b4680d4299c8f08d6c6d31af3c/src/js/array.js
710行代码开始
1
2
21.深拷贝与浅拷贝
共同点
复制

浅拷贝
只复制引用,而未复制真正的值。

var arr1 = ['a','b','c','d'];
var arr2 = arr1;

var obj1 = {a:1,b:2}
var obj2 = Object.assign(obj1);
1
2
3
4
5
深拷贝
概念复制真正的值
var obj3 = {

a:1,
b:2

}
var obj4 = JSON.parse(JSON.stringify( obj3 ));
1
2
3
4
5
递归方法

function copyObj( obj ){

if(  Array.isArray(obj)  ){
    var newObj = [];
}else{
    var newObj = {};
}
for( var key in obj ){
    if( typeof obj[key] == 'object' ){
        newObj[key] = copyObj(obj[key]);
    }else{
        newObj[key] = obj[key];
    }
}
return newObj;

}
console.log( copyObj(obj5) );

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

22.localStorage、sessionStorage、cookie的区别

共同点:在客户端存放数据
不同点:

存放数据的有效期 sessionStorage:进当前浏览器窗口关闭之前有效,关闭后就消失。 loaclStorage:始终有效,窗口或浏览器关闭也一直保存,所以叫做持久化存储
localStorage、sessionStorage不可以设置过期时间
cookie 有过期时间,可以设置过期(把时间调整到之前的时间,就过期了)
存储大小的限制
cookie存储量不能超过4k
localStorage、sessionStorage不能超过5M
**根据不同的浏览器存储的大小是不同的。

23.var let const的区别

共同点:var const let 都是可以声明变量的
不同点:
区别一:
var 具有变量提升机制
let 和 const 没有变量提升机制

区别二:
var 可以多次声明
let 和 const 不可以多次声明同一个变量

区别三:
var let 声名变量的
const 声明常量
var 和 let 声明的变量可以再次赋值,但是const不可以再次赋值了。

区别四
var 声明的变量没有自身作用域
let与const声明的变量有自身的作用域

24.作用域考题

考题一:let和const没有变量提升性
console.log( str );//undefined
var str = '你好';

console.log( num );//报错
let num = 10;
1
2
3
4
5
考题二:
function demo(){

var n = 2;
if( true ){
    var n = 1;
}
console.log( n );//1

}
demo();

function demo(){

let n = 2;
if( true ){
    let n = 1;
}
console.log( n );//2

}
demo();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
考题三:可以修改
const obj = {

a:1

}
obj.a = 11111;
console.log( obj )

const arr = ['a','b','c'];
arr[0]= 'aaaaa';
console.log( arr );
1
2
3
4
5
6
7
8
9

25.将下列对象进行合并

方式一:Object.assign
const a = {a:3,b:4},
const b = {b:2,c:4}
let obj = Object.assigon(a,b);
console.log(obj)
1
2
3
4
方式二:扩展运算符
let obj1 = {...a,...b}
console.log(obj1)
1
2
方式三:自己封装的方法
function extend( target, source ){

for(var key in source){
    target[key] = source[key];
}
return target;

}
console.log( extend(a,b) );

26.箭头函数和普通函数区别

1、外形不同:箭头函数使用箭头定义,普通函数中没有。
2、 箭头函数全都是匿名函数:普通函数可以有匿名函数,也可以有具名函数
3、箭头函数不能用于构造函数:普通函数可以用于构造函数,以此创建对象实例。
4、箭头函数中 this 的指向不同:在普通函数中,this 总是指向调用它的对象,如果用作构造函数,它指向创建的对象实例。
5、箭头函数不具有 arguments 对象:每一个普通函数调用后都具有一个
arguments 对象,用来存储实际传递的参数。但是箭头函数并没有此对象。
6、其他区别:箭头函数不具有 prototype 原型对象。箭头函数不具有 super。
7、箭头函数不能new(不能当作构造函数)
1
2
3
4
5
6
7
8

27.Promise有几种状态

有三种状态:
pending(进行中)
fulfilled(已成功)
rejected(已失败)
1
2
3
4

28.find与 fitter的区别

区别一:返回的内容不同

filter 返回是新数组
find   返回具体的内容

区别二:

find :匹配到第一个即返回
filter : 返回整体(没一个匹配到的都返回)

1
2
3
4
5
6

29.some与 every 的区别

some ==》 如果有一项匹配则返回true
every ==》 全部匹配才会返回true

最后修改:2023 年 02 月 01 日
如果觉得我的文章对你有用,请随意赞赏