0%

常规面试总结2017

常规面试总结2017

导语:

在2017年对前端领域内的常见的问题进行系统性的总结,说法可能会有所不妥,会综合网上资源发表自己的见解

1.前端安全的理解与防范

开发过程中不可避免的会出现漏洞,黑客会抓住漏洞去攻击它获取利益,所以我们需要让我们的应用变的更加安全。

前端攻击有哪些形式,如何防范

XSS攻击:

一种安全漏洞,允许代码植入到其他页面中,通过插入script标签获取用户信息

如何防范: 将前端输入输出数据进行转义,避免使用eval执行个人重要信息,使用httpOnly提升cookie的安全性,限制web页面浏览器端script程序读取cookie。当使用append的时候,Jquery会将元素变为fragment,接着查找其中的script标签,使用eval去执行,会造成之前的问题,所以将输入输出部分进行转义,同样使用img标签时当加载失败时会调用onError方法,此时插入攻击代码同样需要转义进行防范

CSRF攻击:

跨站请求伪造,利用一些提交行为转换到操作其他网站(在网站支付时打款被转义到黑客账户);

如何防范: 遵循http协议,token即时验证,添加验证码阻止信息外泄

控制台注入代码:

不懂的人会被欺骗到某个网站在控制台通过执行某段代码暴漏个人信息从而被黑客截取。

2.this指向与箭头函数

this指向

this在函数执行时被绑定,指向为调用该函数的对象。函数调用模式的不同造就了this指向问题上的差异。
函数作为一个对象的方法时,this指向该对象

1
2
3
4
5
6
7
var name = 'window';
var obj = {
name: 'lee',
say: function(){
console.log(this.name); // lee
}
}

函数暴漏在全局作用域下,this指向为window
1
2
3
4
var name = 'window';
function say(){
console.log(this.name) // lee
}

构造函数内部this指向为构造函数实例对象
1
2
3
4
5
function Obj(){
this.name = 'lee';
}
var obj = new Obj();
obj.name // lee;

改变this指向的方法: apply、call、bind, call与apply的区别在于第二部分参数前者为多个参数,后者为一个参数数组,bind与它们的差异在于只是返回一个改变了this指向的新函数,需要调用,而apply与call在改变了this指向后立即执行了。

1
2
3
4
5
6
7
8
9
10
11
12
var name = "window";
var person = {
name: "lee"
};
function say() {
console.log(this.name);
}
say(); //window
say.apply(person); //lee
say.call(person); //lee
say.bind(person)(); //lee
say.apply(); //window

箭头函数

es6新增的特性之一,简化了函数定义.

1
2
3
x => ({
foo: x // 单纯的返回一个表达式对象,注意需要加()
})

this

箭头函数内部this是词法作用域,由上下文确定;而函数中的this指向则在函数执行时根据调用模式确定。

1
2
3
4
5
6
7
8
9
10
11
12
var obj = {
birth: 1990,
getAge: function () {
var b = this.birth; // 1990
var fn = function () {
return new Date().getFullYear() - this.birth; // 匿名函数this指向window
};
var fn = () => new Date().getFullYear() - this.birth; // this指向obj对象

return fn();
}
};

3.var let const 之间的区别

var定义的变量是该变量作用域的局部变量,可以定义全局变量,但是会污染全局环境不容易维护,不推荐。
const定义常量,且不可重新赋值,但是如果定义的变量是一个对象的话,对象内部变量是可以改变的。
let声明块级作用域,块作用域内的变量在包含它们的块或for循环之外是不能访问的,否定变量声明提升,对var的一种增强。
es6里面不建议使用var,因为其没有块级作用域,非严格模式下会有变量声明提升的情况,会产生意想不到的错误。

1
2
3
4
5
for(var i = 0; i <5; i++){
setTimeout(function(){
console.log(i)
},10)
}

结果会是 5 5 5 5 5; 为什么呢,怎么能打印出想要的 0,1,2,3,4,呢?

setTimeout事件在for循环结束后触发,此时i的值为5,解决方法;

1
2
3
4
5
for(var i = 0; i<5; i++){
(function(i){
setTimeout(function() { console.log(i); }, 10);
})(i)
}

利用立即执行函数迭代i的值;如果利用块级作用域呢?
1
2
3
4
5
for(let i = 0; i <5; i++){
setTimeout(function(){
console.log(i)
},10)
}

每次for循环都会产生对应的作用域

4.深拷贝与浅拷贝

浅拷贝只是复制了对象的指针,不会赋值对象本身,公用一块内存,所以改变一个对象的属性值都会变化,

1
2
3
4
5
6
7
8
var a = {
name: 'john'
}
var b = a;
b.name = 'jeff';

a // { name: 'jeff};
b // { name: 'jeff};

深拷贝则复制了对象,不会共享内存与指针,修改一个不会影响到另一个;
1
2
3
4
5
6
7
8
var a = {
name: 'john'
}
var b = Object.assign({}, a);
b.name = 'jeff';

a // { name: 'john};
b // { name: 'jeff};

但是由于javascript中存储对象都是存地址的,Object.assign的局限性存在于它只是相对浅拷贝深入了一层,换句话就是如果对象的属性值是一个指向对象的引用,它只拷贝那个引用值,可以利用对象字符串的转换(JSON.parse(JSON.stringify(obj))与递归实现真正的深拷贝。
1
2
3
4
5
6
var obj = { a:1, arr: [2,3] };
var shadowObj = Object.assign({}, obj);
obj.arr[0] = 22;
obj.a = 11;
// obj { a: 11, arr: [22,3]}
// shadObj { a: 1, arr: [22,3]}