1.dom结构操作怎么添加、移除、替换、插入、创建和查找节点?
添加:appendChild()
移除:removeChild()
替换:replaceChild()
插入:insertBefore()
创建节点:
1、创建一个dom片段:createDocumentFragment()
2、创建一个元素:createElement()
3、创建一个文本节点:createTextnode()
查找节点:
1、通过标签名称:getElementsByTagName()
2、通过元素的 Name 属性的值:getElementsByName()
3、通过元素 Id,唯一性:getElementById()
2.Javascript动画和CSS3动画有什么区别?
- Javascript:
优点:
1、精细控制:可以通过改变属性值来实现对动画过程的精细控制,可实现css无法实现的动画效果,如动态的曲线运动,视差滚动等效果。
2、交互性:可以在动画过程中获得数据响应,并实时调整。
3、可以使用requestAnimationFrame浏览器Api解决丢帧,白屏问题。
4、兼容性:绘制的动画基于dom,在操作上不存在兼容性问题,JavaScript动画能够在几乎所有浏览器上运行。
缺点:
1、重复的操作DOM元素的css样式,对浏览器性能消耗大,容易造成丢帧,白屏等情况。
2、js动画只运行在主线程上,造成阻塞问题。
3、实现复杂动画效果需要大量的逻辑与判断,复杂度高。 - CSS3
优点:
1、性能高:CSS3动画通常使用GPU硬件加速,具有更好的性能表现。
2、体验好:CSS3动画具有更好的动画体验,效果更加流畅。
3、开发成本低:CSS3动画可以通过简单的CSS属性设置实现,开发成本较低。
缺点:
1、CSS3动画在一些旧版本的浏览器中不支持,需要额外编写代码兼容性
2、CSS3动画的控制力度较低,无法实现对动画过程的精细控制。
3.DOM事件模型?
一个事件发生后,会在子元素和父元素之间传播。这种传播分为三个阶段:
W3C制定的机制标准为先捕获再冒泡。
1、捕获阶段
2、目标阶段
3、冒泡阶段
4.document.write和innerHTML的区别?
1、类型:document.write()是对象中的方法,innerHTML是element对象中的属性
2、插入位置:document.write()插入在执行该脚本元素的script标签位置,innerHTML插入在指定元素位置。
3、性能:document.write会重绘整个页面,而innerHTML可以重绘页面的某一部分
5.什么是重绘?什么是回流(重排)?如何减小化重绘和回流?
- 回流:当Rendering Tree中部分元素的尺寸大小、布局、隐藏等属性改变时,浏览器的布局需要重新调整,需要重新渲染,这个过程就叫回流,也叫重排。(对整个页面进行排版)
- 重绘:当元素属性的改变(比如元素字体颜色,背景颜色)不会影响浏览器的布局,针对这个样式进行重新绘制。
- 减小化重绘和回流:
1、避免逐次改变样式,样式统一批量修改。
2、使DOM脱离文档流,再批量处理
3、缓存布局属性
4、 CSS3硬件加速:能够触发CSS3硬件加速的属性,彻底不会引起回流(transform、opacity、filters)
6.js脚本延迟加载的方式有哪些?
1、使用defer属性
<script src="test2.js" defer></script>
2、使用async属性
<script src="test2.js" async></script>
3、使用setTmeout属性
4、把js文件放在最后
7.什么是内存泄漏?
内存泄漏:是指程序动态分配的内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致运行速度减慢或系统崩溃等。
常见的内存泄漏:
1、意外的全局变量(变量未被定义或者胡乱引用了全局变量)
2、定时器(启动循环定时器后不清理)
3、闭包
4、事件监听未被移除
5、console.log(console.log对象是不能垃圾回收的)
6、缓存(建议所有缓存都设置过期时间,如果缓存过多也会造成内存泄漏)
8.什么是闭包?
闭包就是能够访问其他函数内部变量的函数
function fn(){
var a = 1;
function bar(){
console.log(a);
}
return bar;
}
var result = fn();
result();
//解决内存泄漏
result = null;
fn = null;
闭包的优点:可以访问函数内部的变量,并且不被全局污染,始终保存在内存中。
闭包的缺点:会使得函数中的变量始终保存在内存中,内存消耗大,所以不能滥用闭包,否则会造成页面性能问题,可能造成内存泄漏。
9.数组遍历的方式有哪些?性能如何?
1、for循环:所有循环遍历方法中性能最高的一种
for(i = 0,len=arr.length;i<len;i++){
}
2、forEach:性能比普通for循环弱
arr.forEach(function(e){
});
3、map:返回一个新数组,性能比forEach循环弱
let result = arr.map((item,index)=>{
if(index == 1) item = 2;
return item;
})
4、reduce:通常用来求和
var arr = [1, 3, 5, 7, 9];
arr.reduce((x,y)=>{
return x+y;
}) //25
5、filter:通过一些条件过滤一些数组的元素
var arr = [1, 2, 4, 5, 6, 9, 10, 15];
arr.filter((item)=>{
return item%2 !== 0;
}) // // [1, 5, 9, 15]
6、some:用于检测数组元素是否有元素满足指定条件,有一个满足就会返回true,遇到满足指定条件的元素,就返回 true,剩余元素不再进行检测。对于空数组不会检测,直接返回false。
let arr = [2,4,6,8,10];
arr.some((item)=>{
return item=2;
}) //true
7、every:用于检测数组元素是否都满足指定条件,若都满足才返回true,有一个不满足则返回false,剩余元素不再检测。对于空数组不会检测,直接返回true。
let arr = [2,4,6,8,10];
arr.every((item)=>{
return item>0;
}) //true
10.js的继承方式?
1、原型链继承:通过b函数的原型(b.prototype)指向a的实例(new a())来实现
缺点:对象实例共享所有继承的属性和方法(父类共享),不能传参。
function Parent(){
this.info = {
name: 'a',
age: 12
}
}
Parent.prototype.getInfo= function(){
console.log(this.info);
}
function Child() {}
Child.prototype = new Parent();
let c = new Child();
c.info.gender = "男";
c.getInfo(); //{name: 'a', age: 12, gender: '男'}
let c1 = new Child();
c1.getInfo(); //{name: 'a', age: 12, gender: '男'}
2、构造函数继承:在子类的构造函数内部调用父类的构造函数,使用call()将父对象的构造函数绑定在子对象上。
优点:解决了原形链继承中父类共享和不能传参的问题。
缺点:方法只能在构造函数中定义,无法实现函数复用,否则在父类原型上定义方法,在子类是不可见的。
function Parent(gender){
this.info = {
name: 'a',
age: 12,
gender:gender,
}
}
function Child(gender) {
Parent.call(this,gender)
}
let c1 = new Child('女');
c1.info; //{name: 'a', age: 12, gender: '女'}
3、组合式继承:将原型链和构造函数组合到一块,在原型上定义方法实现了函数复用,又能够保证每个实例都有自己的属性。
缺点: 无论在什么情况下,都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部。
function Parent(gender){
this.info = {
name: 'a',
age: 12,
gender:gender,
}
}
Parent.prototype.getInfo=function(){
console.log(this.info);
}
function Child(gender) {
Parent.call(this,gender)
}
Child.prototype = new Parent();
let c1 = new Child('女');
c1.info.age=1
c1.getInfo(); //{name: 'a', age: 1, gender: '女'}
4、原型式继承
5、寄生式继承
6、寄生组合式继承
7、extends继承 (ES6)
不是所有的浏览器都支持class关键字
class Parent{
constructor(){
this.name = 'a';
this.age = 12;
}
}
class Child extends Parent{
constructor(){
super()
}
}
let c = new Child()
c.name //'a'
11.改变函数内部this指针的指向函数(apply、call、bind的区别)?
如果不传参数,或者第一个参数时null或者undefined,默认this指向window
- call:接受若干个参数,会立即执行函数。
foo.call(obj,1,2);
- apply: 接受一个包含多个参数的数组,会立即执行函数。
foo.apply(obj,[1,2]);
- bind:接受若干个参数,不会立即执行,会返回一个新函数,需要将函数再执行一遍。
let fn = foo.bind(obj,1,2);
fn();
12.new 操作符做了哪些事情?
1、创建一个空对象
var obj=new Object();
2、将空对象的原型_proto_ 指向构造函数的prototype
obj.__proto__= Func.prototype;
3、将构造函数的this指向自己
var result = Func.call(obj);
4、如果是值类型,返回该对象,如果是引用类型返回该引用类型的对象
return typeof(result == 'object'))? result:obj;
13.防抖和节流
- 防抖:事件被触发n秒后再执行回调逻辑,如果在n秒内事件又被触发,则重新计算
比如:input输入框、收藏和取消收藏
function debounce(){
let timer = null;
clearTimeout(timer);
timer = seTimeout(()=>{
func();
},300)
}
- 节流:事件以n秒为间隔执行回调逻辑。
滚动事件
let timer = null
if(timer) return;
let timer = setTimeout(()=>{
func();
clearTimeout(timer);
timer = null;
},300)
14.eval()函数是做什么的?
接受一个字符串的参数,并把这个参数作为脚本代码执行
eval('2+3') //5
eval('1>0?true:false') //true
15.深拷贝和浅拷贝?
浅拷贝:只是复制了原数据的内存地址,两个数据的指针指向了相同地址,其中任一个数据改变会影响另一个。
Object.assign()、扩展运算符、Array.from()数组的浅拷贝。
深拷贝:开辟了一个新的内存空间,数据发生改变时不会影响原数据。
JSON.stringify /parse、直接遍历push
16.数组常用方法?
push、pop、unshif、shif、splice、slice、sort、map、reduce、filter、some、every、concat、join(将数组的每一项用指定的字符连接成一个字符串)、reverse(颠倒数组元素顺序)、indexOf(检索值在第一次出现的位置索引)、includes(判断数组是否包含一个指定的值)
17.简单介绍一下symbol?
是一种基本数据类型,表示独一无二的值,可用于对象属性的命名。
除了作为对象属性名,Symbol 还可以用于实现类似于枚举类型的功能。
在es6之前,javascript对象的属性名只能以字符串类型,容易造成属性名冲突的问题,为了解决这个问题,es6引入Symbol类型。
let name = Symbol('id');
var bj = {
[name]: 'a',
age:10
}
console.log(bj) //{age: 10, Symbol(id): 'a'}
18.localstorage、sessionstorage和cookie的区别?
1、webstorage本地存储
webstorage是本地存储: localStorage (本地存储)和 sessionstorage(本地存储)。
存放数据一般为5MB,只在客户端中保存,不与服务器参与通信。
localStorage:永久存储数据,除非用户主动删除,否则数据一直存在。
sessionstorage:存储特定于某个会话的数据,也就是关闭浏览器数据就不存在了。
2、cookie
即HTTP Cookie,在HTTP请求发送Set-Cookie HTTP头作为响应的一部分。通过name=value的形式存储。(主要保存登录信息)
存放数据大小为4k左右,与服务器参与通信。
只在设置的cookie过期时间之前一直有效。如果不设置过期时间,则关闭浏览器就失效。
19.cookie、session、token?
20.{}和[] 的valueOf和toString的区别?
valueOf:偏向于运算
toString:偏向于显示