天天看点

前端面试--JS

前端面试–JS

Q:JS数据类型

JS简单数据类型:String、Number、Boolean、Null、undefined、Symbol。复杂数据类型Object、Array、Function。

Q:null和undefined的区别

(1)null是一个表示”无”的对象,转我数值是为0,undefined是一个表示”无”的原始值,转为数值时为NaN。当声明的变量还未被初始化时,能量的默认值为undefined

(2)Null用来表示尚未存在的对象,常用来表示函数企图返回一个不存在的对象

(3)Undefined表示”缺少值”,就是此处应该有一个值,但是还没有定义。典型用法是:

a、变量被声明了,但没有赋值时,就等于undefined

b、调用函数时,应该提供的参数没有提供,该参数等于undefined

c、对象没有赋值属性,该属性的值为undefined

d、函数没有返回值时,默认返回undefined

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

 a、作为函数的参数,表示该函数的参数不是对象

 b、作为对象原型链的终点

Q:拖拽涉及的事件

前端面试--JS

Q:垃圾回收

什么是垃圾回收

JS垃圾回收机制的目的是为了防止内存泄漏,内存泄漏是指有一些已经不被需要的变量但仍然存在在内存中,这样便会造成内存泄漏。垃圾回收机制就是为了回收这些不被需要的变量,并且释放掉他们所指向的内存。

JS垃圾回收的方法

①标记清除

这是最常见的回收方法,也是大部分浏览器使用的回收方法。

当变量进入执行环境是,就会被标记上“进入环境”,从逻辑上讲,被标记上“进入环境”的变量所指向的内存是永远不会被回收的。当某个变量离开执行环境时,就会被标上“离开环境”。被标记为“离开环境”的变量则是可以回收的。

②引用计数

这是一种不太常见的回收方式。引用计数法就是同级引用类型声明后被引用的次数,当次数为0时,该变量就会被回收。

有可能造成内存泄漏的案例

①全局变量造成的内存泄漏

②未销毁的定时器和回调函数造成的内存泄漏

③DOM引用造成的内存泄漏

https://www.cnblogs.com/pingzi-wq/p/11531185.html

Q:原型链

因为每个对象和原型都有原型,对象的原型指向原型对象,而父的原型又指向父的父,这种原型层层连接起来的就构成了原型链。

Q:JS定时器

setInterval() :按照指定的周期(以毫秒计)来调用函数或计算表达式。方法会不停地调用函数,直到 clearInterval() 被调用或窗口被关闭。

setTimeout() :在指定的毫秒数后调用函数或计算表达式。

Q:数组和对象内置方法

数组内置方法

constructor 所建立对象的函数参考

prototype 能够为对象加入的属性和方法

Array.concat( ) 连接数组

Array.join( ) 将数组元素连接起来以构建一个字符串

Array.length 数组的大小

Array.pop( ) 删除并返回数组的最后一个元素

Array.push( ) 给数组添加元素

Array.reverse( ) 颠倒数组中元素的顺序

Array.shift( ) 将元素移出数组

Array.slice( ) 返回数组的一部分

Array.sort( ) 对数组元素进行排序

Array.splice( ) 插入、删除或替换数组的元素

Array.toLocaleString( ) 把数组转换成局部字符串

Array.toString( ) 将数组转换成一个字符串

Array.unshift( ) 在数组头部插入一个元素

对象内置方法

属性

constructor

prototype

实例方法

1、toString()返回当前对象的字符串形式,返回值为String类型。

2、toLocaleString()返回当前对象的"本地化"字符串形式,以便于当前环境的用户辨识和使用,返回值为String类型。

3、valueOf()返回指定对象的原始值。

静态方法

1.Object.assign(target, …sources)

功能:把一个或多个源对象的可枚举、自有属性值复制到目标对象中,返回值为目标对象。

2、Object.create(proto [,propertiesObject])

功能:创建一个对象,其原型为prototype,同时可添加多个属性。

3、Object.defineProperty(obj, prop, descriptor)

功能:在一个对象上定义一个新属性或修改一个现有属性,并返回该对象。

4、Object.defineProperties(obj, props)

功能:在一个对象上定义一个或多个新属性或修改现有属性,并返回该对象。

8、Object.getOwnPropertyNames(obj)

功能:获取目标对象上的全部自有属性名(包括不可枚举属性)组成的数组。

9.Object.getPrototypeOf(obj)

功能:获取指定对象的原型,即目标对象的prototype属性的值。

11、Object.keys(obj)

功能:获取目标对象上所有可枚举属性组成的数组。

Q:for in和for of的区别

a. for in 便历出来的是属性

