AI智能
改变未来

JavaScript面试题三千问—下篇


31.IE 的事件处理和 W3C 的事件处理有哪些区别?

绑定事件:

  • W3C:targetEl.addEventLIstener(‘click’,handler,false);
  • IE:targetEl.attachEvent(‘onclick’,handler);

删除事件:

  • W3C:targetEl.removeEventListener(‘click’,handler,false);
  • IE:targetEl.detachEvent(‘onclick’,handler);

事件对象:

  • W3C:var e = arguments.callee.caller.arguments[0];
  • IE:window.evnet;

事件目标:

  • W3C:e.target;
  • IE:window.evnet.srcElement;

阻止事件默认行为:

  • W3C:e.preventDefault();
  • IE:window.evnet.returnValue = false;

阻止事件传播:

  • W3C:e.stopPropagation();
  • IE:window.evnet.cancelBubble = true;

32.事件的 target 与 currentTarget 的区别

  • target 只会出现在事件流的目标阶段
  • currentTarget 可能出现在事件流的任何阶段
  • 当事件流处在目标阶段时,二者的指向相同
  • 当事件流处于捕获或冒泡阶段时,currentTarget 指向当前时间活动的对象(一般为父级)

33.如何派发事件(dispatchEvent)?

  • W3C:使用 dispatchEvent 方法
  • IE:使用 fireEvent 方法
