JavaScript面试题(三)

56.不使用promise能否把请求数据返回出来?

  • async/await
  • generator函数
  • 回调函数(类似于react中的子父传值)

57.async和await

  • asyncawait是ECMAScript 2017 (ES8)标准引入的新特性,用于简化异步操作。
  • async:用于修饰一个异步操作的函数,该函数返回一个Promise对象。如果函数中没有返回值,会默认返回一个Promise对象。如果在函数中 return 一个直接量,async 会把这个直接量通过 Promise.resolve() 封装成 Promise 对象;
  • await:用来等待一个异步方法执行完成。后面await如果是一个 Promise 对象,返回该Promise执行成功或者失败的结果。如果不是 Promise 对象,就直接返回对应的值。注意await 只能出现在 async 函数中,await会阻塞进程。

58.原型和原型链

  • 每个对象都有一个prototype属性,表示对象的原型(prototype也是一个对象)。
  • prototype作为对象的内部属性,是不能被直接访问的,但是可以通过__proto__来访问。
  • 原型链,当访问对象的属性或方法时,首先对象会从自身去找,找不到就会往原型(prototype)中去找,如果原型(prototype)中找不到,就会往原型后面的原型上去找,这样就形成了链式的结构,称为原型链。
  • 原型链的最顶层是Object,在往上就是null。

59.this指向问题

  • 全局作用域中的函数:非严格模式下其内部this指向window
  • 对象内部的函数:其内部this指向对象本身:
  • 构造函数:其内部this指向生成的实例:
  • 由apply、call、bind改造的函数:其this指向第一个参数:
  • 箭头函数:箭头函数没有自己的this,看其外层的是否有函数,如果有,外层函数的this就是内部箭头函数的this,如果没有,则this是window。(函数定义时的this,而不是调用时this)

60.怎么改变this指向?

  • call():在改变this指向的同时会调用函数
  • apply():在改变this指向的同时会调用函数,第二个参数接收的是一个数组。
  • bind():在改变this指向的同时不会调用原来的函数,而使生成并调用一个新的已经改变过this指向的函数

61.new做了什么

  • 在内存创建一个新对象
  • 把构造函数中this指向新建的对象
  • 会在新对象上添加一个__proto__属性,指向函数的原型对象prototype
  • 判断函数返回值,如果值是引用类型就直接返回值;否则返回this(创建的新对象)

62.构造函数

  • 什么是构造函数:JS中的任何一个普通函数,当用new关键字来调用时,它就是构造函数。构造函数与函数定义无关,与调用方法有关。构造函数一般首字母大写。
  • 构造函数的目的:在JavaScript中,构造函数是用来初始化新创建的对象的函数。构造函数的主要目的是在创建对象时初始化对象的属性。
  • 构造函数的意义:使用对象字面量创建一系列同一类型的对象时,这些对象可能具有一些相似的属性和方法,此时会产生很多重复的代码,把这些重复性的特征和属性抽象出来,做成构造函数,可以实现代码复用。
  • 构造函数的作用:构造新对象,设置对象的属性和方法。创建对象时完成初始化,当我们在new一个对象并传入参数的时候,会自动调用构造函数并完成参数的初始化。
  • 常见的构造函数:Object、Array、String、Boolean、Number、Date等。
  • 构造函数的this指向:
    • 当以函数的形式调用时,this是window
    • 当以方法的形式调用时,谁调用方法this就是谁
    • 当以构造函数的形式调用时,this就是新创建的那个对象
  • 自定义构造函数:
    • 首字母大写
    • 通过new创建实例对象
    • 创建构造函数时,里面的属性和方法前必须加this,this就表示当前运行时的对象
  • 返回值
    • 不写return,返回一个this对象
    • return一个基本数据类型,返回一个this对象
    • return一个复杂数据类型,返回一个复杂数据类型,比如对象、数组

63.构造函数和普通函数的区别

  • 普通函数是小驼峰的名命方式,而构造函数是大驼峰的名命方式(行业规范)。
  • 我们知道普通函数的this指向是指向全局对象的,而构造函数内部的this指向当前对象的实例。
  • 使用的方式不同,普通函数直接调用,构造函数必须使用new 来调用,通过 new.target 来判断调用的方式是不是构造函数。
  • 任何函数只要使用new操作符调用就是构造函数,而不使用new操作符调用的函数就是普通函数

