前端常见面试-js篇

        前面咱们已经梳理了前端面试中css的相关内容,同时也对面试中常问的本地存储 、缓存作了一个详细的介绍,有须要的可自行查看以前的文章,文章连接以下:
前端常见面试-css篇
前端常见面试-存储/缓存篇
        固然,对于前端开发来讲,js那就是每个前端小可爱都须要必备掌握的技能,不管如今多火爆的mv*框架,都是基于基础的js来进行的。好了,话很少说,直接进入主题javascript

1. 请说出js中的数据类型

        答:js中的数据类型主要分为两种:基础类型和引用类型,其中基础类型包括:字符串(String)、数字(Number)、布尔(Boolean)、对空(Null)、未定义(Undefined)、Symbol(es6中引入的);引用类型包括:对象(Object)、数组(Array)、函数(Function)css

2. 请说明一下js中定义变量的方式

        答:js中申明变量的方式主要有三种,分别为:var、let、const
1)var:定义的变量能够修改,若是不初始化会输出undefined,不会报错
2)let:是块级做用域,函数内部使用let定义后,对函数外部无影响
3)const:定义变量不能够修改,并且必须初始化,可是若是定义的是对象,则对象的属性能够修改(缘由是引用的是对象的地址,地址不可更改,可是地址对应的内容能够修改)
        不过对于非严格模式下来讲,没有使用这三者方式定义的变量会自动的挂载到window对象下,成为一个全局变量。html

3. js的变量提高和函数提高

        答:在ES6以前,JavaScript没有块级做用域(一对花括号{}即为一个块级做用域),只有全局做用域和函数做用域。变量提高即将变量声明提高到它所在做用域的最开始的部分。js中建立函数有两种方式:函数声明式和函数表达式申明。只有函数声明才存在函数提高前端

4. js的事件冒泡机制

        答:事件冒泡(event bubbling),事件最开始时由触发的那个元素身上发生,而后沿着DOM树向上传播,直到document对象。java

js事件冒泡的应用:

        1)事件冒泡容许多个操做被集中处理(把事件处理器添加到一个父级元素上,避免把事件处理器添加到多个子级元素上),它还可让你在对象层的不一样级别捕获事件
        2)让不一样的对象同时捕获同一事件,并调用本身的专属处理程序作本身的事情es6

阻止js冒泡

        1)使用e.stopPropagation()
        2)根据当前触发的元素来进行判断阻止:e.target==e.currentTarget面试

事件冒泡的注意事项

●事件捕获其实有三种方式,事件冒泡只是其中的一种:(1)IE从里到外(inside→outside)的冒泡型事件。(2)Netscape4.0从外到里(outside→inside)的捕获型事件。(3)DOM事件流,先从外到里,再从里到外回到原点(outside→inside→outside)的事件捕获方法(彷佛对象将触发两次事件处理,这有什么做用?鄙人不懂!)。正则表达式

●不是全部的事件都能冒泡。如下事件不冒泡:blur、focus、load、unload。编程

●事件捕获方式在不一样浏览器,甚至同种浏览器的不一样版本中是有所区别的。如Netscape4.0采用捕获型事件解决方案,其它多数浏览器则支持冒泡型事件解决方案,另外DOM事件流还支持文本节点事件冒泡。segmentfault

●事件捕获到达顶层的目标在不一样浏览器或不一样浏览器版本也是有区别的。在IE6中HTML是接收事件冒泡的,另外大部分浏览器将冒泡延续到window对象,即……body→documen→window。

●阻止冒泡并不能阻止对象默认行为。好比submit按钮被点击后会提交表单数据,这种行为无须咱们写程序定制。

5. js的严格模式和非严格模式

        答:js默认状况是非严格模式,若是须要使用严格模式,可使用关键字:'use strict',对于严格模式和非严格模式有以下区别:
        1) 严格模式下, delete运算符后跟随非法标识符(即delete 不存在的标识符),会抛出语法错误; 非严格模式下,会静默失败并返回false
        2)严格模式中,对象中定义同名属性会抛出语法错误; 非严格模式不会报错
        3)严格模式中,函数形参存在同名的,抛出错误; 非严格模式不会
        4)严格模式不容许八进制整数(如:023)
        5)严格模式中,arguments对象是传入函数内实参列表的静态副本;非严格模式下,arguments对象里的元素和对应的实参是指向同一个值的引用
        6)严格模式中 eval和arguments当作关键字,它们不能被赋值和用做变量声明
        7)严格模式会限制对调用栈的检测能力,访问arguments.callee.caller会抛出异常
        8)严格模式下变量必须先声明,直接给变量赋值,不会隐式建立全局变量,不能用with
        9)严格模式中 call、apply传入null、undefined保持原样不被转换为window