var fireEvent = function(element, event){if (document.createEventObject){var mockEvent = document.createEventObject();return element.fireEvent(\'on\' + event, mockEvent)}else{var mockEvent = document.createEvent(\'HTMLEvents\');mockEvent.initEvent(event, true, true);return !element.dispatchEvent(mockEvent);}}

34.什么是函数节流?介绍一下应用场景和原理

  • 函数节流(throttle)是指阻止一个函数在很短时间间隔内连续调用。 只有当上一次函数执行后达到规定的时间间隔,才能进行下一次调用。 但要保证一个累计最小调用间隔(否则拖拽类的节流都将无连续效果)
  • 函数节流用于 onresize, onscroll 等短时间内会多次触发的事件
  • 函数节流的原理:使用定时器做时间节流。 当触发一个事件时,先用 setTimout 让这个事件延迟一小段时间再执行。 如果在这个时间间隔内又触发了事件,就 clearTimeout 原来的定时器, 再 setTimeout 一个新的定时器重复以上流程。

函数节流简单实现:

function throttle(method, context) {clearTimeout(methor.tId);method.tId = setTimeout(function(){method.call(context);}, 100); // 两次调用至少间隔 100ms}// 调用window.onresize = function(){throttle(myFunc, window);}

35.区分什么是\”客户区坐标\”、“页面坐标”、“屏幕坐标”

  • 客户区坐标:鼠标指针在可视区中的水平坐标(clientX)和垂直坐标(clientY)
    相对于浏览器左上角为原点
  • 页面坐标:鼠标指针在页面布局中的水平坐标(pageX)和垂直坐标(pageY)
    相对于 Document 对象即文档窗口的左上角为原点
  • 屏幕坐标:设备物理屏幕的水平坐标(screenX)和垂直坐标(screenY)

36.如何获得一个 DOM 元素的绝对位置

  • elem.offsetLeft:返回元素相对于其定位父级左侧的距离
  • elem.offsetTop:返回元素相对于其定位父级顶部的距离
  • elem.getBoundingClientRect():返回一个 DOMRect 对象,包含一组描述边框的只读属性,单位为像素(px)

37.分析[‘1’,‘2’,‘3’].map(parseInt) 答案是多少

答案:[1,NaN,NaN]
parseInt(string,radix)第二个参数 radix 表示进制。省略 radix 或 radix = 0,则数字将以十进制解析
map每次为 parseInt 传3个参数(elem,index,array),其中 index 为数组索引
因此,map 遍历[‘1’,‘2’,‘3’],相应 parseInt 接收参数如下

parseInt(\'1\', 0);  // 1parseInt(\'2\', 1);  // NaNparseInt(\'3\', 2);  // NaN

所以,parseInt 参数 radix 不合法,导致返回值为 NaN

38.new 操作符的作用

  • 创建实例对象,this 变量引用该对象,同时还继承了构造函数的原型
  • 属性和方法被加入到 this 引用的对象中
  • 新创建的对象由 this 所引用,并且最后隐式的返回 this

39.解释下面代码的意思

[].forEach.call($$(\"*\"), function(el){el.style.outline = \"1px solid #\" + (~~(Math.random()*(1<<24))).toString(16);})

解释:获取页面所有的元素,遍历这些元素,为他们添加 1 像素随机颜色的轮廓(outline)

  • $$(sel) // 次函数被许多现代浏览器命令行支持,等价于 document.querySelectorAll(sel)
  • [].forEach.call(NodeLists) // 使用 call 函数将数组遍历函数 forEach 应到节点元素列表
  • el.style.outline = “1px solid #333” // 样式 outline 位于盒模型之外,不影响元素布局位置
  • Math.random() * (1 << 24) // 表示一个位于 0 到 16777216 之间的随机浮点数
  • ~~Math.random() * (1 << 24) // 作用相当于 parseInt 取整
  • (~~(Math.random()*(1<<24))).toString(16) // 转换为一个十六进制

40.JavaScript 实现异步编程的方法

  • 回调函数
  • 事件监听
  • 发布/订阅
  • Promises 对象
  • Async 函数 [ES7]

41.web 开发中会话跟踪的方法有哪些?

  • cookie
  • session
  • url 重写
  • 隐藏 input
  • ip 地址

42.什么是闭包(closure),为什么要使用闭包?

闭包是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量,利用闭包可以突破作用域链

闭包的特性:

  • 函数内在嵌套函数
  • 内部函数可以引用外层的参数和变量
  • 参数和变量不会被垃圾回收机制回收

43.JavaScript 中的\”use strict\”是什么意思?

use strict 是一种 ECMAscript 5 添加的(严格)运行模式,这种模式使得 JavaScript 在更严格的条件下运行,使 JS 编码更加规范化,消除 JavaScript 语法的一些不合理、不严谨之处,减少一些怪异行为

44.如何判断一个对象是否属于某个类?

// 使用instanceof (待完善)if(a instanceof Person){alert(\'yes\');}

45.js 延迟加载的方式有哪些?

defer 和 async、动态创建 DOM 方式、按需异步载入 js

46.defer 和 async

defer 并行加载 js 文件,会按照页面上 script 标签的顺序执行
async 并行加载 js 文件,下载完成立即执行,不会按照页面上的 script 标签的顺序执行

47.Ajax 是什么? 如何创建一个 Ajax?

ajax 的全称为:Asynchronous Javascript And XML
异步传输+js+XML
所谓异步,就是向服务器发送请求的时候,我们不必等待结果,而是可以同时做其他的事情,等到有了结果他会根据设定进行后续操作,与此同时,页面是不会发生整页刷新的,提高了用户体验

  • 创建 XMLHttpRequest 对象,也就是创建一个异步调用对象
  • 建一个新的 HTTP 请求,并指定该 HTTP 请求的方法、URL 和验证信息
  • 设置响应 HTTP 请求状态变化的函数
  • 发送 HTTP 请求
  • 获取异步调用返回的数据
  • 用 JavaScript 和 DOM 实现局部刷新

48.同步和异步的区别

  • 同步:浏览器访问服务器请求,用户看得到页面刷新,重新发请求,等请求完毕,页面刷新,新内容出现,用户看到新内容,进行下一步操作
  • 异步:浏览器访问服务器请求,用户正常操作,浏览器后端进行请求。等请求完毕,页面不刷新,新内容也会出现,用户看到新内容

49.document.write 和 innerHTML 的区别

  • document.wirte 只能重绘整个页面
  • innerHTML 可以重绘页面的一部分

50.DOM 操作怎么添加、删除、移动、复制、创建和查找节点?

创建新节点

  • createDocumentFragment() // 创建一个 DOM 片段
  • createElement() // 创建一个具体的元素
  • createTextNode() // 创建一个文本节点

添加、替换、删除、插入

  • appendChild()
  • replaceChild()
  • removeChild()
  • insertBefore() // 在已有的子节点前插入新的子节点

查找

  • getElementsByTagName() // 通过标签名称查找
  • getElementsByName() // 通过元素的 Name 属性查找
  • getElementById() // 通过元素的 id 属性查找

51.哪些操作会造成内存泄漏?

  • 内存泄漏指任何对象在不再拥有或不需要之后仍然存在
  • 垃圾回收器定期扫描对象,并计算引用了每个对象的其他对象的数量。如果一个对象的引用数量为 0,或对该对象的唯一引用是循环的,那么该对象的内存即可回收
  • setTimeout 的第一个参数使用字符串而非函数的话,会引发内存泄漏
  • 闭包、控制台日志、循环(在两个对象彼此引用且彼此保留时,就会产生一个循环)会引起内存泄漏

52.渐进增强和优雅降级

  • 渐进增强:针对低版本浏览器进行构建页面,保证最基本的功能,然后在针对高级浏览器进行效果、交互等改进和追加功能达到更好的用户体验
  • 优雅降级:一开始就构建完整的功能,然后在针对低版本浏览器进行兼容

53.JavaScript 垃圾回收方法

标记清除(mark and sweep)

  • 这是 JavaScript 最常见的垃圾回收方式,当变量进入执行环境的时候,比如函数中声明一个变量,垃圾回收器将其标记为“进入环境”,当变量离开环境的时候(函数执行结束)将其标记为“离开环境”
  • 垃圾回收器会在运行的时候给存储在内存中的所有变量加上标记,然后去掉环境中的变量以及被环境中变量所引用的变量(闭包),在这些完成之后仍存在标记的就是要删除的变量了

引用计数(reference counting)

  • 在低版本 IE 中经常会出现内存泄露,很多时候就是因为其采用引用计数方式进行垃圾回收。引用计数的策略是跟踪记录每个值被使用的次数,当声明了一个 变量并将一个引用类型赋值给该变量的时候这个值的引用次数就加 1,如果该变量的值变成了另外一个,则这个值得引用次数减 1,当这个值的引用次数变为 0 的时 候,说明没有变量在使用,这个值没法被访问了,因此可以将其占用的空间回收,这样垃圾回收器会在运行的时候清理掉引用次数为 0 的值占用的空间

54.介绍几种设计模式

工厂模式:

  • 主要好处就是可以消除对象间的耦合,通过使用工程方法而不是 new 关键字。将所有实例化的代码集中在一个位置防止代码重复
  • 工厂模式解决了重复实例化的问题,但还有一个问题,那就是识别问题,因为根本无法搞清楚他们到底哪个是对象的实例
function createObject(name,age,profession){//集中实例化的函数var obj = new Object();obj.name = name;obj.age = age;obj.profession = profession;obj.move = function () {return this.name + \' at \' + this.age + \' engaged in \' + this.profession;};return obj;}var test1 = createObject(\'trigkit4\',22,\'programmer\');//第一个实例var test2 = createObject(\'mike\',25,\'engineer\');//第二个实例

构造函数模式:

  • 使用构造函数的方法,可以很好地解决上述两个问题,该模式与工厂模式的不同之处在于
  • 构造函数没有显示的创建对象(new Object())
  • 直接将属性和方法赋值给 this 对象
  • 没有 return 语句

55.闭包的作用?

使用闭包主要是为了设计私有变量和方法。闭包的优点是可以避免全局变量的污染,缺点是闭包会常驻内存,增大内存使用量,使用不当很容易会造成内存泄漏。在 js 中,函数即闭包,只有函数才会产生作用域的概念
闭包有三个特性:

  • 函数嵌套函数
  • 函数内部可以引用外部的参数和变量
  • 参数和变量不会被垃圾回收机制回收

56.什么是 JavaScript 的同源策略?

  • 同源策略是客户端脚本的重要的安全度量标准。它最早出自 Netscape Navigator2.0,其目的是防止某个文档或脚本从多个不同源装载。这里的同源策略指的是:协议,域名,端口相同,同源策略是一种安全协议
  • 指一段脚本只能读取来自同一来源的窗口和文档的属性

57.实现一个函数 clone,可以对 JavaScript 中的5种主要的数据类型(Number、String、Object、Array、Boolean)进行复制

function deepClone(obj) {if (!isObject(obj)) {throw new Error(\'obj 不是一个对象!\')}let isArray = Array.isArray(obj)let cloneObj = isArray ? [] : {}for (let key in obj) {cloneObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key]}return cloneObj}

注意:for…in 法不支持拷贝 function、date、reg 和 err

// 代理法function deepClone(obj) {if (!isObject(obj)) {throw new Error(\'obj 不是一个对象!\')}let isArray = Array.isArray(obj)let cloneObj = isArray ? [...obj] : { ...obj }Reflect.ownKeys(cloneObj).forEach(key => {cloneObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key]})return cloneObj}

58.说说严格模式的限制

  • 变量必须先声明在使用
  • 函数的参数不能有同名属性
  • 不能使用 with 语句
  • 不能对只读属性赋值
  • 不能使用前缀 0 表示八进制数
  • 不能删除不可删除的属性
  • 不能删除变量 delete prop,只能删除属性 delete global[prop]
  • eval 不会在他的外层作用域引入变量
  • eval 和 arguments 不能被重新赋值
  • arguments 不会自动反映函数参数的变化
  • 不能使用 arguments.callee
  • 不能使用 arguments.caller
  • 禁止 this 指向全局对象
  • 不能使用 fn.caller 和 fn.arguments 获取函数调用的堆栈
  • 增加了保留字,如:protected、static、interface

59.如何删除一个 cookie?

将时间设为当前时间往前
setDate() 方法用于设置一个月的某一天

var date = new Date();date.setDate(date.getDate() - 1);//真正的删除

expires 的设置

document.cookie = \'user=\'+ encodeURIComponent(\'name\')  + \';expires = \' + new Date(0)

60.编写一个求字符串长度的方法

function GetBytes(str){var len = str.length;var bytes = len;for(var i=0; i<len; i++){if (str.charCodeAt(i) > 255) bytes++;}return bytes;}

61.什么是事件代理?

事件代理(Event Delegation),又称之为事件委托。是 JavaScript 中常用绑定事件的常用技巧。顾名思义,“事件代理”即是把原本需要绑定的事件委托给父元素,让父元素担当事件监听的职务。事件代理的原理是 DOM 元素的事件冒泡。使用事件代理的好处是可以提高性能

62.attribute 和 property 的区别

  • attribute 是 dom 元素在文档中作为 html 标签拥有的属性
  • property 是 dom 元素在 js 中作为对象拥有的属性
  • 对于 html 的标准属性来说,attribute 和 property 是同步的,时会自动更新的
  • 但是对于自定义的属性来说,他们是不同步的

63.页面编码和被请求的资源编码不一致如何处理?

  • 后端响应头设置 charset
  • 前端页面 设置 charset

64.把

<script>

放在

</body>

之前和之后有什么区别?浏览器会如何解析它们?

按照 HTML 标准,在结束后出现

<script>

或任何元素的开始标签,都是解析错误 虽然不符合 HTML 标准,但浏览器会自动容错,使实际效果与写在

</body>

之前没有区别 浏览器的容错机制会忽略

<script>

之前的,视作

<script>

仍在 body 体内。省略

</body>

</html>

闭合标签符合 HTML 标准,服务器可以利用这一标准

65.把

<script>

放在

</head>

中会有什么问题?

在浏览器渲染页面之前,它需要通过解析HTML标记然后构建DOM树。在这个过程中,如果解析器遇到了一个脚本(script),它就会停下来,并且执行这个脚本,然后才会继续解析HTML。如果遇到了一个引用外部资源的脚本(script),它就必须停下来等待这个脚本资源的下载,而这个行为会导致一个或者多个的网络往返,并且会延迟页面的首次渲染时间。

还有一点是需要我们注意的,那就是外部引入的脚本(script)会阻塞浏览器的并行下载,HTTP/1.1规范表明,浏览器在每个主机下并行下载的组件不超过两个(也就是说,浏览器一次只能够同时从同一个服务器加载两个脚本);如果你网站的图片是通过多个服务器提供的,那么按道理来说,你的网站可以一次并行下载多张图片。但是,当我们网站在加载脚本的时候;浏览器不会再启动任何其它的下载,即使这些组件来自不同的服务器。

66.异步加载 JS 的方式有哪些?

  • 设置
    <script>

    属性 async = “async”

  • 动态创建 script DOM:document.createElement(‘script’)
  • XmlHttpRequest 脚本注入
  • 异步加载库 LABjs
  • 模块加载器 Sea.js

67.JavaScript 中,调用函数有哪几种方式?

  • 方法调用模式 Foo.foo(arg1,arg2);
  • 函数调用模式 foo(arg1,arg2);
  • 构造器调用模式 (new Foo())(arg1,arg2);
  • call/apply 调用模式 Foo.foo.call(that,arg1,arg2);
  • bind 调用模式 Foo.foo.bind(that)(arg1,arg2)();

68.简单实现 function.bind 函数

if (!Function.prototype.bind) {Function.prototype.bind = function(that) {var func = this, args = arguments;return function() {return func.apply(that, Array.prototype.slice.call(args, 1));}}}// 只支持 bind 阶段的默认参数:func.bind(that, arg1, arg2)();// 不支持以下调用阶段传入的参数:func.bind(that)(arg1, arg2);

69.列举 JavaScript 数组和对象的原生方法

数组:

  • arr.concat(arr1,arr2,…);
  • arr.join(\”,\”);
  • arr.sort(fn);
  • arr.push(e1,e2,en);
  • arr.shift();
  • unshift(e1,e2,en);
  • arr.revarse();
  • arr.slice(start,end);
  • arr.splice(index,count,e1,e2,en);
  • arr.indexOf(el);
  • arr.includes(el);

对象:

  • object.hasOwnProperty(prop);
  • object.propertyIsEnumerable(prop);
  • onject.valueOf();
  • object.toString();
  • object.toLocaleString();
  • Class.prototype.isPropertyOf(object);

70.Array.slice() 与 Array.splice() 的区别

slice:

  • 读取数组指定的元素,不会对原数组进行修改
  • 语法:arr.slice(start,end);

splice:

  • 操作数组指定的元素,会修改数组,返回被修改后的数组
  • 语法:arr.splice(index,count,[insert Elements]);
  • index 是操作的起始位置
  • count = 0 插入元素,count > 0 删除元素
  • [insert Element] 向数组插入的新元素

71.JavaScript 对象声明周期

  • 当创建一个对象时,JavaScript 会自动为该对象分配适当的内存
  • 垃圾回收器定期扫描对象,并计算引用了该对象的其他对象的数量
  • 如果被引用数量为 0,或唯一引用是循环的,那么该对象的内存即可回收

72.JavaScript 中,1 和 Number(1) 有什么区别

var a = Number(1) // 1var b = new Number(1)  // Number {[[PrimitiveValue]]: 1}typeof (a) // numbertypeof (b) // objecta == b // true
  • var a = 1 是一个常量,而 Number(1) 是一个函数
  • new Number(1) 返回的是一个对象
  • a == b 为 true 是因为在求值过程中,总是会强制转为原始数据类型,例如下面的代码:
typeof 123 // \"number\"typeof new Number(123) // \"object\"123 instanceof Number // false(new Number(123)) instanceof Number // true123 === new Number(123) // false

73.console.log(!!(new Bollean(false))) 输出什么

true

布尔的包装对象 Boolean 的对象实例,对象只有在 null 与 undefined 时,才会认定为布尔的 false 值,布尔包装对象本身是个对象,对象->布尔 都是 true,所以 new Boolean(false)其实是布尔的 true,看下面这段代码:

if(new Boolean(false)){alert(\'true!!\');}

只有使用了 valueOf 后才是真正的转换布尔值,与上面包装对象与原始资料转换说明的相同:

!!(new Boolean(false))  //true(new Boolean(false)).valueOf() //false

74.为什么 JS 是单线程而不是多线程?

  • 单线程是指 JavaScript 在执行的时候,有且只有一个主线程来处理所有的任务
  • 目的是为了实现与浏览器交互
  • 我们设想一下,如果 JavaScript 是多线程的,现在我们在浏览器中同时操作一个 DOM,一个线程要求浏览器在这个 DOM 中添加节点,而另一个线程却要求浏览器删掉这个 DOM 节点,那这个时候浏览器就会很郁闷,他不知道应该以哪个线程为准。所以为了避免此类现象的发生,降低复杂度,JavaScript 选择只用一个主线程来执行代码,以此来保证程序执行的一致性。

75.浏览器中的 Event Loop

  • 主线程运行的时候会生成堆(heap)和栈(stack)
  • js 从上到下解析方法,将其中的同步任务按照执行顺序排列到执行栈中
  • 当程序调用外部的 API 时,比如 ajax、setTimeout 等,会将此类异步任务挂起,继续执行执行栈中的任务,等异步任务返回结果后,再按照执行顺序排列到事件队列中
  • 主线程先将执行栈中的同步任务清空,然后检查事件队列中是否有任务,如果有,就将第一个事件对应的回调推到执行栈中执行,若在执行过程中遇到异步任务,则继续将这个异步任务排列到事件队列中
  • 主线程每次将执行栈清空后,就去事件队列中检查是否有任务,如果有,就每次取出一个推到执行栈中执行,这个过程是循环往复的… …,这个过程被称为“Event Loop 事件循环”
赞(0) 打赏
未经允许不得转载:爱站程序员基地 » JavaScript面试题三千问—下篇