63.es6class类

  • 类(class)是ES6新的基础性语法糖结构,用于创建对象的模板。可以看成构造函数的另一种写法,这种写法可以让对象原型的写法更加清晰、更像面向对象编程的语法而已。
  • 类必须使用new调用,否则会报错。普通构造函数使用new创建的是实例化对象,不使用new则执行的是普通函数的调用。
  • 类的数据类型就是函数,类本身就指向构造函数。
  • 函数受函数作用域限制,而类受块作用域限制。

64.constructor

  • constructor 方法是一个特殊的方法,用于创建和初始化一个由class创建的对象。通过 new 关键字生成对象实例时,自动会调用该方法。
  • 一个类只能拥有一个名为"constructor"构造函数,不能出现多个,如果定义了多个"constructor"构造函数,则将抛出 一个SyntaxError错误。
  • 如果没有定义"constructor"构造函数,class 会默认添加一个空的"constructor"构造函数。

65.super

  • super代表的是父类的构造函数。super可以用来调用父类的属性和方法,也可以用来调用父类的构造函数。
  • super继承。
    • ES6 class 可以通过extends关键字实现继承,同时子类必须在constructor中调用super,否则新建实例时会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象,只有调用super之后,才可以使用this关键字。
    • 子类如果没有定义constructor方法,super方法会被默认添加。
  • super方法。
    • super作为函数调用时,代表父类的构造函数。
    • super虽然代表了父类的构造函数,但是返回的是子类的实例,即super内部的this指的是子类的实例,因此super()在这里相当于A.prototype.constructor.call(this)
  • super对象。
    • 在普通方法中,指向父类的原型对象。由于super指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过super调用的。ES6 规定,在子类普通方法中通过super调用父类的方法时,方法内部的this指向当前的子类实例。由于this指向子类实例,所以如果通过super对某个属性赋值,这时super就是this,赋值的属性会变成子类实例的属性。
    • 在静态方法中(statrc修饰的方法),指向父类。如果super作为对象,用在静态方法之中,这时super将指向父类,而不是父类的原型对象。在子类的静态方法中通过super调用父类的方法时,方法内部的this指向当前的子类,而不是子类的实例。

66.构造器constructor为什么要使用super

因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象,只有调用super之后,才可以使用this关键字。

67.继承

  • 实例继承:将子构造函数的 prototype 指向父构造函数的一个实例
  • 原型继承:将子构造函数的 prototype 指向父构造函数的 prototype
  • 构造函数绑定:使用 call 或 apply 方法,将父对象的构造函数绑定在子对象上
  • 拷贝继承:如果把父对象的所有属性和方法,拷贝进子对象
  • ES6 语法 extends:class ColorPoint extends Point {} 并在子类的构造器中调用super函数

68.什么是js严格模式

严格模式(Strict Mode)是一种在代码中启用的特殊模式,用于提供更严格的语法和错误检查,以改善代码质量和增强安全性。使用严格模式可以帮助大家避免一些常见的错误,并禁用一些不推荐使用的特性。

  • 变量必须先声明后使用:在严格模式下,变量必须通过 var、let 或 const 关键字进行声明,否则会抛出 ReferenceError。在非严格模式下,未声明的变量会被隐式创建,并被添加到全局对象(比如浏览器环境中的 window 对象)中。
  • 禁止删除变量、函数或函数参数:在严格模式下,使用 delete 操作符删除变量、函数或函数参数会抛出 SyntaxError。
  • 禁止对只读属性进行赋值:在严格模式下,对只读属性(通过 const 关键字声明的常量)进行赋值会抛出 TypeError。
  • 禁止使用八进制字面量:在严格模式下,以 0 开头的数字会被视为八进制字面量,这在非严格模式下是允许的。严格模式下,使用八进制字面量会抛出 SyntaxError。
  • 限制 this 值:在严格模式下,函数内部的 this 值不再是全局对象(比如浏览器环境中的 window 对象),而是undefined,除非通过 call()、apply() 或 bind() 明确指定。
  • 禁止使用重复的函数参数名:在严格模式下,函数参数名不能重复。在非严格模式下,重复的函数参数名会被忽略。
  • 禁止使用 with 语句:在严格模式下,使用 with 语句会抛出 SyntaxError。with 语句在非严格模式下允许将对象的属性添加到作用域链中,但这被认为是不推荐使用的特性,因为它可能导致代码可读性和性能问题。
  • 限制 eval 和 arguments 的赋值:在严格模式下,无法对 eval 和 arguments 进行赋值。在非严格模式下,这种赋值是允许的。

69.怎么阻止表单提交默认行为

  • **e.**preventDefault()
  • οnsubmit事件中return false

