JavaScript面试题集合(三)
目录:
1.JS中的事件模型
事件模型:
详细请参考:https://www.cnblogs.com/leftJS/p/10948138.html
JavaScript事件模型主要分为3种:原始事件模型、DOM2事件模型、IE事件模型。
-
原始事件模型
所有浏览器都支持的事件模型,对于原始事件而言,没有事件流,事件一旦发生将马上进行处理,有两种方式可以实现原始事件:
<!-- 在html代码中直接指定属性值 --> <button id="demo" type="button" onclick="doSomeTing()" />
// 在js代码中为 document.getElementsById("demo").onclick = doSomeTing()
优点:所有浏览器都兼容
缺点:1)逻辑与显示没有分离;2)相同事件的监听函数只能绑定一个,后绑定的会覆盖掉前面的,如:a.onclick = func1; a.onclick = func2;将只会执行func2中的内容。3)无法通过事件的冒泡、委托等机制(后面会讲到)完成更多事情
-
DOM2事件模型
此模型是W3C制定的标准模型,现代浏览器(IE6~8除外)都已经遵循这个规范。W3C制定的事件模型中,一次事件的发生包含三个过程:
(1).事件捕获阶段,(2).事件目标阶段,(3).事件冒泡阶段
看下图
事件捕获:当某个元素触发某个事件(如onclick),顶层对象document就会发出一个事件流,随着DOM树的节点向目标元素节点流去,直到到达事件真正发生的目标元素。在这个过程中,事件相应的监听函数是不会被触发的。
事件目标:当到达目标元素之后,执行目标元素该事件相应的处理函数。如果没有绑定监听函数,那就不执行。
事件冒泡:从目标元素开始,往顶层元素传播。途中如果有节点绑定了相应的事件处理函数,这些函数都会被一次触发。
-
IE事件模型
IE的事件模型只有两步,先执行元素的监听函数,然后事件沿着父节点一直冒泡到document。IE的事件模型已经可以解决原始模型的三个缺点,但其自己的缺点就是兼容性,只有IE系列浏览器才可以这样写
var demo = document.getElementById('demo'); if(demo.attachEvent){ demo.attachEvent('onclick',func); }else{ demo.addEventListener('click',func,false); }
事件:
- 事件概念:事件,大部分情况下指的是用户的鼠标动作和键盘动作,如点击、移动鼠标、按下某个键。事件被封装成一个event对象,包含了该事件发生时的所有相关信息(event的属性)以及可以对事件进行的操作(event的方法)
事件常见的属性:
事件 | 说明 |
---|---|
clientX/clientY | 距浏览器可视区域(工具栏除外区域)左/上的距离 |
pageX/pageY | 距页面左/上的距离,它与clientX/clientY的区别是不随滚动条的位置变化 |
screenX/screenY | 距计算机显示器左/上的距离,拖动你的浏览器窗口位置可以看到变化 |
target | 发生事件的节点 |
currentTarget | 当前正在处理的事件的节点,在事件捕获或冒泡阶段 |
timeStamp | 事件发生的时间,时间戳 |
bubbles | 事件是否冒泡 |
cancelable | 事件是否可以用preventDefault()方法来取消默认的动作 |
keyCode | 按下的键的值 |
常见的方法:
方法 | 说明 |
---|---|
event. preventDefault() | 阻止元素默认的行为,如链接的跳转、表单的提交 |
event. stopPropagation() | 阻止事件冒泡 |
event.initEvent() | 初始化新事件对象的属性,自定义事件会用,不常用 |
常见的事件类型:
事件类型 | 说明 |
---|---|
Events | 包括所有的事件 |
HTMLEvents | 包括 ‘abort’, ‘blur’, ‘change’, ‘error’, ‘focus’, ‘load’, ‘reset’, ‘resize’, ‘scroll’, ‘select’, ‘submit’, ‘unload’. 事件 |
UIEevents | 包括 ‘DOMActivate’, ‘DOMFocusIn’, ‘DOMFocusOut’, ‘keydown’, ‘keypress’, ‘keyup’. 间接包含 MouseEvents. |
MouseEvents | 包括 ‘click’, ‘mousedown’, ‘mousemove’, ‘mouseout’, ‘mouseover’, ‘mouseup’ |
MutationEvents | 包括 ‘DOMAttrModified’, ‘DOMNodeInserted’, ‘DOMNodeRemoved’, ‘DOMCharacterDataModified’, ‘DOMNodeInsertedIntoDocument’, ‘DOMNodeRemovedFromDocument’, 'DOMSubtreeModified |
2. javascript 代码中的"use strict";是什么意思 ? 使用它区别是什么
use ‘strict’: "严格模式"是一种在JavaScript代码运行时自动实行更严格解析和错误处理的方法。这种模式使得Javascript在更严格的条件下运行。
设立"严格模式"的优点:
-
消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为;
-
消除代码运行的一些不安全之处,保证代码运行的安全;
-
提高编译器效率,增加运行速度;
-
为未来新版本的Javascript做好铺垫。
严格模式的示例:
// 变量必须先声明,再使用
function test(){
"use strict";
foo = 'bar'; // Error
}
// 对象的属性名不能重复
{ foo: true, foo: false } // Error
// 不能对变量执行delete操作
var foo = "test";
function test(){}
delete foo; // Error
3.变量范围:以下代码输出的值为
function f() {
var a = b = 5;
}
f();
console.log(b); //5
b其实就是个全部变量,b=5,而var a 只能在函数作用域内访问,代码就相当于
b = 5;
var a = b;
4.js中的几种遍历方式
参考:https://www.cnblogs.com/Jacky-MYD/p/8984176.html
-
普通for循环,有下标,支持break
var arr = [1,2,3,4,5]; for(let i=0;i<arr.length;i++){ console.log(i); if(i === 1){ break; } }
改进版:使用临时变量,将长度缓存起来,避免重复获取数组长度,当数组较大时优化效果才会比较明显。这种方法基本上是所有循环遍历方法中性能最高的一种
var arr = [1,2,3,4,5]; for(let i=0,len=arr.length;i<len;i++){ console.log(i); }
-
for…of循环,写法简洁,也支持break跳出循环,性能比fori还是差点,不过比for in强
var arr = [1,2,3,4,5]; for(let item of arr){ console.log(item); if(item === 1){ break; } }
-
forEach循环,Array型自带的,无法使用break终止遍历,并且使用return false也不行
var arr = [1,2,3,4,5]; arr.forEach((item, index, arr) => { // item为arr的元素,index为下标,arr原数组 console.log(item); console.log(index); console.log(arr); if(item === 1){ return false; } });
-
some循环,类似于forEach,区别在于它满足了某一条件后会终止循环并返回true
some() 方法会依次执行数组的每个元素:
- 如果有一个元素满足条件,则表达式返回true , 剩余的元素不会再执行检测。
- 如果没有满足条件的元素,则返回false。
var arr = [1,2,3,4,5]; let b = arr.some((item, index, arr) => { // item为arr的元素,index为下标,arr原数组 if(item === 2){ return item // 虽然返回了item,但是b的变量仍然是布尔值 } console.log(item); console.log(index); }); console.log(b);// true
-
every循环,用于检测数组所有元素是否都符合指定条件
- 如果数组中检测到有一个元素不满足,则整个表达式返回 false ,且剩余的元素不会再进行检测
- 如果所有元素都满足条件,则返回 true
var arr = [1,2,3,4,5]; let b = arr.every((item, index, arr) => { // item为数组中的元素,index为下标,arr为目标数组 return item > 0; // true }) console.log(b) // true
-
filter,筛选过滤,通过遍历并过滤每一个数组中的元素,返回符合条件的数据,组成一个新的数组
var arr = [1,2,3,4,5]; let numbers = arr.filter(item => { // item为数组当前的元素 return item > 1; }); console.log(numbers); // [2, 3, 4, 5]
-
map() 方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。
map() 方法按照原始数组元素顺序依次处理元素。
var arr = [1,2,3,4,5]; let numbers = arr.map(item => { // item为数组的元素 return item * 2; // 返回一个处理过的新数组[2, 4, 6, 8, 10] }); console.log(numbers);// [2, 4, 6, 8, 10]
-
for in可以用来遍历数组和对象,注意:for…in循环会把某个类型的原型(prototype)中方法与属性给遍历出来,所以这可能会导致代码中出现意外的错误
例如:
Array.prototype.myfunction = function () { console.log('this is a function...') }; var arr = [1,2,3,4,5]; for(var i in arr){ console.log(arr[i]); // 1 2 3 4 5 f(){ ...} }
在第一段代码中,对js的数组Array添加了一个新的方法,为了在某些时候能够方便使用。此时,如果用for-in循环出数组arr的内容,会发现在本来想要得到的0,1,2,3的后面会多一个function(){alert(“this is a function…”);},这应该不是我们想要得到的结果
另外:
Array.prototype.myfunction = function () { console.log('this is a function...') }; var arr = [{name:'xm',age:20},{name:'aa',age:21}]; for(var i in arr){ console.log(arr[i].name); }
上面这段代码由于给Array原型上增添了方法,会发现遍历后输出
xm aa ""
,多了一个空字符串,这显然数据有问题。所以慎用for…in,因为它会把原型上的一些内容给遍历出来
5.Object与Function的关系
参考:https://blog.csdn.net/w599020860/article/details/101148822
看下面代码:
var F = function(){};
Object.prototype.a = function(){ console.log(a) };
Function.prototype.b = function(){ console.log(b) };
var f = new F();
f.a();
f.b();
问 f.a()与 f.b()能否执行?
实际a是可以执行的,b却不能执行。因为,f已经是创建的对象了,由于new的作用,会改变this的指向问题
//一个new的过程
var obj = {};
obj.__proto__=F.prototype;
F.call(obj,...arg);
结合下面这张图分析
f.__proto__ == F.prototype
//结果: true
F.prototype.__proto__ == Object.prototype
//结果: true
由于this的指向改变,会导致f._proto_ = Object.prototype;当去调用a方法时,会去当前对象f上去找,f上没有,就去原型对象上找,由于原型对象已经被this指向为Object.prototype。所以能找到a方法,b方法找不到
实际上b方法确实是存在的,只不过不能通过对象访问,通过函数实例能访问到
var F = function(){};
Object.prototype.a = function(){ console.log('a') };
Function.prototype.b = function(){ console.log('b') };
F.b() // b
总结:
- 在var f = new F()时,会创建新的对象,生成新的a方法,在f实例对a,b方法的查找上,原型链里不会去Function.prototype里,而是在Object.prototype里
- b方法实际一直都存在于构造函数的原型里,因为F是一个构造函数,所以F.b()是可以执行的
- 实际上在此到题目的上下文中,创建的所有的Object.prototype都存在a方法,它对应的实例也都存在a方法,而b方法则是所有的Function.prototype都存在b方法,它对应的实例也都存在b方法