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 事件循环”