6. call,apply,bind的用法及区别
共同点和区别:

        1)apply 、 call 、bind 三者都是用来改变函数的this对象的指向的;
        2)apply 、 call 、bind 三者第一个参数都是this要指向的对象,也就是想指定的上下文(函数的每次调用都会拥有一个特殊值——本次调用的上下文(context)——这就是this关键字的值。);
        3)apply 、 call 、bind 三者均可以利用后续参数传参;
        4)bind 是返回对应函数,便于稍后调用;apply 、call 则是当即调用
        5)apply也能够有多个参数,可是不一样的是,第二个参数必须是一个数组
        6)call的的多个参数须要依次罗列传入

7.js中的this对象理解

        答:通常状况下:this的指向在函数定义的时候是肯定不了的,只有函数执行的时候才能肯定this到底指向谁实际上this的最终指向的是那个调用它的对象。可是,在几个特殊的状况下this的指向会有所变化,具体以下:
        1)状况1:若是一个函数中有this,可是它没有被上一级的对象所调用,那么this指向的就是window(非严格模式下)
        2)状况2:若是一个函数中有this,这个函数有被上一级的对象所调用,那么this指向的就是上一级的对象。
        3)状况3:若是一个函数中有this,这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象

知识点补充:

        1)在严格版中的默认的this再也不是window,而是undefined。
        2)new操做符会改变函数this的指向问题,虽然咱们上面讲解过了,可是并无深刻的讨论这个问题,网上也不多说,因此在这里有必要说一下。

function fn(){
    this.num = 1;
}
var a = new fn();
console.log(a.num); //1

参考:js中this的指向的原理详解

8. js中arguments的理解

        答:其实Javascript并无重载函数的功能,可是Arguments对象可以模拟重载。Javascrip中每一个函数都会有一个Arguments对象实例arguments,它引用着函数的实参,能够用数组下标的方式"[]"引用arguments的元素。其中arguments.length为函数实参个数,arguments.callee引用函数自身。
        固然,对于arguments来讲,其只是一个类数组对象,不能直接使用数组对象封装的内置方法,其转换为数组的方法为: Array.prototype.slice.call(arguments)

9. es6的箭头函数与function函数的区别

        答:二者的具体区别以下:
        1)箭头函数与function定义函数的写法

function demo(){console.log('demo')}
  var demo=()=>{consoloe.log('demo')}

        2) this指向的区别:使用function定义的函数,this的指向随着调用环境的变化而变化的,而箭头函数中的this指向是固定不变的,一直指向的是定义函数的环境。(箭头函数自己没有this对象)
        3) 构造函数的区别:function是能够定义构造函数的,而箭头函数是不行的(箭头函数自己没有this对象)
        4)变量提高的区别:因为js的内存机制,function的级别最高,而用箭头函数定义函数的时候,须要var(let const定义的时候更没必要说)关键词,而var所定义的变量不能获得变量提高,故箭头函数必定要定义于调用以前!

10. es6解构赋值

        对某种结构进行解析,而后将解析出来的值赋值给相应的变量,常见的有数组、对象、字符串、函数的解构赋值等
        1.数组解构赋值:1)模式匹配解构赋值,2)省略解构赋值,3)含剩余参数的解构赋值(省略号模式),4)set解构赋值:先执行new Set()进行数据去重 而后进行解构赋值,5)迭代器解构赋值
        2. 对象解构赋值:1)解构的属性名称必须保持一致,2)解构的属性名称能够直接使用冒号进行设置新的别名,设置为新的别名后,原属性名称不存在 只能进行别名调用
        3.字符串解构赋值
