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, 依次检查经过的节点是否绑定了事件监听函数,如果有则执行

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值