b. for of 遍历的是value

c. 手动给对象添加属性后, for in 是可以将新添加的属性遍历出来 但是for of 不行

d. for in 的属性是使用[] 不可以使用 “.” eg: data[‘index’] instead of data.index

Q:继承的方式

1.原型链继承

2.借用构造函数(经典继承) 复制父类构造函数内的属性

call继承是在子类中把父类当做普通的函数去执行,让父类的this指向子类的实例,相当于给子类设置了很多私有的属性和方法。

3.组合继承

组合原型链继承和借用构造函数继承

背后的思路是:使用原型链实现对原型方法的继承,而通过借用构造函数来实现对实例属性的继承。

4.寄生式继承

创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来做增强对象,最后返回对象。

可以理解为在原型式继承的基础上新增一些函数或属性。

5.寄生组合式继承

子类构造函数复制父类的自身属性和方法,子类原型只接收父类的原型属性和方法。

https://www.jianshu.com/p/85899e287694

Q:数据类型检测

第一种:typeof可以检测一些基本的数据类型

第二种:valueof可以看到数据最本质内容(原始值)

第三种:instanceof检测当前实例是否隶属于某各类

双目运算符 a instanceof b 判断a的构造器是否为b,返回值是布尔值

第四种:isArray判断是否为数组,isNaN()判断是否为NaN

第五种:Object.prototype.toString

语法 Object.prototype.toString.call([value])

获取Object.prototype上的toString方法,让方法的this变为需要检测的数据类型值,并在Number、String、Array、Function…这些类的原型上都有一个toString方法:这个方法就是把本身的值转换为字符串

Q:怎么改变this指向

call、apply、bind

共同点:call和apply第一个参数都为改变this的指针,若为null/undefined,默认指向window

不同点:①call、apply可以自动执行,bind需手动调用

②call、bind都有无数个参数,apply只有两个参数,而且第二个参数为数组

Q:判断this指向

按照this指针的优先级,列出下面常会遇到的四种情况,从上到下依次是优先级从高到低(后面会详细比较优先级)。

1.函数是和new一起被调用的吗(new绑定)?如果是,this就是新构建的对象。

var bar = new foo()

2.函数是用call或apply被调用(明确绑定),甚至是隐藏在bind 硬绑定 之中吗?如果是,this就是明确指定的对象。

var bar = foo.call( obj2 )

3.函数是用环境对象(也称为拥有者或容器对象)被调用的吗(隐含绑定)?如果是,this就是那个环境对象。

var bar = obj1.foo()

4.否则,使用默认的this(默认绑定)。如果在strict mode下,就是undefined,否则是global对象。 var bar = foo()

Q:设计模式

(不全)

js设计模式有:单例,工厂,观察者等。

单例模式:保证一个类只有一个实例。实现方法:判断实际是否存在,不存在,创建实际,存在直接返回。

观察者模式:当改变一个对象会同时改变其他对象时,应该使用观察者模式。观察者模式主要作用是解耦。

工厂模式:通过对产品类的抽象使其创建业务主要负责用于创建多类产品实例。

Q:new实现

new的实现原理

• 以构造器的prototype属性为原型,创建新对象;

• 将this(也就是上一句中的新对象)和调用参数传给构造器,执行;

• 如果构造器没有手动返回对象,则返回第一步创建的新对象,如果有,则舍弃掉第一步创建的新对象,返回手动return的对象。

通过new创建对象经历4个步骤

1、创建一个新对象;[var o = {};]

2、将构造函数的作用域赋给新对象(因此this指向了这个新对象);[Person.apply(o)] [Person原来的this指向的是window]

3、执行构造函数中的代码(为这个新对象添加属性);

4、返回新对象。

https://blog.csdn.net/weixin_47047317/article/details/107567674

Q:防抖、节流

防抖:触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间。

节流:高频事件触发,但在n秒内只会执行一次,所以节流会稀释函数的执行频率。

防抖和节流都是为了限制事件的执行次数,防止出现事件的响应速度跟不上触发的速度,导致出现卡顿延迟的现象。

Q:哪些操作会造成内存泄漏

1.意外的全局变量

2.被遗忘的计时器或回调函数

3.脱离 DOM 的引用

4.闭包

第一种情况是我们由于使用未声明的变量,而意外的创建了一个全局变量,而使这个变量一直留在内存中无法被回收。

第二种情况是我们设置了 setInterval 定时器,而忘记取消它,如果循环函数有对外部变量的引用的话,那么这个变量会被一直留

在内存中,而无法被回收。