70.js事件流是什么?怎么修改事件传播机制?怎么阻止事件传播?

  • 捕获阶段:从外向里依次查找元素
  • 目标阶段:从当前事件源本身的操作
  • 冒泡阶段:从内到外依次触发相关的行为
  • addEventListener事件监听器的第三个参数设置成true捕获false冒泡
  • event.stopPropagation() 阻止事件传播

71.dom事件委托原理,有什么优缺点

  • 事件委托原理
    • 利用事件冒泡机制,将事件监听器设置在其父节点上,通过event.target.nodeName判断是否是子节点来实现控制子节点。
  • 优点
    • 可以大量节省内存占用,减少事件注册
    • 可以实现当新增子对象时,无需再对其进行事件绑定
  • 缺点
    • 如果把所有事件都用事件代理,可能会出现事件误判

72.栈内存和堆内存

  • 在JavaScript中,数据是分为两类存储的:基本类型和对象类型。基本类型值指的是那些保存在栈内存(Stack)中的数据,而对象类型值则被保存在堆内存(Heap)中。
  • 栈内存是一种后进先出(LIFO)的数据结构,主要用于存储函数的局部变量、临时数据、书签等。当你创建一个基本类型的变量时,它会被存储在栈内存中,并且占据一块连续的空间。
  • 堆内存是用来存储对象的地方,对象可以包含多个值,大小不固定,可以动态地增加或减少。当你创建一个对象类型的变量时,这个对象会被存储在堆内存中。

73.主线程和任务队列

  • JavaScript 中的主线程和任务队列是浏览器的 JavaScript 引擎如何工作的基本概念。
  • 主线程:主线程是 JavaScript 引擎用来执行执行代码的地方。当 JavaScript 引擎开始执行代码时,程序的主线程就会被创建。
  • 任务队列:JavaScript 是单线程的,这意味着它只有一个主线程来执行代码。但是,JavaScript 引擎还有其他的任务队列,如微任务队列和宏任务队列。

74.执行栈和调用栈

执行栈和调用栈通常是指程序在执行过程中的两种不同的数据结构。

  • 执行栈(Execution Stack):在JavaScript中,执行栈是用来存储执行上下文(Execution Context)的数据结构。每当一个函数被调用时,就会为这个函数创建一个新的执行上下文并将其推入执行栈。执行栈是后进先出(LIFO)的数据结构。当函数执行完毕,它的执行上下文就会从栈中移除。
  • 调用栈(Call Stack):调用栈是一个系统级的数据结构,用于存储一个个正在被执行的函数的地址。当一个函数调用另一个函数时,被调用的函数的地址会被添加到调用栈顶部。当这个函数执行完毕,它的地址会从调用栈顶部移除。在JavaScript中,调用栈是由JavaScript引擎管理的,开发者可以通过错误栈跟踪(stack trace)来查看调用栈的状态。

75.js事件循环机制(底层原理)

因为js引擎存在monitoring process进程,会持续不断的检查主线程执行栈是否为空,一旦为空,就会去Event Queue那里检查是否有等待被调用的函数。

整体会把所有代码分为两个部分:'同步任务','异步任务'。所有同步任务都在主线程上执行,形成一个执行栈。主线程之外还存在一个任务队列,专门存放异步任务(宏任务和微任务)。

  • 宏任务进入到事件表(Event Table)中,并在里面注册回调函数,每当指定的事件完成时,Event Table会将这个函数移到事件队列(Event Queue)中
  • 微任务也会进入到另一个事件表(Event Table)中,并在里面注册回调函数,每当指定的事件完成时,事件表(Event Table)会将这个函数移到事件队列(Event Queue)中
  • 整体script作为第一个宏任务进入主线程,当主线程的任务执行完毕,主线程为空时,会检查微任务的事件队列(Event Queue),如果有任务,就会全部执行,如果没有就执行下一个宏任务
  • 主线程不断重复上面的步骤,这就是Event Loop事件循环,只要主线程空了,就会去读取"任务队列"。这个过程会不断重复。

76.target 和 currentTarget 区别

  • 都是事件对象上的属性
  • event.target:返回触发事件的元素
  • event.currentTarget:返回绑定事件的元素(相当于事件中this)

77.浮点数精度失真

  • 计算机内部使用二进制浮点数表示法,而不是十进制。这种二进制表示法在某些情况下无法准确地表示某些十进制小数,从而导致精度丢失。
  • 解决方法
    • 保留指定位数的小数
    • 获取最大小数位,根据最大小数位乘以10的倍数
    • 使用decimal.js

