您的位置:首页 > Web前端

完美世界前端实习生一面面经和总结

2019-04-10 14:50 323 查看

完美世界前端一面面经

主要还是以基础的问题为主,涉及了很多js的基础知识。

1. js如何进行事件监听?

事件就是在什么时候做什么事,比如在鼠标移入的时候弹出窗口就叫一个事件,事件监听也就是事件绑定。
JavaScript事件一共有三种监听方法分别如下:

1.1 事件监听一夹杂在html标签内

<div id="box" onClick="alert('HELLO WORLD')">
<div id="box2" onClick="notice();"></div>
<div id="box3" onClick="service('HELLO WORLD'');"></div>
</div>

function notice(){ alert(HELLO WORLD'");}
function service(str){ alert(str);}

说明:以上把事件监听功能onClick写到div中的形式是最古老原始形式,所有主流浏览器都支持。
类似行内CSS样式一样,是有效的(注意大小敏感),但是缺点和写到行内的CSS样式一样。

优点:

1)兼容性好,基本上所有浏览器都支持该种方式

缺点:

1)复用性不好。
2)JS与HTML夹杂到一块,代码混乱,发生错误难以检测和排除,不利于分工合作。
3)如果发生修改需要同时修改HTML和JS,改动相对困难。
为了解决以上问题把事件监听形式改进如下,以下方式也是目前主流的使用方式之一。

1.2 事件监听方法二 on+“事件”

<div id="box"></div>

var box = document.getElementById("box");

box.onclick = function(){
alert("HELLOW WORLD");
};

说明:通过以上形式可以把事件与HTML完全分离,是最常用的形式之一。
以上对于一般项目已经足够用。但如果想单击一次执行多个函数时,这种绑定方式就无法完成了

box.onclick = function(){
fnA();
fnB();
};
function fnA(){
alert("我会被执行");
}
function fnB(){
alert("我也会被执行");
}

说明:这种情况下 alert(“HELLOW WORLD”) 就不会被执行了,也就是说后面的函数覆盖了之前声明的函数。
优点:
1)兼容性好,基本支持所有浏览器
2)做到了文档与JS的分离,方便后期的代码管理

缺点:
1)同一个事件,在执行多个函数时会发生覆盖

1.3 事件监听方法三 element.addEventListener(事件名,函数,冒泡/捕获)

<div id="box">
<div id="box1"></div>
<div id="box2"></div>
</div>

var box = document.getElementById("box");
box.addEventListener("click",fnA,false);
box.addEventListener("click",fnB,false);

function fnA(){
alert("HELLO WORLD!");
}
function fnB(){
alert("HI CHINA!");
}

上面两个函数fA(),fB()都会执行,不会发生覆盖现象。

使用介绍:

addEventListener是DOM2的标准语法,新版本主流浏览器基本都支持。但是老版本IE浏览器不支持;

这种绑定方法有三个参数:

第一个是事件类型,不需要on前缀,但事件名需加 " " ;
第二个参数是发生这个事件的时候要调用的函数;

第三个是布尔值的true或false.(默认值是false)
false代码是以冒泡型事件流进行处理,一般都选用false.
true意味着代码以捕获型事件流进行处理,不是必须不推荐使用。

优点:

1)做到JS与HTML文档分离,便于代码维护;

2)不会发生像on+"事件"的函数覆盖现象;

3)提供监听的事件以冒泡或者捕获的可选方式执行

缺点:

1)兼容性还不完善,老板IE浏览器可能不兼容;

2)方法名较长,记忆稍有难度

注:使用element.removeEventListener(type,listener,useCapture);方法可以移除已经添加的实际。
使用方法:box.removeEventListener(“click”,fnB,false);
参考文章:js事件监听

2. 事件冒泡和事件捕获的原理?

JS事件冒泡和事件捕获
事件冒泡和事件捕获是描述事件触发时序问题的术语。
事件捕获指的是从document到触发事件的那个节点,即自上而下由外到内的去触发事件。
相反的,事件冒泡是自下而上由内到外的去触发事件。
绑定事件方法(addEventListener)的第三个参数,就是控制事件触发顺序是否为事件捕获。true,事件捕获;false,事件冒泡。默认false,即事件冒泡。
Jquery的e.stopPropagation会阻止冒泡,意思就是到我为止,我的爹和祖宗的事件就不要触发了。

