面试题(js高级)

120 阅读12分钟

typeof 和 instanceof 区别(必会)

在 javascript 中,判断一个变量的类型可以用 typeof

1、数字类型、typeof 返回的值是 number。比如说:typeof(1),返回值是 number

2、字符串类型,typeof 返回的值是 string。比如 typeof(“123”返回值时 string)

3、布尔类型,typeof 返回的值是 boolean。比如 typeof(true)返回值时 boolean

4、对象、数组、null 返回的值是 object。比如 typeof(window),typeof(document), typeof(null)返回的值都是 object

5、函数类型,返回的值是 function。比如:typeof(eval),typeof(Date)返回的值都是function。

6、不存在的变量、函数或者 undefined,将返回 undefined。比如:typeof(abc)、typeof(undefined)都返回 undefined,使用 typeof 运算符无论引用的是什么类型的对象,它都返回”object” 运算符 instanceof 来解决这个问题。用于判断某个对象是否被另一个函数构造

2、js 使用 typeof 能得到的哪些类型?

typeof 只能区分值类型

typeof undefined // undefined

typeof null // object

typeof console.log // function

typeof NaN // number

解释一下什么是回调函数

回调函数就是一个通过调用的函数。如果你把函数的(地址)作为给另一个函数,当这个指 针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接 调用,而是在特定的事件或条件发生时由另外的一方调用的

什么是闭包

一个作用域可以访问另外一个函数内部的局部变量 ,或者说一个函数(子函数)访问另一个函数(父函数)中的变量。 此时就会有闭包产生 ,那么这个变量所在的函数我们就称之为闭包函数

优缺点:

  • 闭包的主要作用: 延伸了变量的作用范围, 因为闭包函数中的局部变量不会等着闭包函数执行完就销毁, 因为还有别的函数要调用它 , 只有等着所有的函数都调用完了他才会销毁

  • 闭包会造成内存泄漏,如何解决:用完之后手动释放

    详解:

    闭包不仅仅可以实现函数内部的作用域访问这个函数中的局部变量,还可以实现全局作用域或者是别的地方的作用域也可以访问到函数内部的局部变量 ,实现方法就是 return 了一个函数所以 return 函数也是我们实现闭包的一个主要原理, 因为返回的这个函数本身就是我们fn 函数内部的一个子函数 ,所以子函数是可以访问父函数里面的局部变量的, 所以返回完毕之后 ,外面的函数一调用, 就会回头调用返回的这个函数, 所以就可以拿到这个子函数对 应的父函数里面的局部变量.

注意:

1、由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在 IE 中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