⚠️:解构赋值时 只有当变量严格等于(===)的时候才会进行赋值为默认值 其余状况下均直接赋值为变量的值
        总结:解构成对象,只要等号右边的值不是对象或者数组,就先将其转换为对象。因为undefined和null没法转换为对象,所以直接对它们解构赋值会直接报错;解构成数组,等号右边必须为了迭代的对象

11. 列举几个es6新特性及用途

        1. let定义变量,const定义常量(若是常量为对象 对象的属性能够修改),做用域为块级做用域,无变量提高 必须先定义才可使用
        2. 函数扩展:增长函数参数的默认值定义,箭头函数,箭头函数无this和arguments,可是其有refuse,其this指向箭头函数最近的一个函数,若是没有最近的函数则寻找到window对象
        3. 对象扩展:属性名为变量名时直接能够写属性名,Object.keys():获取对象的全部属性名和方法名组成一个数组,Object.assign():合并对象 有对象属性名相同时,后者覆盖前者;
        4. for of循环;
        5. import和export:export能够对外暴露一个module,import引入export暴露的module
        6. promise:异步解决方案,其包含三个状态,分别为:rejected:失败、resolveed:已完成、pending:进行中。promise构造函数中包括一个参数和一个带有resolve和reject的回调,对于实例化的promise能够经过promise.then来接受resolve和reject
        7. 解构赋值
        8. Set数据结构:一个类数组格式的数据结构,无重复值。size为长度,add()添加数据 返回Set自己,delete()删除一个数据 返还boolean值表示是否删除成功,has()查找某个元素 返回boolean是否找到,clear()清除全部的元素 无返回值
        9. 模板字符串,使用反引号进行模板字符串 内部可使用${}来书写变量

12. setTimeOut和setInterval的用法和区别

        答:对于二者来讲,其都是定义在必定的时间后进行相应函数的触发操做,可是两者也有明显的区别,具体以下:
        1)setTimeout(fn,t),超时调用,超过期间t,就执行fn;setInterval(fn,t),间歇调用,调用周期t,执行fn。
        2) 两者调用后,均返回一个数值ID,此ID是计划执行代码的惟一标识符,能够经过它来取消还没有执行的调用clearTimeout(id)是取消setTimeout的调用,clearInterval(id)是取消setInterval的调用。取消间歇调用的重要性要远远高于取消超时调用,由于在不加干涉的状况下,间歇调用将会一直执行到页面卸载。
        3) setTimeout(fn,t)中t告诉JS再过多久把当前任务添加到队列中。并非执行的到setTimeout就添加任务。若是队列是空,那么添加的代码会当即执行;若是队列不空,那么它就要等前面的代码执行完了之后在执行。二对于setInterval(fn,t),在间歇调用时,会出现一些意外:

setInterval(function () {
    func(i++);
}, 100)

        a、若是函数的执行时间小于100毫秒的时候,那么下一个循环调用将在100毫秒前完成;
        b、当func的执行时间多于100毫秒,在触发下一个func函数时(200ms处),上一个func尚未执行完,那么此时第二个func会在队列(event loop)中等待,直到第一个函数执行完,第二个func当即执行
        c、当第一个函数的执行时间特别长,以至于在执行过程当中出发了多个func时,就会致使第3个func及之后的函数被忽视。由于任务队列中不会有两个及以上同一个定时器的回调函数
        所以,若是须要每次函数在相同的间隔下完成指定的内容,则须要使用setTimeout,若是只是为了每隔必定范围的时间运行指定的内容,则使用setInterval
详情了解:setTimeout和setInterval的详解

13. 深度拷贝的用途及实现

        一、首先要理解js的数据类型,js基本类型主要包括String、Number、Boolean、Null、Undefined、Symbol:直接将值存在堆内存中,直接进行值赋值;引用类型包括:object、function、array,引用类型分为引用地址和具体值,引用地址存在堆内存中,具体值存在队内存中
        二、深拷贝和浅拷贝:经常使用的浅拷贝引用的方法:Object.assign()、Array.prototype.slice()、Array.prototype.concat();经常使用深拷贝:JSON.stringify()、JSON.parse()