<div id="parent">
  <div id="child" class="child"></div>
</div>
document.getElementById("parent").addEventListener("click",function(e){
alert("parent事件被触发,"+this.id);
})
document.getElementById("child").addEventListener("click",function(e){
alert("child事件被触发,"+this.id)
})

点击child div输出结果:
child事件被触发,child
parent事件被触发,parent
结论:先child,然后parent。事件的触发顺序自内向外,这就是事件冒泡。

如果改变第三个参数的值为true:

 document.getElementById("parent").addEventListener("click",function(e){
alert("parent事件被触发,"+e.target.id);
},true)
document.getElementById("child").addEventListener("click",function(e){
alert("child事件被触发,"+e.target.id)
},true)

点击child div输出结果:
parent事件被触发,parent
child事件被触发,child
结论:先parent,然后child。事件触发顺序变更为自外向内,这就是事件捕获。

事件冒泡经典案例

<ul>
<li>item1</li>
<li>item2</li>
<li>item3</li>
<li>item4</li>
<li>item5</li>
<li>item6</li>
</ul>

需求:鼠标放到li上对应的li的背景变为灰色
利用事件冒泡实现:

$("ul").on("mouseover",function(e){
$(e.target).css("background-color","#ddd").siblings().css("background-color","white");
})

也许有人会说,我们直接给所有li都绑上事件也可以啊,一点也不麻烦,只要……

$("li").on("mouseover",function(){
$(this).css("background-color","#ddd").siblings().css("background-color","white");
})

是,这样也行。而且从代码简洁程度上,两者是相若仿佛的。但是,前者少了一个遍历所有li节点的操作,所以在性能上肯定是更优的。
还有就是,如果我们在绑定事件完成后,页面又动态的加载了一些元素……

$("<li>item7</li>").appendTo("ul");

这时候,第二种方案,由于绑定事件的时候item7还不存在,所以为了效果,我们还要给它再绑定一次事件。而利用冒泡方案由于是给ul绑的事件所以不用再重新进行一次绑定了。

3. new 关键字来调用构造函数的时候发生了什么?

var obj = new Constructor();

第一,在内存中创建一个空对象obj。

第二,将这个空对象的__proto__成员指向了构造函数对象的prototype成员对象.

第三,执行构造函数,将构造函数的作用域赋给新对象。

第四,返回新对象obj。

4. this关键字的指向问题

  1. 普通函数中的this指向的是Window。
  2. 对象的方法中的this指向的是这个方法所属的对象。
  3. 构造函数中的this指向的是构造函数所创建的对象。
  4. 函数在定义的时候this是不确定的,只有在调用的时候才可以确定。

5. JS是如何实现继承的

JS继承的方式

既然要实现继承,那么首先我们得有一个父类,代码如下:

// 定义一个动物类
function Animal (name) {
// 属性
this.name = name || 'Animal';
// 实例方法
this.sleep = function(){
console.log(this.name + '正在睡觉!');
}
}
// 原型方法
Animal.prototype.eat = function(food) {
console.log(this.name + '正在吃:' + food);
};

1. 原型链继承

核心: 将父类的实例作为子类的原型

function Cat(){
}
Cat.prototype = new Animal();
Cat.prototype.name = 'cat';

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.eat('fish'));
console.log(cat.sleep());
console.log(cat instanceof Animal); //true
console.log(cat instanceof Cat); //true

特点:

非常纯粹的继承关系,实例是子类的实例,也是父类的实例
父类新增原型方法/原型属性,子类都能访问到
简单,易于实现
缺点:

要想为子类新增属性和方法,必须要在new Animal()这样的语句之后执行,不能放到构造器中
无法实现多继承
来自原型对象的所有属性被所有实例共享(来自原型对象的引用属性是所有实例共享的)(详细请看附录代码: 示例1)
创建子类实例时,无法向父类构造函数传参

2. 构造继承
核心: 使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)

function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // false
console.log(cat instanceof Cat); // true

特点:
解决了1中,子类实例共享父类引用属性的问题
创建子类实例时,可以向父类传递参数
可以实现多继承(call多个父类对象)

缺点:
实例并不是父类的实例,只是子类的实例
只能继承父类的实例属性和方法,不能继承原型属性/方法
无法实现函数复用,每个子类都有父类实例函数的副本,影响性能

(写到一半,等待补充)

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: