0


前端:每天5道面试题(JavaScript)

1. 说说JavaScript中的数据类型?
基本数据类型:
  1. Number:用于表示数字,可以是整数或浮点数,还包括 Infinity-InfinityNaN(Not a Number)。
  2. String:表示文本数据,可以是一个字符或一串字符,使用单引号、双引号或反引号来定义。
  3. Boolean:有两个值,truefalse,用于进行逻辑操作。
  4. Undefined:当一个变量被声明了但没有被赋值时,它的值就是 undefined
  5. Null:表示一个空值,通常用于表示某个对象变量现在没有指向任何对象。
  6. Symbol(ES6 新增):表示独一无二的值,主要用于创建对象的唯一属性名。
  7. BigInt(近期新增):用于表示大于 2^53 - 1 的整数。
引用数据类型:
  1. Object:JavaScript 中的对象是键值对的集合。
  2. Array:用于表示元素的有序集合。
  3. Function:函数实际上是一种特殊类型的对象,可以被调用。
  4. Date:用于处理日期和时间。
  5. RegExp:用于执行正则表达式匹配。
2. 说说你对闭包的理解?闭包使用场景

我的理解是 就是函数包函数 里面的函数引用外层函数的变量的组合就是闭包

闭包是指有权访问另一个函数作用域中变量的函数

最简单的例子
functionA(){let count =1functionB(){
    count++}B()}
使用场景
  1. 数据封装和信息隐藏:通过闭包,可以创建私有变量,避免全局变量的污染。functioncreateCounter(){let count =0;return{increment:function(){ count++;return count;},decrement:function(){ count--;return count;}};}const counter =createCounter();console.log(counter.increment());// 1console.log(counter.increment());// 2console.log(counter.decrement());// 1
  2. 在循环中创建局部作用域:解决循环中使用异步函数导致的变量作用域问题。for(var i =0; i <5; i++){(function(j){setTimeout(function(){ console.log(j);}, j *1000);})(i);}
  3. 函数柯里化:通过闭包,可以实现函数的柯里化,预先设置一些参数。
functioncurriedAdd(a){returnfunction(b){return a + b
  }}const addFive =curriedAdd(5)
console.log(addFive(3))// 输出:8
闭包的注意事项

虽然闭包非常有用,但它们也可能导致内存泄漏,因为闭包中的外部函数作用域的变量不会被垃圾回收器回收,直到闭包本身被销毁。因此,在使用闭包时应当注意不要过度使用,以避免占用过多内存。

3. JavaScript中的原型,原型链分别是什么?

一张图就能全部理解

2292203-20220921194552812-492799817.png

__proto__

: 隐式原型,是每个对象都具有的属性

prototype

: 显式原型,是Function独有的属性

functionStudent(name, age){this.name = name;this.age = age;}// 原型上挂载方法Student.prototype.getName=function(){returnthis.name;};const s1 =newStudent("张三",18);// 构造函数// 这样的就可以访问到原型上的方法了
console.log("🚀 ~ s1:", s1.getName());// 当实例对象,在自身找不到时,会向上一级原型(Student.prototype)对象上查找;// 如果还找不到,则 又会到原型对象上一级的原型对象(Object.prototype)上查找,这种链式查找机制,称为原型链
4. Javascript如何实现继承?

在 JavaScript 中实现继承主要有几种方式:

原型链继承
// 父类构造函数functionAnimal(name){this.name = name;this.colors =["black","white"];this.sleep=function(){
    console.log(this.name +"正在睡觉!");};}// 子类构造函数functionCat(name){this.name = name;}// 原型指向 AnimalCat.prototype =newAnimal();// Cat就可以继承Animal的属性和方法了var Cat1 =newCat("Cat1");