具体实现js深度拷贝:
function deepCopy(obj) {
 if (obj instanceof Object) { 
            //当拷贝的是对象时
  let tmp =  {}
  for(let key in obj) {
   tmp[i] = deepCopy(key)
  }
  return tmp
 } else if (obj instanceof Array) { 
            //当拷贝的是数组时
  let tmp = []
  for (let i = 0;i < obj.length;i++) {
    tmp.push(deepCopy(obj[i]))
   }
   return tmp
 } else if (obj instanceof Function) {
          //当拷贝的是函数时
  return new Function('return '+ obj.toString())
 } else {
  return obj   //当拷贝的是基本数据类型
      }

参考地址:深度拷贝详解

14. 递归的用法及尾递归

        答:递归就是在函数体内重复的调用函数自己,达到循环计算的逻辑。可是,因为递归不断的调用函数自己,引用函数的时候会给函数引用开辟一个独立的内存空间直到因函数引用结束才会释放,所以待必定量后的递归就容易出现堆栈溢出。所以,为了优化递归的堆栈溢出,就提出了尾递归的概念:即:在函数的最后一步进行函数的引用,而不是在代码的最后一行进行函数的引用。具体原理以下:
        1. 堆栈:后进先出的原则。想象一下桌子上放一堆书,先放的在最底下,后放的在最顶部,拿书的时候,后放的被先拿走。即后进入堆栈的先出栈。
        2. 函数的堆栈概念:js中,每次函数调用会在内存造成一个“调用记录”, 保存着调用位置和内部变量等信息。若是函数中调用了其余函数,则js引擎将其运行过程暂停,去执行新调用的函数,新调用函数成功后继续返回当前函数的执行位置,继续日后执行。执行新调用的函数过程,也是将其推入到堆栈的最顶部并为其开辟一块内容。新函数执行完毕后将其推出堆栈,并回收内存。由此便造成了函数的堆栈。

15. js闭包的用法及注意事项

        答:闭包是实现可重用的局部变量,且保护其不受污染的机制。具体包括:
        1. 外层函数包裹受保护的变量和内层函数。
        2. 内层函数专门负责操做外层函数的局部变量。
        3. 将内层函数返回到外层函数外部,反复调用。

做用域

        子函数会一级一级地向上寻找全部父函数的变量。因此,父函数的全部变量,对子函数都是可见的,反之则不成立。

函数调用

        1. 外层函数调用了几回,就有几个受保护的局部变量副本。
        2. 同一次外层函数调用返回的多个内层函数,共同用一个局部变量。
        3. 闭包的局部变量不能释放。
        4. 尽可能不要在定时器中使用闭包。由于闭包的外层函数定义的变量不能释放,可是定时器使用中须要释放

闭包的危害

        1)引用的变量可能发生变化

function outer() {
      var result = [];
      for (var i = 0; i<10; i++){
        result.[i] = function () {
            console.info(i)
        }
     }
     return result
}
function outer() {
      var result = [];
      for (var i = 0; i<10; i++){
        result.[i] = function (num) {
             return function() {
                   console.info(num);    // 此时访问的num,是上层函数执行环境的num,数组有10个函数对象,每一个对象的执行环境下的number都不同
             }
        }(i)
     }
     return result
}

        2)this的指向问题

var object = {
     name: ''object",
     getName: function() {
        return function() {
             console.info(this.name)
        }
    }
}
object.getName()()    // underfined
// 由于里面的闭包函数是在window做用域下执行的,也就是说,this指向windows

        3)内存泄漏

function  showId() {
    var el = document.getElementById("app")
    el.onclick = function(){
      aler(el.id)   // 这样会致使闭包引用外层的el,当执行完showId后,el没法释放
    }
}

// 改为下面
function  showId() {
    var el = document.getElementById("app")
    var id  = el.id
    el.onclick = function(){
      aler(id)   // 这样会致使闭包引用外层的el,当执行完showId后,el没法释放
    }
    el = null    // 主动释放el
}
16. js原型链

        一、全部的引用对象都有__proto__属性,其指向它的构造函数的prototype属性,全部的函数都有prototype属性
        二、当试图获得一个对象的属性时,若是对象自己不存在这个属性,那么就会去它的__proto__属性中寻找

