• Javascript最全面试题


    javascript的数据类型,存储上的差别?

    在js中,我们可以分为两种类型:

    基本数据类型:

    1. Number:数值最常见的整数类型格式就是十进制,还可以设置八进制,十六进制,在数值类型中,存在一个特殊数值NaN,他是用来表示本来要返回数值的操作失败了
    2. string:字符串可以使用单引号,双引号,或反引号表示
    3. Boolean:只有两个字面量,true和false
    4. Undefined:是一个特殊值,undefined值的变量和未定义变量是有区别的
    5. null:null类型同样只有一个值,逻辑上讲,null值表示的就是一个空对象指针,只要变量要保存对象,而当时又没有那个对象可保存,就可用 null来填充该变量
    6. symbol:Symbol (符号)是原始值,且符号实例是唯一、不可变的。符号的用途是确保对象属性使用唯一标识符,不会发生属性冲突的危险

    复杂数据类型: 

    • Object:创建object常用的方式为对象字面量表达式,属性名可以是字符串或数值
    • Array:JavaScript数组是一组有序的数据,但跟其他语言不同的是,数组中每个槽位可以存储任意类型的数据
    • Function:函数上就是一个对象,每个函数都是function类型的实例,而function也有属性和方法,跟其他引用类型一样

    其他引用类型

    date。RegExp,Map,Set等

    存储区别:

    基本数据类型和引用数据类型存储在内存中的位置不同:

    • 基本数据类型存储在栈中

    • 引用类型的对象存储于堆中

    总结

    • 声明变量时不同的内存地址分配:
      • 简单类型的值存放在栈中,在栈中存放的是对应的值
      • 引用类型对应的值存储在堆中,在栈中存放的是指向堆内存的地址
    • 不同的类型数据导致赋值变量时的不同:
      • 简单类型赋值,是生成相同的值,两个对象对应不同的地址
      • 复杂类型赋值,是将保存对象的内存地址赋值给另一个变量。也就是两个变量指向堆内存中同一个对象

     数组中常用的方法有哪些?

    数组操作的增删改查操作

    • push() 接收任意数量的参数,添加在数组的末尾,返回数组的最新长度
    • unshift() 在开头添加任意多个值,然后返回新的数组的长度
    • splice()传入三个参数,分别是开始位置、要删除的元素数量、插入的元素,返回空数组
    • concat()会创建一个当前数组的副本,然后再把它的参数添加到副本的末尾,最后返回这个新构建的数组,不会影响原始数组

    • pop() 用于删除数组的最后一项,同时会减少数组的length,返回被删除的项
    • shift()用于删除数组的第一项,同时减少数组的length,返回被删除的项
    • splice()传入两个参数,分别是开始位置,删除元素的数量,返回包含删除元素的数组
    • slice()用于创建一个包含原有数组中的一个或者多个元素 ,不会影响原始数组

    splice():接收三个参数,分别是开始位置,要删除元素的数量,要插入的任意多个元素,返回删除元素的数组,对原数组产生影响

    • indexOf()返回要查找的元素在数组中的位置,如果没有则返回-1
    • includes()返回要查找的元素在数组中的位置,找到返回true,否则是false
    • find()返回的是第一个匹配的元素

    排序方法

    • reverse()将数组元素方向反转
    • sort()接收一个比较函数,用于判断哪个值应该排在前面

    转换方法

     join()接收一个参数,即字符串分隔符,返回包含所有项的字符串

    数组迭代(都不会改变原数组)

    • some()如果至少有一个元素返回true,则方法就返回true
    • every()如果所有元素返回true,则这个方法就返回true
    • forEach()对数组的每一项都运行传入的函数,没有返回值
    • filter()对数组进行过滤,函数返回true的项会组成一个数组后进行返回
    • map()对数组中的每一项都进行遍历,在react中使用较多

    javascript字符串常用的方法有哪些?

    增 

    这里增的意思不是说直接增添内容,而是创建字符串的副本,再进行操作

    除了我们常用的+以及${]进行字符串拼接外,还可以通过concat

    concat()用于将一个或者多个字符串拼接为一个新的字符串

    删 

    • slice()
    • substr()
    • substring()

    这三个方法都返回调用它们的字符串的一个子字符串,而且都接收一或两个参数 

    • trim()、trimLeft()、trimRight()删除前、后或前后所有空格符,再返回新的字符串

    • repeat()接收一个整数参数,表示要将字符串复制多少次,然后返回拼接所有副本后的结果

    • padStart()、padEnd()复制字符串,如果小于指定长度,则在相应一边填充字符,直至满足长度条件

    • toLowerCase()、 toUpperCase()大小写转化

    • chatAt()返回给定索引位置的字符,由传给方法的整数参数指定

    • indexOf()从字符串开头去搜索传入的字符串,并返回位置(如果没找到,则返回 -1 )

    • startWith()从字符串中搜索传入的字符串,并返回一个表示是否包含的布尔值

    • includes()从字符串中搜索传入的字符串,并返回一个表示是否包含的布尔值

     转换方法

    split()把字符串按照指定的分割符,拆分成数组中的每一项

    模板匹配

    • match()接收一个参数,可以是一个正则表达式字符串,也可以是一个RegExp对象,返回数组
    • search()接收一个参数,可以是一个正则表达式字符串,也可以是一个RegExp对象,找到则返回匹配索引,否则返回 -1
    • replace()接收两个参数,第一个参数为匹配的内容,第二个参数为替换的元素(可用函数)

    javascript中的类型转换机制?

    常见的类型转换有:

    强制转换(显示转换)

    • Number()将任意类型的值转化为数值
    • parseInt()parseInt相比Number,就没那么严格了,parseInt函数逐个解析字符,遇到不能转换的字符就停下来
    • String()可以将任意类型的值转化成字符串
    • Boolean()可以将任意类型的值转为布尔值 

    自动转换 (隐式转换)

    何时会发生隐式转换?

    我们这里可以归纳为两种情况发生隐式转换的场景:

    • 比较运算(==!=><)、ifwhile需要布尔值地方
    • 算术运算(+-*/%

    除了上面的场景,还要求运算符两边的操作数不是同一类型

    自动转换为布尔值

    在需要布尔值的地方,就会将非布尔值的参数自动转为布尔值,系统内部会调用Boolean函数

    可以得出除了undefined,null,false,+0,-0,NaN,""会被转换为false,其他的会被转换为true

    自动转换为字符串

    遇到预期为字符串的地方,就会将非字符串的值自动转为字符串

    具体规则是:先将复合类型的值转为原始类型的值,再将原始类型的值转为字符串

    常发生在+运算中,一旦存在字符串,则会进行字符串拼接操作

    自动转换为数值

    除了+有可能把运算转为字符串,其他的运算符都会把运算自动转成数值

    null转为数值时,值为0  undefined转为数值时,值为NaN

    ==和===的区别?分别在什么情况下使用

    ==

    如果操作数相等,则会返回true,在比较中会先进行类型转换,再确定操作数是否相等

    遵循以下规则:

    • 两个都为简单类型,字符串和布尔值都会转换成数值,再比较

    • 简单类型与引用类型比较,对象转化成其原始类型的值,再比较

    • 两个都为引用类型,则比较它们是否指向同一个对象

    • null 和 undefined 相等

    • 存在 NaN 则返回 false

    全等操作符(===)

    全等操作符由 3 个等于号( === )表示,只有两个操作数在不转换的前提下相等才返回 true。即类型相同,值也需相同

    undefined 和 null 与自身严格相等

    区别?

    相等操作符(==)会做类型转换,再进行值的比较,全等运算符不会做类型转换

    null 和 undefined 比较,相等操作符(==)为true,全等为false

    总结:

    除了在比较对象属性为null或者undefined的情况下,我们可以使用相等操作符(==),其他情况建议一律使用全等操作符(===)

    深拷贝浅拷贝的区别?如何实现一个深拷贝?

    深拷贝:值的是创建一个新的数据,这个数据有着原始数据属性值的一份精准拷贝

    如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址

    即浅拷贝只是拷贝一层,深层次的引用类型则是共享内存地址

    浅拷贝的实现:

    • Object.assign
    • Array.prototype.slice()Array.prototype.concat()
    • 使用拓展运算符实现的复制

    深拷贝

    深拷贝开辟了一个新的栈,两个对象属性完全相同,但是对应了两个不同的地址,修改一个对象的属性,不会该白你另一个对象的属性

    常见的深拷贝的方式有:

    • _.cloneDeep()

    • jQuery.extend()

    • JSON.stringify()

    • 手写循环递归

     区别?

    浅拷贝只复制属性指向某个对象的指针,而不复制对象本身,新旧对象还是会共享同一块内存,修改对象属性会影响原对象

    深拷贝会创造出一个一摸一样的对象,新旧对象不共享内存,修改新对象不会影响到原对象

    谈谈你对闭包的理解?以及使用的场景?

    函数嵌套函数,内部函数始终可以访问到其外部函数的作用域以及参数,这就是闭包

    在js中,我们每创建一个函数,闭包就会在函数创建的同时被创建出来,作为函数内部与外部连接起来的一座桥梁

    使用场景:

    创建私有变量

    延长变量的生命周期

    一般函数的词法环境在函数返回后就被销毁,但是闭包会保存对创建时所在词法环境的引用,即便创建时所在的执行上下文被销毁,但是创建时所在词法环境仍然存在,以达到延长变量生命周期的目的

    柯里化函数

    柯里化的目的在于避免频繁的调用具有相同参数函数的同时,又能轻松的重用

    注意事项: 

    如果不是某些特定任务需要使用闭包,在其他函数中创建函数是不明智的,因为闭包在处理速度和内存消耗方面对脚本性能具有负面影响

    谈谈你对作用域链的理解?

     作用域,即变量和函数生效的区域或集合,作用域决定了代码块中变量和其他资源的可见性

    我们一般将作用域分为三种:

    全局作用域:任何不再函数中或者是大括号中声明的变量,都是在全局作用域下,全局作用域下声明的变量可以在程序的任意位置访问

    函数作用域:函数作用域也叫局部作用域,如果一个变量是在函数内部声明的它就在一个函数作用域下面,这些变量只能在函数内部访问,不能在函数以外进行访问

    块级作用域:像我们es6中引入了let和const关键字,let和const属于块级作用域,在大括号以外不能访问这些变量

    词法作用域:

    又叫静态作用域,变量被创建时确定好了,而非执行阶段确定的,也就是说我们写好的代码时他的作用域就确定了,js遵循的就是词法作用域

    作用域链:

    在js中使用一个变量的时候,首先js引擎会尝试在当前作用域下去寻找该变量,如果没有找到变量,会到他的上层作用域寻找,以此类推直到找到该变量或者到了全局作用域

    如果在全局作用域仍然找不到该变量,他就会在全局范围内隐式声明该变量

    JavaScript原型,原型链 ? 有什么特点?

    js常被描述为一个基于原型的语言--每个对象拥有一个原型对象

    当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次向上搜索,直到找到一个名字匹配的属性或者达到原型链的顶端

    原型链:

    原型对象也可能拥有原型,并从中继承方法和属性,一层一层,以此类推,这种关系被称为原型链,他解释了为何一个对象会拥有定义在其他对象中的属性和方法

    在对象实例和他的构造器之间建立一个链接,_proto_属性,是从构造函数的prototype属性中派生的,之后可以通过上溯原型链,在构造器中找到这些属性和方法

    总结:

    一起对象都是继承自object对象,object对象直接继承根源对象null

    一切的函数对象,都是继承自Function对象

    object对象直接继承自Function对象

    Function对象的_proto_会指向自己的原型对象,最总还是继承自object对象

    Javascript如何实现继承?

    继承:是面向对象软件技术中的一个概念

    如果一个类别B“继承自”另一个类别A,就把这个B称为“A的子类”,而把A称为“B的父类别”也可以称“A是B的超类”

    继承的优点:

    继承可以使得子类具有父类别的各种属性和方法,而不需要再次编写相同的代码

    再子类别继承父类别的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类别的原有属性和方法,使其获得于父类别不同的功能

    实现方式:

    • 原型链继承:是比较常见的一种继承方式之一,其中设计的构造函数、原型和实例,三者之间存在一定的关系,即每一个构造函数中都会有一个原型对象,原型对象有包含一个指向构造函数的指针,而实例则包含一个原型对象的指针

    • 构造函数继承(借助 call)借助call调用Parent函数,父类原型对象中一旦存在父类之前自己定义的方法,那么子类将无法继承这些方法,父类的引用属性不会被共享,优化了原型链继承的弊端,但是它只能继承父类的实例属性和方法,不能继承原型属性或者方法

    • 组合继承:组合继承就是将原型链继承和构造函数继承结合起来

    • 原型式继承:借助Object.create方法实现普通对象的继承:因为它实现的是浅拷贝,多个实例的引用类型属性指向相同的内存,存在篡改的可能

    • 寄生式继承:就是利用浅拷贝的能力进行增强,添加一些其他方法

    • 寄生组合式继承:借助解决普通对象的继承问题的Object.create 方法,在前面几种继承方式的优缺点基础上进行改造,这也是所有继承方式里面相对最优的继承方式

     谈谈this对象的理解?

     函数的this关键字在js中表现得略有不同,在严格模式下和非严格模式下也会有一些差别

    在绝大多数情况下,函数的调用方式决定了this的值

    this关键字是在函数运行时自动生成一个内部对象,只能在函数内部使用,总能调用他的对象

    在this的执行过程中,一旦被确定,就不能再进行更改

    绑定规则:

    • 默认绑定:全局环境中定义person函数,内部使用this关键字

    • 隐式绑定:函数还可以作为某个对象的方法调用,这时this就指这个上级对象

    • new绑定:通过构建函数new关键字生成一个实例对象,此时this指向这个实例对象

    • 显示绑定:apply()、call()、bind()是函数的一个方法,作用是改变函数的调用对象。它的第一个参数就表示改变后的调用这个函数的对象。因此,这时this指的就是这第一个参数

     箭头函数

    es6中还提供了箭头函数语法,让我们在代码书写时就能确定this指向

    虽然箭头函数的this能够在编译的时候就确定this的指向,但是我们也需要注意一些潜在的坑

    箭头函数没有this指向,箭头函数不能作为构造函数

    优先级

     new绑定优先级 > 显示绑定优先级 > 隐式绑定优先级 > 默认绑定优先级

     JavaScript中执行上下文和执行栈是什么?

    简单来说,执行上下文是一种对js代码执行环境的抽象概念,也就是说只有js代码运行,那么它就一定是运行在执行的上下文中

    执行上下文的类型分为三种:

    • 全局执行上下文:只有一个,浏览器中的全局对象就是 window对象,this 指向这个全局对象
    • 函数执行上下文:存在无数个,只有在函数被调用的时候才会被创建,每次调用函数都会创建一个新的执行上下文
    • Eval 函数执行上下文: 指的是运行在 eval 函数中的代码,很少用而且不建议使用

    生命周期:

     执行上下文的生命周期包括三个阶段:创建阶段 → 执行阶段 → 回收阶段

    创建阶段:

    创建阶段做了三件事:

    • 确定 this 的值,也被称为 This Binding
    • LexicalEnvironment(词法环境) 组件被创建
    • VariableEnvironment(变量环境) 组件被创建

    词法环境

    词法环境有两个组成部分:

    • 全局环境:是一个没有外部环境的词法环境,其外部环境引用为null,有一个全局对象,this 的值指向这个全局对象

    • 函数环境:用户在函数中定义的变量被存储在环境记录中,包含了arguments 对象,外部环境的引用可以是全局环境,也可以是包含内部函数的外部函数环境

    变量环境

    变量环境也是一个词法环境,因此它具有上面定义的词法环境的所有属性

    在 ES6 中,词法环境和变量环境的区别在于前者用于存储函数声明和变量( let 和 const )绑定,而后者仅用于存储变量( var )绑定

    执行阶段

    在这阶段,执行变量赋值、代码执行

    如果 Javascript 引擎在源代码中声明的实际位置找不到变量的值,那么将为其分配 undefined 值

    回收阶段

    执行上下文出栈等待虚拟机回收执行上下文

    说说JavaScript中的事件模型

    javascript中的事件,可以理解就是在HTML文档或者浏览器中发生的一种交互操作,使得网页具备互动性, 常见的有加载事件、鼠标事件、自定义事件等

    事件流都会经历三个阶段:

    • 事件捕获阶段(capture phase)
    • 处于目标阶段(target phase)
    • 事件冒泡阶段(bubbling phase)

     事件冒泡是一种从下往上的传播方式,由最具体的元素(触发节点)然后逐渐向上传播到最不具体的那个节点,也就是DOM中最高层的父节点

    事件捕获与事件冒泡相反,事件最开始由不太具体的节点最早接受事件, 而最具体的节点(触发节点)最后接受事件

    事件模型可以分为三种:

    原始事件模型(DOM0级)html代码直接绑定,js代码绑定

    特性:

    绑定速度快

    只支持冒泡,不支持捕获

    同一个类型的事件只能绑定一次

    标准事件模型(DOM2级)

    • 事件捕获阶段:事件从document一直向下传播到目标元素, 依次检查经过的节点是否绑定了事件监听函数,如果有则执行
    • 事件处理阶段:事件到达目标元素, 触发目标元素的监听函数
    • 事件冒泡阶段:事件从目标元素冒泡到document, 依次检查经过的节点是否绑定了事件监听函数,如果有则执行

    特性:

    • 可以在一个DOM元素上绑定多个事件处理器,各自并不会冲突
    • 当第三个参数(useCapture)设置为true就在捕获过程中执行,反之在冒泡过程中执行处理函数

    IE事件模型(基本不用

    IE事件模型共有两个过程:

    • 事件处理阶段:事件到达目标元素, 触发目标元素的监听函数。
    • 事件冒泡阶段:事件从目标元素冒泡到document, 依次检查经过的节点是否绑定了事件监听函数,如果有则执行

  • 相关阅读:
    金仓数据库 KingbaseGIS 使用手册(6.3. 几何对象创建函数)
    在云服务器上搭建个人版chatGPT及后端Spring Boot集成chat GPT
    【打工人摸鱼系列】python做皮卡丘桌宠,工作都有效率了呢
    LabVIEW工业虚拟仪器的标准化实施
    13.108.Spark 优化、Spark优化与hive的区别、SparkSQL启动参数调优、四川任务优化实践:执行效率提升50%以上
    el-input输入框高度修改
    ipsec主模式加密身份信息有什么意义?
    简单迅速解决windows电脑下载windows应用商店(Microsoft Store)
    【FPGA+sin】基于DDS(直接数字合成)的正弦信号发生器模块FPGA实现
    电子电气架构设计需要考虑哪些方面?
  • 原文地址:https://blog.csdn.net/qq_60976312/article/details/127534884