【必看】JavaScript面试题集合(三)

本文是JavaScript面试题的第三部分,涵盖了JS事件模型,包括原始事件模型、DOM2事件模型、IE事件模型的比较;解释了"use strict"的作用和优势;讨论了变量作用域的问题;列举了JavaScript中不同类型的遍历方式,包括for循环、for...of、forEach、some、every、filter和map;最后探讨了Object与Function的关系及其在原型链中的表现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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在更严格的条件下运行。

设立"严格模式"的优点:

  1. 消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为;

  2. 消除代码运行的一些不安全之处,保证代码运行的安全;

  3. 提高编译器效率,增加运行速度;

  4. 为未来新版本的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方法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值