17. js面向对象编程

        js的面向对象编程和大多数其余语言如Java、C#的面向对象编程都不太同样。面向对象的两个基本概念:
        1. 类:类是对象的类型模板,例如,定义Student类来表示学生,类自己是一种类型,Student表示学生类型,但不表示任何具体的某个学生;
        2. 实例:实例是根据类建立的对象,例如,根据Student类能够建立出多个实例,每一个实例表示一个具体的学生,他们全都属于Student类型。
        JavaScript不区分类和实例的概念,而是经过原型(prototype)来实现面向对象编程

// 原型对象:
var Student = {
    name: 'Robot',
    height: 1.2,
    run: function () {
        console.log(this.name + ' is running...');
    }
};

function createStudent(name) {
    // 基于Student原型建立一个新对象:
    var s = Object.create(Student);
    // 初始化新对象:
    s.name = name;
    return s;
}

var xiaoming = createStudent('小明');
xiaoming.run(); // 小明 is running...
xiaoming.__proto__ === Student; // true
18. 操做数组的方法及用途

        答:操做数组的经常使用方法具体分为改变原数组、不改变原数组、遍历原数组等:

改变原数组的方法:

        push():向数组的尾部增长一个元素,并返回新的长度;
        pop():删除一个数组中的最后的一个元素,而且返回这个元素;
        shift():删除数组的第一个元素,并返回这个元素;
        unshift():向数组的开头添加一个或更多元素,并返回新的长度;
        reverse():颠倒数组中元素的顺序;
        splice():向/从数组中添加/删除项目,而后返回被删除的项目,第二个参数为0的时候表示不删除;
        sort():对数组元素进行排序,并返回这个数组;
        conpyWithin():在当前数组内部,将指定位置的成员复制到其余位置,并返回这个数组;
        fill():使用给定值,填充一个数组;

不改变原数组的方法:

        join():把数组中的全部元素经过指定的分隔符进行分隔放入一个字符串,返回生成的字符串;
        slice():返回一个从开始到结束(不包括结束)选择的数组的一部分浅拷贝到一个新数组对象,且原数组不会被修改;
        concat():方法用于合并两个或多个数组,返回一个新数组;
        indexOf():在数组中能够找到一个给定元素的第一个索引,若是不存在,则返回-1;
        lastIndexOf():返回指定元素,在数组中的最后一个的索引,若是不存在则返回 -1。(从数组后面往前查找);
        includes():返回一个布尔值,表示某个数组是否包含给定的值;

遍历数组的方法:

        forEach():按升序为数组中含有效值的每一项执行一次回调函数(没法中途退出循环,只能用return退出本次回调,进行下一次回调。它老是返回 undefined值,即便你return了一个值。)
        every():方法用于检测数组全部元素是否都符合函数定义的条件(若是数组中检测到有一个元素不知足,则整个表达式返回 false,且剩余的元素不会再进行检测。若是全部元素都知足条件,则返回 true)
        some():数组中的是否有知足判断条件的元素(若是有一个元素知足条件,则表达式返回true, 剩余的元素不会再执行检测。若是没有知足条件的元素,则返回false)
        filter():返回一个新数组, 其包含经过所提供函数实现的测试的全部元素
        map():建立一个新数组,其结果是该数组中的每一个元素都调用一个提供的函数后返回的结果
        reduce():对累加器和数组中的每一个元素(从左到右)应用一个函数,最终合并为一个值(reduceRight()是从右到左的执行)
        find()& findIndex() 根据条件找到数组成员:find()定义:用于找出第一个符合条件的数组成员,并返回该成员,若是没有符合条件的成员,则返回undefined。findIndex()定义:返回第一个符合条件的数组成员的位置,若是全部成员都不符合条件,则返回-1。
        keys()&values()&entries() 遍历键名、遍历键值、遍历键名+键值:三个方法都返回一个新的 Array Iterator 对象,对象根据方法不一样包含不一样的值

详细介绍:js数组中常见的操做方法