78.import和require的区别

  • 使用方式。import用于导入整个模块或模块中的特定部分,使用export default或export命令进行导出;require用于导入整个模块,使用module.exports或exports命令进行导出。
  • 加载时机。import是在编译时加载,必须放在文件的开头;require是在运行时加载,可以放在代码的任何位置。
  • 解构赋值。import支持解构赋值,可以直接导入模块中导出的值、函数或对象;require进行的是赋值操作,导入的是模块导出的对象。
  • 所属规范。import是ES6(ECMAScript 2015)引入的关键字,属于ES模块化语法规范;require是CommonJS规范的一部分,主要用于Node.js环境。
  • 动态绑定。import提供静态分析,支持宏和类型检验;require提供动态绑定,更适合服务器或浏览器环境。
  • 此外,由于历史原因和兼容性问题,在Node.js中,import语法通常需要通过Babel等工具转码为require语句才能使用。尽管import是ES6标准的一部分,并且在现代JavaScript开发中非常常用,但require仍然被广泛支持,特别是在Node.js社区中。开发者可以根据项目需求和目标平台选择使用import或require。

79.postMessage

  • 作用:"跨文档消息传递 ",又称为"窗口间消息传递 "或者"跨域消息传递 "。postMessage方法 允许来自一个文档的脚本可以传递文本消息到另一个文档里的脚本,而不用管是否跨域,可以用这种消息传递技术来实现安全的通信。具体使用场景如下:

    • 页面和其打开的新窗口的数据传递
    • 页面与嵌套的 iframe 消息传递
    • 多窗口之间消息传递
    • 跨域数据传递
  • 发送消息:otherWindow.postMessage(message, targetOrigin);

    • otherWindow:其他窗口的一个引用。比如 iframe 的 contentWindow 属性、执行 window.open 返回的窗口对象等。
    • message:要发送的消息。它将会被结构化克隆算法序列化,所以无需自己序列化,html5规范中提到该参数可以是JavaScript的任意基本类型或可复制的对象,然而并不是所有浏览器都做到了这点儿,部分浏览器只能处理字符串参数,所以我们在传递参数的时候需要使用JSON.stringify()方法对对象参数序列化。
    • targetOrigin:"目标域"。URI(包括:协议、主机地址、端口号)。若指定为"*",则表示可以传递给任意窗口,指定为"/",则表示和当前窗口的同源窗口。当为URI时,如果目标窗口的协议、主机地址或端口号这三者的任意一项不匹配 targetOrigin 提供的值,那么消息就不会发送。
  • 接收消息:当调用 postMessage() 方法的时候,目标窗口的Window对象上就会触发一个 message 事件。为window添加message事件即可获取postMessage传来的消息。

  • 安全问题:

    • 如果你不希望从其他网站接收 message,请不要为 message 事件添加任何事件监听器。
    • 如果你确实希望从其他网站接收message,请使用使用事件对象中的 origin 和 source 属性验证发件人的身份。
    • 当你使用 postMessage 将数据发送到其他窗口时,始终指定精确的域名,而不是 *。
相关推荐
亿牛云爬虫专家1 小时前
Puppeteer教程:使用CSS选择器点击和爬取动态数据
javascript·css·爬虫·爬虫代理·puppeteer·代理ip
2401_857610031 小时前
深入探索React合成事件(SyntheticEvent):跨浏览器的事件处理利器
前端·javascript·react.js
熊的猫2 小时前
DOM 规范 — MutationObserver 接口
前端·javascript·chrome·webpack·前端框架·node.js·ecmascript
天农学子2 小时前
Easyui ComboBox 数据加载完成之后过滤数据
前端·javascript·easyui
mez_Blog2 小时前
Vue之插槽(slot)
前端·javascript·vue.js·前端框架·插槽
爱睡D小猪2 小时前
vue文本高亮处理
前端·javascript·vue.js
开心工作室_kaic2 小时前
ssm102“魅力”繁峙宣传网站的设计与实现+vue(论文+源码)_kaic
前端·javascript·vue.js
放逐者-保持本心,方可放逐2 小时前
vue3 中那些常用 靠copy 的内置函数
前端·javascript·vue.js·前端框架
IT古董2 小时前
【前端】vue 如何完全销毁一个组件
前端·javascript·vue.js
Henry_Wu0012 小时前
从swagger直接转 vue的api
前端·javascript·vue.js