Cat1.sleep();// Cat1正在睡觉!
console.log(Cat1.name);// Cat1
console.log(Cat1.colors);// [ 'black', 'white' ]var Cat2 =newCat("Cat2");// 因为两个实例使用的是同一个原型对象,内存空间是共享的
Cat2.colors.push("yellow");
console.log(Cat1.colors);// [ 'black', 'white', 'yellow' ]
console.log(Cat2.colors);// [ 'black', 'white', 'yellow' ]
构造函数继承

构造函数继承的关键在于,在子类构造函数中调用父类构造函数,并将子类实例的

this

绑定到父类构造函数上,从而使得父类的属性成为子类实例的属性

// 父类构造函数functionAnimal(name){this.name = name;this.colors =["black","white"];this.sleep=function(){
    console.log(this.name +"正在睡觉!");};}Animal.prototype.do=function(){
  console.log(this.name +"正在做动作!");};// 子类构造函数functionCat(name){// 在子类构造函数中调用父类构造函数Animal.call(this, name);}var Cat1 =newCat("Cat1");
console.log(Cat1.name);// Cat1
console.log(Cat1.colors);// [ 'black', 'white' ]// Cat1.do(); // 报错 cat.do is not a functionvar Cat2 =newCat("Cat2");
Cat2.colors.push("yellow");
console.log(Cat1.colors);// ["black", "white"];
console.log(Cat2.colors);// ["black", "white", "yellow"];// 相比第一种原型链继承方式,父类的引用属性不会被共享,优化了第一种继承方式的弊端// 但是只能继承父类的实例属性和方法,不能继承原型属性或者方法
组合继承

结合了原型链继承和构造函数继承的优点,组合继承能够实现函数复用(通过原型链继承原型上的属性和方法)同时还能保证每个实例都有自己的属性(通过构造函数继承实例属性)

// 父类构造函数functionAnimal(name){this.name = name;this.colors =["black","white"];this.sleep=function(){
    console.log(this.name +"正在睡觉!");};}Animal.prototype.do=function(){
  console.log(this.name +"正在做动作!");};// 子类构造函数functionCat(name){// 在子类构造函数中调用父类构造函数Animal.call(this, name);}// 原型链继承,继承父类的原型方法Cat.prototype =newAnimal();var Cat1 =newCat("Cat1");
Cat1.sleep();// Cat1正在睡觉!
Cat1.do();// Cat1正在做动作! 这样do方法可以被访问var Cat2 =newCat("Cat2");
Cat2.colors.push("yellow");
console.log(Cat1.colors);// [ 'black', 'white' ]
console.log(Cat2.colors);// [ 'black', 'white', 'yellow' ] 这样不会被共享
原型式继承
Object.create

方法实现普通对象的继承

var Animal ={name:"动物",colors:["black","white"],sleep:function(){
    console.log(this.name +"正在睡觉!");},};var Cat = Object.create(Animal,{name:{value:"猫"}});
Cat.sleep();// 猫正在睡觉!var Dog = Object.create(Animal,{name:{value:"狗"}});
Dog.sleep();// 狗正在睡觉!
Dog.colors.push("yellow");
console.log(Cat.colors);// [ 'black', 'white', 'yellow' ]
console.log(Dog.colors);// [ 'black', 'white', 'yellow' ] 和原型链继承一样共享内存
寄生式继承

寄生式继承是一种使用函数来增强对象的继承方式。它基于原型式继承的基础上,通过创建一个仅用于封装继承过程的函数,在函数内部以某种方式来增强对象,最后返回对象。

var Animal ={name:"动物",colors:["black","white"],sleep:function(){
    console.log(this.name +"正在睡觉!");},};// 封装一个函数functionclone(original){// 通过调用函数创建一个新对象var clone = Object.create(original);// 以某种方式来增强这个对象
  clone.do=function(){
    console.log(this.name +"正在做动作!");};
  clone.updateName=function(name){this.name = name;};// 返回这个对象return clone;}var dog =clone(Animal);