2、闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象 (object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私 有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

什么是内存泄漏

内存泄露是指:内存泄漏也称作"存储渗漏",用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元。直到程序结束。(其实说白了就是该内存空间使用完毕之后未回收)即所谓内存泄漏。

哪些操作会造成内存泄漏

1、垃圾回收器定期扫描对象,并计算引用了每个对象的其他对象的数量。如果一个对象的引用数量为 0(没有其他对象引用过该对象),或对该对象的唯一引用是循环的,那么该对象的 内存即可回收 2、setTimeout 的第一个参数使用字符串而非函数的话,会引发内存泄漏

3、闭包、控制台日志、循环(在两个对象彼此引用且彼此保留时,就会产生一个循环)

对原型(prototype)理解

JavaScript 中所有都是对象,在 JavaScript 中,原型也是一个对象,通过原型可以实现 对象的属性继承

JavaScript 的函数对象中都包含了一个” prototype”内部属性,这个属 性所对应的就是该函数对象的原型

“prototype”作为函数对象的内部属性,是不能被直接访问的。所以为了方便查看一个对 象的原型,Firefox 和 Chrome 内核的 JavaScript 引擎中提供了”proto“这个非标准的访问 器

原型的主要作用就是为了实现继承与扩展对象

介绍下原型链(解决的是继承问题吗)

JavaScript 原型: 每个对象都会在其内部初始化一个属性,就是 prototype(原型)

原型链

访问一个对象的某个属性时,会先在这个对象本身属性上查找,如果没有找到,则会去 它的__proto__隐式原型上查找,即它的构造函数的 prototype,如果还没有找到就会再在 构造函数的 prototype 的__proto__中查找,这样一层一层向上查找就会形成一个链式结 构,我们称为原型链。

特点:

JavaScript 对象是通过引用来传递的,我们创建的每个新对象实体中并没有一份属于自己的原 型副本。当我们修改原型时,与之相关的对象也会继承这一改变

常见的 js 中的继承方法有哪些(必会)

ES5 继承有以下六种方法:

1、原型链继承 JavaScript 实现继承的基本思想:通过原型将一个引用类型继承另一个引用类 型的属性和方法

2、借用构造函数继承(伪造对象或经典继承) JavaScript 实现继承的基本思想:在子类构造 函数内部调用超类型构造函数。 通过使用 apply()和 call()方法可以在新创建的子类对 象上执行构造函数

3、组合继承(原型+借用构造)(伪经典继承) JavaScript 实现继承的基本思想:将原型链和 借用构造函数的技术组合在一块,从而发挥两者之长的一种继承模式,将原型链和借用构造函 数的技术组合到一起,从而取长补短发挥两者长处的一种继承模式

4、型式继承 JavaScript 实现继承的基本思想:借助原型可以基于已有的对象创建新对象, 同时还不必须因此创建自定义的类型

5、寄生式继承 JavaScript 实现继承的基本思想:创建一个仅用于封装继承过程的函数,该函 数在内部以某种方式来增强对象,最后再像真正是它做了所有工作一样返回对象。 寄生式继承是原型式继承的加强版

6、寄生组合式继承 JavaScript 实现继承的基本思想:通过借用函数来继承属性,通过原型链

的混成形式来继承方法

ES6 的继承:

1、使用 class 构造一个父类

class Parent {
constructor(name,age){
this.name = name
this.age = age
}
sayName(){
console.log(this.name);
}
}

2、使用 class 构造一个子类,并使用 extends 实现继承,super 指向父类的原型对象

class Child extends Parent{
constructor(name,age,gender){
super(name,age)
this.gender = gender
}
sayGender(){
console.log(this.gender);
}
}

3、实例化对象

const ming = new Child('ming',18,'男')
ming.sayGender()
ming.sayName()
console.log(ming.name);
console.log(ming.age);

介绍 this 各种情况

this 的情况:

1、以函数形式调用时,this 永远都是 window

2、以方法的形式调用时,this 是调用方法的对象

3、以构造函数的形式调用时,this 是新创建的那个对象

4、使用 call 和 apply 调用时,this 是指定的那个对象

5、箭头函数:箭头函数的 this 看外层是否有函数,

  • 如果有,外层函数的 this 就是内部箭头函数的 this

  • 如果没有,就是 window

6、特殊情况:通常意义上 this 指针指向为最后调用它的对象。这里需要注意的一点就 是 如果返回值是一个对象,那么 this 指向的就是那个返回的对象,如果返回值不是一个对象 那么 this 还是指向函数的实例

数组中的 forEach 和 map 的区别

forEach 和 map 的相同点

  • 都是循环遍历数组中的每一项

  • forEach 和 map 方法里每次执行匿名函数都支持 3 个参数,参数分别是 item(当前每一项),index(索引值),arr(原数组)

  • 匿名函数中的 this 都是指向 window 只能遍历数组 都不会改变原数组

区别 map 方法
  • 1.map 方法返回一个新的数组,数组中的元素为原始数组调用函数处理后的值

  • 2.map 方法不会对空数组进行检测,map 方法不会改变原始数组。若 arr 为空数组,则 map 方法返回的也是一个空数组。

  • 3.浏览器支持:chrome、Safari1.5+、opera 都支持,IE9+,

区别forEach 方法
  • 1.forEach 方法用来调用数组的每个元素,将元素传给回调函数

  • 2.forEach 对于空数组是不会调用回调函数的。 无论 arr 是不是空数组,forEach 返回的都是undefined。这个方法只是将数组中的每一项作为 callback 的参数执行一次

for in 和 for of 的区别

1、推荐在循环对象属性的时候使用 for...in,在遍历数组的时候的时候使用 for...of

2、for...in 循环出的是 key,for...of 循环出的是 value

3、注意,for...of 是 ES6 新引入的特性。修复了 ES5 引入的 for...in 的不足

4、for...of 不能循环普通的对象,需要通过和 Object.keys()搭配使用

call 和 apply,bind 的区别

共同点:

1、都是用来改变函数的 this 对象的指向的。

2、第一个参数都是 this 要指向的对象。

3、都可以利用后续参数传参。

call 方法调用一个函数, 其具有一个指定的 this 值和分别地提供的参数(参数的列表)。

  • 该方法的作用和 apply() 方法类似,只有一个区别,就是 call()方法接受的是若干个参数的列表,而 apply()方法接受的是一个包含多个参数的数组,方法调用一个具有给定 this 值的函数,以及作为一个数组(或类似数组对象)提供的参数。

  • call()方法的作用和 apply() 方法类似,区别就是 call()方法接受的是参数列表,而 apply()方法接受的是一个参数数组

bind()方法创建一个新的函数,当这个新的函数被调用时,其 this 值为提供的值,其参数列 表前几项,置为创建时指定的参数序列 将几次操作合并为一此操作进行。原理是维护一个计时器,规定在延迟时间后触发函数,但是

在延迟时间内再次触发的话,就会取消之前的计时器而重新设置。只有最后一次操作能被触发

js 防抖和节流

防抖(debounce)

所谓防抖,就是指触发事件后 n 秒后才执行函数,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。

防抖函数分为非立即执行版和立即执行版。

  • 非立即执行版的意思是触发事件后函数不会立即执行,而是在 n 秒后执行,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。在触发事件后函数 1 秒后才执行,而如果我在触发事件后的 1 秒内又触发了事件,则会重新计算函数执行时间。
  • 立即执行版的意思是触发事件后函数会立即执行,然后 n 秒内不触发事件才能继续执行函数的效果

节流(throttle)

所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数。 节流会稀释函数的执行频率。

对于节流,一般有两种方式可以实现,分别是时间戳版和定时器版。

  • 间戳版和定时器版的节流函数的区别就是,时间戳版的函数触发是在时间段内开始的时候,而定时器版的函数触发是在时间段内结束的时候。

应用场景

防抖(debounce)

  • search 搜索联想,用户在不断输入值时,用防抖来节约请求资源。

  • window 触发 resize 的时候,不断的调整浏览器窗口大小会不断的触发这个事件,用防抖来让其只触发一次

节流(throttle)

  • 鼠标不断点击触发,mousedown(单位时间内只触发一次)

  • 监听滚动事件,比如是否滑到底部自动加载更多,用 throttle 来判断

new 操作符具体干了什么

1、创建一个空对象: 并且 this 变量引入该对象,同时还继承了函数的原型

2、设置原型链 空对象指向构造函数的原型对象

3、执行函数体 修改构造函数 this 指针指向空对象,并执行函数体

4、判断返回值 返回对象就用该对象,没有的话就创建一个对象

split()和 join()的区别

split()是把一串字符(根据某个分隔符)分成若干个元素存放在一个数组里

即切割成数组的形式;

join() 是把数组中的字符串连成一个长串,可以大体上认为是 split()的逆操作