vw、vh、rem、em、px的区别
px
px是绝对单位,1px就是一个像素点em
em是相对单位,是相对于父级的font-sizerem
rem是相对单位,是相对于html的font-sizevw
vw是相对单位,相对于视口宽度,1vw是视口宽度的 1%vh
vh是相对单位,相对于视口的高度,1vh是视口高度的 1%
前端安全:xss攻击和CSRF攻击
- xss攻击就是通过用户输入的信息,比如input、textarea等,输入一段html、css、js代码,接着提交到数据库,其他用户访问对应页面的时候,就会触发攻击。
- 常见的攻击:css攻击,body { display: none !important; },js攻击,通过script标签嵌入js代码,html攻击,嵌入a标签超链接,指向攻击网站。
- 防御方式:对用户的输入进行过滤,将用户输入的标签内容或者事件内容过滤掉,让它形成不了代码块,只能显示对应文本即可。
- 一般是后端进行替换。
- CSRF攻击,是跨站请求伪造,是用户先打开一个正常页面,接着完成登录等身份验证,在本地浏览器存储了cookie值,接着在钓鱼网站或者攻击网站上,点击a标签超链接,或者触发其他事件,而这个链接指向的就是那个正常网页的API接口,当用户点击之后,就可以通过之前正常网站存储的cookie访问到接口的操作。
- 常见的攻击:新浪微博用户粉丝增加,在支付的时候通过CSRF攻击直接调用支付接口
- 防御方式:使用token验证,每次向后台发送请求都携带token,这样不是由前端发送的请求就过滤掉。或者对网页来源进行验证,如果不是我们自己的网页,就过滤。
- XSS是像你的页面注入JS脚本执行,在JS里面去做他想做的事情;无需做权限认证;
- CSRF是用你API本身的漏洞,帮你自动执行;需要登录认证;
http和https的区别
- HTTPS相较于HTTP更加安全
- HTTPS是密文传输,HTTP是明文传输
- 所以HTTPS不会被劫持,而HTTP协议传输的内容可以被劫持
- HTTPS默认使用443端口,而HTTP默认使用80端口
优雅降级和逐渐增强
- 优雅降级相当于向下兼容,就是先针对最主流、最高级、最完善的浏览器进行产品设计,接着在开发的最后阶段,通过测试以及兼容处理,保证低级浏览器可以提供不影响使用的功能即可。
- 优雅降级根据公司不同,项目不同,所需要兼容的浏览器等级也不同,一般可以是IE8就可以了
- 渐进增强相当于向上兼容,一开始就针对低级浏览器进行产品设计,然后在针对高级浏览器设计更好的用户体验和交互动效。
什么是堆和栈
- 在js中分为基本数据类型和引用数据类型,而基本数据类型(Number、String、null、undefined、Boolean)都是存放在栈内存中的,占据固定大小的空间,而引用类型(Object)是存放在堆内存中的,在栈内存中存放了对应的引用,按照引用访问堆内存的数据。
- 对于引用类型,存放在栈内存中的就是一个引用指针,这个指针指向堆内存中的数据,而数据的大小也是不固定的。
- 堆:先进先出,动态内存空间,不会自动释放
- 栈:先进后出,自动分配内存空间,会自动释放
什么是BFC
- BFC是块级格式化上下文,BFC是一个独立的布局环境,其中的元素布局是不受外界的影响,并且在一个BFC中,块盒与行盒(行盒由一行中所有的内联元素所组成)都会垂直的沿着其父元素的边框排列。
- 它是页面中的一块渲染区域,并且有一套渲染规则,它决定了其子元素将如何定位,以及和其他元素的关系和相互作用。
- 具有 BFC 特性的元素可以看作是隔离了的独立容器,容器里面的元素不会在布局上影响到外面的元素,并且 BFC 具有普通容器所没有的一些特性。
- 内部的Box会在垂直方向,一个接一个地放置
- 在BFC垂直方向的边距会发生重叠
- BFC的区域不会与float区域的box重叠
- BFC是一个页面上的独立的容器,外面的元素不会影响BFC里的元素,反过来,里面的也不会影响外面的
- 计算BFC高度的时候,浮动元素也会参与计算
- 浮动元素和绝对定位元素,非块级盒子的块级容器(例如 inline-blocks, table-cells, 和 table-captions),以及overflow值不为“visiable”的块级盒子,都会为他们的内容创建新的BFC(块级格式上下文)。
- 1、float的值不是none。
2、position的值不是static或者relative。
3、display的值是inline-block、table-cell、flex、table-caption或者inline-flex
4、overflow的值不是visible - 什么时候使用BFC:上下边距重叠、高度塌陷
什么是闭包,闭包的优缺点,内存泄露怎么处理,闭包的使用场景
- 闭包就是一种可以重复使用变量而且不会造成全局变量污染的机制。
- 因为:全局变量可以重复使用,但是容易造成变量污染。局部变量仅在局部作用域内有效,不可以重复使用,不会造成变量污染。闭包结合了全局变量和局部变量的优点。
- 闭包的优点:可以重复使用变量,并且不会造成变量污染,可以用来定义私有属性和私有方法。
- 闭包的缺点:比普通函数更占用内存,会导致网页性能变差,在IE下容易造成内存泄露。
- 内存泄漏的解决方案:在退出函数之前,将不使用的局部变量全部删除。手动释放闭包内的变量。
- 闭包的使用场景:封装功能时(需要使用私有的属性和方法),函数节流、函数防抖、函数柯里化、给元素伪数组添加事件需要使用元素的索引值。
内存泄露
-
造成内存泄露的原因:
- 意外的全局变量(在函数内部没有使用var进行声明的变量)
- console.log
- 闭包
- 对象的循环引用
- 未清除的计时器
- DOM泄露(获取到DOM节点之后,将DOM节点删除,但是没有手动释放变量,拿对应的DOM节点在变量中还可以访问到,就会造成泄露)
- 解决方案:手动释放内存来避免内存泄露
将多维数组转化为一维数组
·通过apply拉平数组,但是只能拉平二维数组
let arr = [1, 2, [3, 4], 5]; let result = Array.prototype.concat.apply([], arr); // [1, 2, 3, 4, 5]
通过ES6新增的flat方法拉平数组,默认拉平二维数组,参数默认为1,可以写n,则拉平n+1维数组,对于未知维度的数组,可以通过Infinity关键字作为参数
let arr = [1, 2, [3, 4, [5, 6]], 7]; let result = arr.flat(); // [1, 2, 3, 4, [5, 6], 7]; let result2 = arr.flat(2); // [1, 2, 3, 4, 5, 6, 7]; let result3 = arr.flat(Infinity);
通过递归拉平数组
let arr = [1, 2, [3, 4, [5, 6]], 7]; function flatArray(arr) { let result = []; for(let i = 0; i < arr.length; i++) { if(Array.isArray(arr[i])) { result = result.concat(arguments.callee(arr[i])); }else { result.push(arr[i]) } } return result; } flatArray(arr); // [1, 2, 3, 4, 5, 6, 7];
JS继承的方法及优缺点
-
原型链继承
function Parent() { this.name = 'parent' } Parent.prototype.sayName = function() { alert(this.name) } function Child() { this.age = 20; } Child.prototype = new Parent(); // 改造子类构造函数的原型,使其指向父类的实例。 let child = new Child(); child.name; // parent child.sayName; // alert(parent); child.age; // 20
通过改造子类的原型prototype,使其指向父类的实例,达到继承父类所有属性和方法的效果。
优点:父类的方法也可以达到复用
缺点:父类的属性也达到了复用,子类实例没有属于自己的属性(如上例中的name属性)
-
构造函数借用继承
function Parent(name) { this.name = name; } Parent.prototype.sayName = function() { alert(this.name); } function Child(name, age) { Parent.call(this, name); // 通过call或者apply的特性实现将父类的属性复用 this.age = age; } Child.prototype.sayAge = function() { alert(this.age); } let child = new Child('child', 24); child.name; // child child.age; // 24 child.sayName(); // error child.sayAge(); // alert(child)
通过call和apply改变this指向并且调用函数的特性,实现父类的属性复用。
优点:可以达到父类的属性复用,并且可以定制子类自己的属性值。
缺点:父类上的方法没有实现复用
-
组合继承
function Parent(name) { this.name = name; } Parent.prototype.sayName = function() { alert(this.name); } function Child(name, age) { Parent.call(this, name); // 通过call或者apply的特性实现将父类的属性复用 this.age = age; } Child.prototype = new Parent(); // 改造子类构造函数的原型,使其指向父类的实例。 Child.prototype.sayAge = function() { alert(this.age); } let child = new Child('child', 24); child.name; // child child.age; // 24 child.sayName(); // alert(child) child.sayAge(); // alert(24)
组合继承,其实就是将原型链继承和构造函数借用继承组合到一块,结合他们之间的优点,使得既可以继承父类的方法,又可以在子类上继承父类的属性并定制。
优点:结合了原型链继承和构造函数借用继承的优点,既继承了父类的方法,又继承了父类的属性,并可以定制。
缺点:父类的构造函数被调用了两次,在子类的构造函数内调用一次,在子类的prototype上调用一次,并且子类的prototype上的属性会被覆盖掉,造成内存浪费。
-
原型式继承
function Parent(name) { this.name = name; } Parent.prototype.sayName = function() { alert(this.name); } function createObject(obj) { function F() {}; F.prototype = obj; return new F(); } let parent = new Parent('parent'); let child = createObject(parent); child.name; // parent; child.sayName(); // alert(parent)
原型式继承是通过借助临时的构造函数,将原有的对象的属性和方法进行浅拷贝,然后赋予新的对象,实现继承。通过ES5新增的Objet.create也可以实现。
优点:好像没什么优点
缺点:缺点和原型链继承一样,只能实现属性和方法的复用,子类没有属于自己的属性,没有可定制性。
-
寄生式继承
function Parent(name) { this.name = name; } Parent.prototype.sayName = function() { alert(this.name); } function createObject(obj) { function F() {}; F.prototype = obj; return new F(); } function Child(name, age) { let parent = new Parent(name); let result = createObject(parent); result.age = age; return result; } let child = Child('child', 24); child.sayName(); // alert(child)
寄生式继承是将原型式继承封装成为一个函数,并且进行参数传递,实现子类继承父类的属性和方法,并且可以定制属性。
优点:子类可以继承父类的属性和方法,并且可以定制属性
缺点:子类不是通过new关键字调用的,所以没有属于自己的方法,如果要创建子类的方法,那就是每一个子类都得重新创建一遍,不能达到子类的方法复用。
-
组合寄生式继承
function Parent(name) { this.name = name; } Parent.prototype.sayName = function() { alert(this.name); } function Child(name, age) { Parent.call(this, name); this.age = age; } Child.prototype = Object.create(Parent.prototype); Child.prototype.constructor = Child; Child.prototype.sayAge = function() { alert(this.age); } let child = new Child('child', 24); child.sayName(); // alert(child) child.sayAge(); // alert(24)
组合寄生式继承,结合了组合继承的优点,并结合了寄生式继承的优点,目前为止完美的继承方式。
优点:可以继承父类的属性和方法,并且可以定制,最主要的是对于父类的构造函数只需要调用一次。并且可以正常的使用intanceof和isPrototypeOf,原型链保持正常。
缺点:好像没什么缺点