dog.do();// 动物正在睡觉!
dog.updateName("小狗");
dog.do();// 小狗正在睡觉!var cat =clone(Animal);
cat.colors.push("yellow");
console.log(cat.colors);// [ 'black', 'white', 'yellow' ]
console.log(dog.colors);// [ 'black', 'white', 'yellow' ] 共享内存了
寄生组合式继承

这个继承也是

ES6

中的

extends 

关键字,实现

JavaScript 

的继承原理,相对于其他的继承是最优的解法

// 父类构造函数functionAnimal(name){this.name = name;this.colors =["black","white"];this.sleep=function(){
    console.log(this.name +"正在睡觉!");};}// 父类原型方法Animal.prototype.do=function(){
  console.log(this.name +"正在做动作!");};// 寄生式继承,用于继承父类原型functionclone(subType, superType){var prototype = Object.create(superType.prototype);// 创建对象
  prototype.constructor = subType;// 增强对象
  subType.prototype = prototype;// 指定对象}// 子类构造函数functionCat(name){Animal.call(this, name);// 构造函数继承}clone(Cat, Animal);var Cat1 =newCat("Cat1");
Cat1.do();// Cat1正在做动作! 可以访问父类原型上的方法var Cat2 =newCat("Cat2");
Cat2.colors.push("yellow");

console.log(Cat1.colors);// [ 'black', 'white' ]
console.log(Cat2.colors);// [ 'black', 'white', 'yellow' ] 内存空间不共享
5. JavaScript事件循环机制?
JavaScript

在设计之初便是单线程,程序运行时,只有一个线程存在,同一时间只能做一件事。
为了解决单线程运行阻塞问题,

JavaScript

用到了计算机系统的一种运行机制,这种机制就叫做事件循环(Event Loop)

JavaScript

中,所有的任务都可以分为

  • 同步任务:立即执行的任务,同步任务一般会直接进入到主线程中执行
  • 异步任务:异步执行的任务,比如ajax网络请求,setTimeout 定时函数等

i0x5lmiguq.png
异步任务:

异步任务分为宏任务(macrotask)与 微任务 (microtask),不同的任务会依次进入自身对应的队列中,然后等待Event Loop将它们依次压入执行栈中执行。

微任务对应有:

  • next tick queue:process.nextTick
  • other queue:Promise的then回调、queueMicrotask

宏任务对应有:

  • timer queue:setTimeout、setInterval
  • poll queue:IO事件
  • check queue:setImmediate
  • close queue:close事件
asyncfunctionasync1(){
  console.log("1");awaitasync2();
  console.log("2");}asyncfunctionasync2(){
  console.log("3");}
console.log("4");setTimeout(function(){
  console.log("5");newPromise((resolve, reject)=>{
    console.log("13");resolve();
    console.log("14");}).then(function(){
    console.log("15");});},0);setTimeout(function(){
  console.log("6");},3);setImmediate(()=> console.log("7"));
process.nextTick(()=> console.log("8"));async1();newPromise(function(resolve){
  console.log("9");resolve();
  console.log("10");}).then(function(){
  console.log("11");});
console.log("12");/**
 *  从上往下执行 执行同步任务  打印4
 *  567是宏任务压入宏任务队列
 *  8是微任务
 *  执行async1 打印1 3
 *  2在await后面 压入为微队列
 *  执行new Promise 打印9 10 .then压入微队列
 *  执行打印 12
 *  主线程执行完成 打印4 1 3 9 10 12
 *  处理微任务队列 打印 8 2 11
 *  执行宏任务5 打印 5 13 14 压入微队列.then 15 执行微任务 打印 15 
 *  执行宏任务6 打印 6
 *  执行微任务7 打印 7
 *  4 1 3 9 10 12 8 2 12 11 5 13 14 15 6 7
 */

本文转载自: https://blog.csdn.net/qq_45124084/article/details/136563895
版权归原作者 Endless-y 所有, 如有侵权,请联系我们删除。

“前端:每天5道面试题(JavaScript)”的评论:

还没有评论