19. 操做字符串的方法及用途

        split():按照指定的格式将字符串进行拆分返回一个新的数组,第二个参数可选,表示返回的数组的最大长度
        indexOf():返回字符串中一个子串第一处出现的索引(从左到右搜索)。若是没有匹配项,返回 -1
        lastIndexOf():返回字符串中一个子串最后一处出现的索引(从右到左搜索),若是没有匹配项,返回 -1
        match():直接经过字符串进行匹配,也能够经过正则进行匹配
        slice():能够为负数,若是起始位置为负数,则从字符串最后一位向前找对应位数而且向后取结束位置,若是为正整数则从前日后取起始位置到结束位置
        substring():只能非负整数,截取起始结束位置同slice()函数一致
        substr():从起始位置开始截取,结束位置为第二个参数截取的字符串最大长度
        trim():删除字符串先后的空格
        charAt()/charCodeAt():返回指定位置的字符或其字符编码值
        replace():替换对应的内容,可直接替换也可以使用正则表达式替换

20. 双等于和三等于的区别

        答:双等因而非严格等于,只须要在值上相等便可为真;三等因而严格等于,必须在值和值的类型都一致的状况下才返回真

21. js的事件委托是什么

        答:事件委托就是利用了js的事件冒泡,经过事件冒泡来对多个事件进行委托触发。

事件委托的做用
  1. 支持为同一个DOM元素注册多个同类型事件
  2. 可将事件分红事件捕获和事件冒泡机制
事件委托的优势
  1. 提升性能:每个函数都会占用内存空间,只需添加一个事件处理程序代理全部事件,所占用的内存空间更少。
  2. 动态监听:使用事件委托能够自动绑定动态添加的元素,即新增的节点不须要主动添加也能够同样具备和其余元素同样的事件。
22. javascript的事件流模型都有什么

        一、“事件冒泡”:事件开始由最具体的元素接受,而后逐级向上传播

        二、“事件捕捉”:事件由最不具体的节点先接收,而后逐级向下,一直到最具体的

        三、“DOM事件流”:三个阶段:事件捕捉,目标阶段,事件冒泡

23. null和undefined的区别

        1)null是一个表示"无"的对象,转为数值时为0;undefined是一个表示"无"的原始值,转为数值时为NaN。

        2)当声明的变量还未被初始化时,变量的默认值为undefined。 null用来表示还没有存在的对象

        3)undefined表示"缺乏值",就是此处应该有一个值,可是尚未定义。典型用法是:

        (1)变量被声明了,但没有赋值时,就等于undefined。

        (2)调用函数时,应该提供的参数没有提供,该参数等于undefined。

        (3)对象没有赋值的属性,该属性的值为undefined。

        (4)函数没有返回值时,默认返回undefined。

        4)null表示"没有对象",即该处不该该有值。典型用法是:

        (1) 做为函数的参数,表示该函数的参数不是对象。

        (2) 做为对象原型链的终点。

24. new操做符具体干了什么

        1.建立一个空对象
        2.由this变量引用该对象
        3.该对象继承该函数的原型
        4.把属性和方法加入到this引用的对象中
        5.新建立的对象由this引用,最后隐式地返回this。
过程以下:

var obj={};
obj.__proto__=Base.prototype;
Base.call(obj);
25. js放在html中的位置

        html文件是自上而下的执行方式,但引入的css和javascript的顺序有所不一样,css引入执行加载时,程序仍然往下执行,而执行到<script>脚本是则中断线程,待该script脚本执行结束以后程序才继续往下执行。
        因此,大部分网上讨论是将script脚本放在<body>以后,那样dom的生成就不会由于长时间执行script脚本而延迟阻塞,加快了页面的加载速度。

        但又不能将全部的script放在body以后,由于有一些页面的效果的实现,是须要预先动态的加载一些js脚本。因此这些脚本应该放在<body>以前。

        其次,不能将须要访问dom元素的js放在body以前,由于此时尚未开始生成dom,因此在body以前的访问dom元素的js会出错,或者无效

        script放置位置的原则“页面效果实现类的js应该放在body以前,动做,交互,事件驱动,须要访问dom属性的js均可以放在body以后

26. 同源策略

        答:同源策略限制了一个源(origin)中加载文本或脚本与来自其它源(origin)中资源的交互方式。同源指的是协议、域名、端口相同,同源策略是一种安全协议。

27. 函数声明和函数表达式申明

        答:在Javscript中,解析器在向执行环境中加载数据时,对函数声明和函数表达式并不是是一视同仁的,解析器会率先读取函数声明,并使其在执行任何代码以前可用(能够访问),至于函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解析执行

相关文章
相关标签/搜索