第三种情况是我们获取一个 DOM 元素的引用,而后面这个元素被删除,由于我们一直保留了对这个元素的引用,所以它也无法被回收。

第四种情况是不合理的使用闭包,从而导致某些变量一直被留在内存当中。

Q:event loop

事件循环机制

JavaScript是一门单线程非阻塞的脚本语言,单线程意味着,JavaScript代码在执行的任何时候,都只有一个主线程来处理所有的任务。而非阻塞则是当代码需要进行一项异步任务(无法立刻返回结果,需要花一定时间才能返回的任务,如I/O事件)的时候,主线程会挂起(pending)这个任务,然后在异步任务返回结果的时候再根据一定规则去执行相应的回调。

在异步函数中,可以细分为两种任务,宏任务与微任务。

宏任务有以下几种:

①I/O

②setTimeout

③setInterval

④setImmediate

⑤requestAnimationFrame

微任务有以下几种:

①process.nextTick

②MutationObserver

③Promise.then catch finally

js执行顺序: 同步函数 -> 微任务 -> 宏任务

async function async1() {

console.log(‘async1 start’);

await async2();

console.log(‘async1 end’);

}

async function async2() {

console.log(‘async2’);

}

console.log(‘script start’);

setTimeout(function() {

console.log(‘setTimeout’);

}, 0)

async1();

new Promise(function(resolve) {

console.log(‘promise1’);

resolve();

}).then(function() {

console.log(‘promise2’);

});

console.log(‘script end’);

Q:promise

(还需了解使用与实现、并行执行与顺序执行)

原理

在Promise的内部,有一个状态管理器的存在,有三种状态:pending、fulfilled、rejected。

(1) promise 对象初始化状态为 pending。

(2) 当调用resolve(成功),会由pending => fulfilled。

(3) 当调用reject(失败),会由pending => rejected

Q:闭包

闭包概念

能够读取其他函数内部变量的函数。或简单理解为定义在一个函数内部的函数,内部函数持有外部函数内变量的引用。

闭包用途

1、读取函数内部的变量

2、让这些变量的值始终保持在内存中。不会再f1调用后被自动清除。

3、方便调用上下文的局部变量。利于代码封装。

原因:f1是f2的父函数,f2被赋给了一个全局变量,f2始终存在内存中,f2的存在依赖f1,因此f1也始终存在内存中,不会在调用结束后被自动清除。

https://www.cnblogs.com/Renyi-Fan/p/11590231.html

Q:垃圾回收和内存泄露

垃圾回收

Javascript具有周期性的自动垃圾收集机制,执行环境会负责管理代码运行过程中使用的内存。

1.标记清除(mark-and-sweep)。常用的垃圾收集方式。当变量进入环境,就将这个变量标记为“进入环境”。离开环境就标记“离开环境”。

工作流程:

1)垃圾收集器在运行时给内存中的所有变量加上标记。

2)去掉环境中的变量和被环境中的变量引用的变量标记。

3)后来再加上的变量会被视为准备删除的变量。

4)销毁带标记的值并回收他们所占用的空间。

2.引用计数:

不太常用的垃圾收集策略(reference counting)。意为:跟踪记录每个值被引用的次数。

工作流程:

1)声明一个变量并将一个引用类型值赋给该变量,则这个值的引用次数为1。

2)同一个值若再次赋给另一个变量,则改值引用次数加1。

3)包含对这个值引用的变量又取得了另一个值,引用次数减1。

4)当引用次数变为0,则无法在访问这个值,就可以将其占用的内存回收回来。

5)垃圾回收器下次再运行时,释放引用次数为零的值被占用的内存。

内存泄露

对内存使用不当的话会造成内存泄露。以下这几种情况会造成内存的泄漏:

1.意外的全局变量引起的内存泄漏。

原因:全局变量,不会被回收。

解决:使用严格模式避免。

2.闭包引起的内存泄漏

原因:闭包可以维持函数内局部变量,使其得不到释放。

解决:将事件处理函数定义在外部,解除闭包,或者在定义事件处理函数的外部函数中,删除对dom的引用。

3.没有清理的DOM元素引用

原因:虽然别的地方删除了,但是对象中还存在对dom的引用

解决:手动删除。

4.被遗忘的定时器或者回调

原因:定时器中有dom的引用,即使dom删除了,但是定时器还在,所以内存中还是有这个dom。

解决:手动删除定时器和dom。

5.子元素存在引用引起的内存泄漏

原因:div中的ul li 得到这个div,会间接引用某个得到的li,那么此时因为div间接引用li,即使li被清空,也还是在内存中,并且只要li不被删除,他的父元素都不会被删除。

解决:手动删除清空。

继续阅读