web 前端面试题整理(JS 篇)
####1. ajax, 跨域, jsonp
参考: 《JavaScript》高级程序设计第 21 章: Ajax 和 Comet http://www.cnblogs.com/haoyijing/p/5778155.html
jQuery 中 Ajax 操作
####2. apply 和 call 的用法和区别:
用法:
都能继承另一个对象的方法和属性, 区别在于参数列表不一样
区别:
Function.apply(obj, args) args 是一个数组, 作为参数传给 Function
Function.call(obj, arg1, arg2,...) arg * 是参数列表
apply 一个妙用: 可以将一个数组默认的转化为一个参数列表
举个栗子: 有一个数组 arr 要 push 进一个新的数组中去, 如果用 call 的话需要把数组中的元素一个个取出来再 push, 而用 apply 只有 Array.prototype.push.apply(this, arr)
####3. bind 函数的兼容性
用法:
bind()函数会创建一个新函数, 为绑定函数. 当调用这个绑定函数时, 绑定函数会以创建它时传入 bind 方法的第一个参数作为 this, 传入 bind 方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数.
一个绑定函数也能使用 new 操作符创建对象: 这种行为就像把原函数当成构造器. 提供的 this 值被忽略, 同时调用时的参数被提供给模拟函数.
####4. 解释下事件代理
事件委托利用了事件冒泡, 只指定一个事件处理程序, 就可以管理某一类型的所有事件.
例: html 部分: 要点击 li 弹出其 idhtml 代码
- <ul id="list">
- <li id="li-1">Li 2</li>
- <li id="li-2">Li 3</li>
- <li id="li-3">Li 4</li>
- <li id="li-4">Li 5</li>
- <li id="li-5">Li 6</li>
- <li id="li-6">Li 7</li>
- </ul>
javascript 代码
- //js 部分
- document.getElementById("list").addHandler("click", function(e){
- var e = e || window.event;
- var target = e.target || e.srcElement;
- if(target.nodeName.toUpperCase == "LI"){
- console.log("List item", e,target.id, "was clicked!");
- }
- });
- ####5. 解释下 js 中 this 是怎么工作的?
this 在 JavaScript 中主要由以下五种使用场景.
作为函数调用, this 绑定全局对象, 浏览器环境全局对象为 window .
内部函数内部函数的 this 也绑定全局对象, 应该绑定到其外层函数对应的对象上, 这是 JavaScript 的缺陷, 用 that 替换.
作为构造函数使用, this 绑定到新创建的对象.
作为对象方法使用, this 绑定到该对象.
使用 apply 或 call 调用 this 将会被显式设置为函数调用的第一个参数.
6. 继承
参考: js 怎么实现继承? http://www.cnblogs.com/haoyijing/p/5760586.htm
####7. AMD vs. CommonJS?
AMD 是依赖提前加载
CMD 是依赖延时加载
####8. 什么是哈希表?
哈希表 (Hash table, 也叫散列表), 是根据关键码值(Key value) 而直接进行访问的数据结构. 也就是说, 它通过把关键码值映射到表中一个位置来访问记录, 以加快查找的速度. 这个映射函数叫做散列函数, 存放记录的数组叫做散列表.
使用哈希查找有两个步骤:
使用哈希函数将被查找的键转换为数组的索引. 在理想的情况下, 不同的键会被转换为不同的索引值, 但是在有些情况下我们需要处理多个键被哈希到同一个索引值的情况.
所以哈希查找的第二个步骤就是处理冲突. 处理哈希碰撞冲突. 有很多处理哈希碰撞冲突的方法, 比如拉链法和线性探测法.
元素特征转变为数组下标的方法就是散列法. 散列法当然不止一种, 下面列出三种比较常用的:
1, 除法散列法
最直观的一种, 上图使用的就是这种散列法, 公式: index = value % 16
学过汇编的都知道, 求模数其实是通过一个除法运算得到的, 所以叫 "除法散列法".
2, 平方散列法
求 index 是非常频繁的操作, 而乘法的运算要比除法来得省时(对现在的 CPU 来说, 估计我们感觉不出来), 所以我们考虑把除法换成乘法和一个位移操作. 公式: index = (value value)>> 28 (右移, 除以 2^28. 记法: 左移变大, 是乘. 右移变小, 是除.)
如果数值分配比较均匀的话这种方法能得到不错的结果, 但我上面画的那个图的各个元素的值算出来的 index 都是 0-- 非常失败. 也许你还有个问题, value 如果很大, value value 不会溢出吗? 答案是会的, 但我们这个乘法不关心溢出, 因为我们根本不是为了获取相乘结果, 而是为了获取 index.
3, 斐波那契 (Fibonacci) 散列法
解决冲突的方法:
拉链法
将大小为 M 的数组的每一个元素指向一个条链表, 链表中的每一个节点都存储散列值为该索引的键值对, 这就是拉链法.
对采用拉链法的哈希实现的查找分为两步, 首先是根据散列值找到等一应的链表, 然后沿着链表顺序找到相应的键.
线性探测法:
使用数组中的空位解决碰撞冲突
参考: 浅谈算法和数据结构: 十一 哈希表 哈希表的工作原理 http://www.cnblogs.com/ghj1976/p/3689917.html
####9. 什么是闭包? 闭包有什么作用?
闭包是指有权访问另一个函数作用域中的变量的函数. 创建闭包常见方式, 就是在一个函数内部创建另一个函数.
作用:
匿名自执行函数 (function (){ ... })(); 创建了一个匿名的函数, 并立即执行它, 由于外部无法引用它内部的变量, 因此在执行完后很快就会被释放, 关键是这种机制不会污染全局对象.
缓存, 可保留函数内部的值
实现封装
实现模板
参考: js 闭包的用途
####10. 伪数组:
什么是伪数组:
伪数组是能通过 Array.prototype.slice 转换为真正的数组的带有 length 属性的对象
比如 arguments 对象, 还有像调用 getElementsByTagName,document.childNodes 之类的, 它们都返回 NodeList 对象都属于伪数组
我们可以通过 Array.prototype.slice.call(fakeArray)将伪数组转变为真正的 Array 对象: 返回新数组而不会修改原数组
参考: 伪数组
11. undefined 和 null 的区别, 还有 undeclared:
null 表示没有对象, 即此处不该有此值. 典型用法:
(1) 作为函数的参数, 表示该函数的参数不是对象.
(2) 作为对象原型链的终点.
( 3 ) null 可以作为空指针. 只要意在保存对象的值还没有真正保存对象, 就应该明确地让该对象保存 null 值.
undefined 表示缺少值, 即此处应该有值, 但还未定义.
(1)变量被声明了, 但没有赋值时, 就等于 undefined.
(2) 调用函数时, 应该提供的参数没有提供, 该参数等于 undefined.
(3)对象没有赋值的属性, 该属性的值为 undefined.
(4)函数没有返回值时, 默认返回 undefined.
undeclared 即为被污染的命名, 访问没有被声明的变量, 则会抛出异常, 终止执行. 即 undeclared 是一种语法错误
参考: undefined 与 null 的区别
####12. 事件冒泡机制:
从目标元素开始, 往顶层元素传播. 途中如果有节点绑定了相应的事件处理函数, 这些函数都会被一次触发. 如果想阻止事件起泡, 可以使用 e.stopPropagation()(Firefox)或者 e.cancelBubble=true(IE)来组织事件的冒泡传播.
####13. 解释下为什么接下来这段代码不是 IIFE(立即调用的函数表达式):function foo(){ }();?
而函数定义 (语句以 function 关键字开始) 是不能被立即执行的, 这无疑会导致语法的错误(SyntaxError). 当函数定义代码段包裹在括号内, 使解析器可以将之识别为函数表达式, 然后调用. IIFE: (function foo(){})()
区分 (function(){})(); 和 (function(){}()); 其实两者实现效果一样.
函数字面量: 首先声明一个函数对象, 然后执行它.(function () { alert(1); })();
优先表达式: 由于 Javascript 执行表达式是从圆括号里面到外面, 所以可以用圆括号强制执行声明的函数.(function () { alert(2); }());
####14. "attribute" 和 "property" 的区别是什么?
DOM 元素的 attribute 和 property 两者是不同的东西. attribute 翻译为 "特性",property 翻译为 "属性".
attribute 是一个特性节点, 每个 DOM 元素都有一个对应的 attributes 属性来存放所有的 attribute 节点, attributes 是一个类数组的容器, 说得准确点就是 NameNodeMap, 不继承于 Array.prototype, 不能直接调用 Array 的方法. attributes 的每个数字索引以名值对 (name="value") 的形式存放了一个 attribute 节点.<div class="box" id="box" gameid="880">hello</div>
property 就是一个属性, 如果把 DOM 元素看成是一个普通的 Object 对象, 那么 property 就是一个以名值对 (name="value") 的形式存放在 Object 中的属性. 要添加和删除 property 和普通的对象类似.
很多 attribute 节点还有一个相对应的 property 属性, 比如上面的 div 元素的 id 和 class 既是 attribute, 也有对应的 property, 不管使用哪种方法都可以访问和修改.
总之, attribute 节点都是在 HTML 代码中可见的, 而 property 只是一个普通的名值对属性.
####15. 请指出 document load 和 document ready 两个事件的区别.
document.ready 和 onload 的区别 --JavaScript 文档加载完成事件. 页面加载完成有两种事件:
一是 ready, 表示文档结构已经加载完成(不包含图片等非文字媒体文件)
二是 onload, 指示页面包含图片等文件在内的所有元素都加载完成.
jQuery 中 $(function(){/do something/}); 他的作用或者意义就是: 在 DOM 加载完成后就可以可以对 DOM 进行操作. 一般情况先一个页面响应加载的顺序是, 域名解析 - 加载 html - 加载 js 和 CSS - 加载图片等其他信息.
####16. 什么是 use strict? 其好处坏处分别是什么?
在所有的函数 (或者所有最外层函数) 的开始处加入 "use strict"; 指令启动严格模式.
"严格模式" 有两种调用方法
1)将 "use strict" 放在脚本文件的第一行, 则整个脚本都将以 "严格模式" 运行. 如果这行语句不在第一行, 则无效, 整个脚本以 "正常模式" 运行. 如果不同模式的代码文件合并成一个文件, 这一点需要特别注意.
2)将整个脚本文件放在一个立即执行的匿名函数之中.
好处
消除 Javascript 语法的一些不合理, 不严谨之处, 减少一些怪异行为;
消除代码运行的一些不安全之处, 保证代码运行的安全;
提高编译器效率, 增加运行速度;
为未来新版本的 Javascript 做好铺垫.
坏处
同样的代码, 在 "严格模式" 中, 可能会有不一样的运行结果; 一些在 "正常模式" 下可以运行的语句, 在 "严格模式" 下将不能运行
####17. 浏览器端的 js 包括哪几个部分?
核心( ECMAScript) , 文档对象模型(DOM), 浏览器对象模型(BOM)
####18. DOM 包括哪些对象?
DOM 是针对 HTML 和 XML 文档的一个 API(应用程序编程接口). DOM 描绘了一个层次化的节点树, 允许开发人员添加, 移除和修改页面的某一部分.
常用的 DOM 方法:
- getElementById(id)
- getElementsByTagName()
- appendChild(node)
- removeChild(node)
- replaceChild()
- insertChild()
- createElement()
- createTextNode()
- getAttribute()
- setAttribute()
常用的 DOM 属性
innerHTML 节点 (元素) 的文本值
parentNode 节点 (元素) 的父节点
childNodes
attributes 节点 (元素) 的属性节点
参考: HTML DOM 方法 http://www.w3school.com.cn/htmldom/dom_methods.asp
####19. js 有哪些基本类型?
Undefined, Null, Boolean, Number, String
Object 是复杂数据类型, 其本质是由一组无序的名值对组成的.
####20. 基本类型与引用类型有什么区别?
基本类型如上题所示. 引用类型则有: Object, Array, Date, RegExp, Function
存储
基本类型值在内存中占据固定大小的空间, 因此被保存在栈内存中
引用类型的值是对象, 保存在堆内存中. 包含引用类型的变量实际上包含的并不是对象本身, 而是一个指向改对象的指针
复制
从一个变量向另一个变量复制基本类型的值, 会创建这个值的一个副本
从一个变量向另一个变量复制引用类型的值, 复制的其实是指针, 因此两个变量最终都指向同一个对象
检测类型
确定一个值是哪种基本类型可以用 typeof 操作符,
而确定一个值是哪种引用类型可以使用 instanceof 操作符
####21. 关于 js 的垃圾收集例程
js 是一门具有自动垃圾回收机制的编程语言, 开发人员不必关心内存分配和回收问题
离开作用域的值将被自动标记为可以回收, 因此将在垃圾收集期间被删除
"标记清除" 是目前主流的垃圾收集算法, 这种算法的思路是给当前不使用的值加上标记, 然后再回收其内存
另一种垃圾收集算法是 "引用计数", 这种算法的思想是跟踪记录所有值被引用的次数. js 引擎目前都不再使用这种算法, 但在 IE 中访问非原生 JS 对象 (如 DOM 元素) 时, 这种算法仍然可能会导致问题
当代码中存在循环引用现象时, "引用计数" 算法就会导致问题
解除变量的引用不仅有助于消除循环引用现象, 而且对垃圾收集也有好处. 为了确保有效地回收内存, 应该及时解除不再使用的全局对象, 全局对象属性以及循环引用变量的引用
####22. ES5 中, 除了函数, 什么能够产生作用域?
try-catch 和 with 延长作用域. 因为他们都会创建一个新的变量对象.
这两个语句都会在作用域链的前端添加一个变量对象. 对 with 语句来说, 会将指定的对象添加到作用域链中. 对 catch 语句来说, 会创建一个新的变量对象, 其中包含的是被抛出的错误对象的声明.
当 try 代码块中发生错误时, 执行过程会跳转到 catch 语句, 然后把异常对象推入一个可变对象并置于作用域的头部. 在 catch 代码块内部, 函数的所有局部变量将会被放在第二个作用域链对象中. 请注意, 一旦 catch 语句执行完毕, 作用域链机会返回到之前的状态. try-catch 语句在代码调试和异常处理中非常有用, 因此不建议完全避免. 你可以通过优化代码来减少 catch 语句对性能的影响. 一个很好的模式是将错误委托给一个函数处理
with(object) {statement}. 它的意思是把 object 添加到作用域链的顶端 javascript 代码
- function buildUrl(){
- var qs = "?debug=true";
- //with 接收 location 对象, 因此其变量对象中就包含了 location 对象的所有属性和方法, 而这个变量对象被添加到了作用域链的前端
- with(location){
- // 这里的 href 其实是 location.href. 创建了一个名为 url 的变量, 就成了函数执行环境的一部分
- var url = href + qs;
- }
- return url;
- }
参考: js try,catch,finally 语句还有 with 语句 JavaScript 开发进阶: 理解 JavaScript 作用域和作用域链
####23. js 有几种函数调用方式?
方法调用模型 var obj = { func : function(){};} obj.func()
函数调用模式 var func = function(){} func();
构造器调用模式
apply/ call 调用模式
####24. 描述事件模型? IE 的事件模型是怎样的? 事件代理是什么? 事件代理中怎么定位实际事件产生的目标?
捕获 ->处于目标 ->冒泡, IE 应该是只有冒泡没有捕获.
事件代理就是在父元素上绑定事件来处理, 通过 event 对象的 target 来定位.
####25. js 动画有哪些实现方法?
用定时器 setTimeout 和 setInterval
####26. 还有什么实现动画的方法?
js 动画:
使用定时器 setTimeout 和 setInterval
CSS : transition , animation
transition 包含 4 种属性: transition-delaytransition-durationtransition-propertytransition-timing-function, 对应动画的 4 种属性: 延迟, 持续时间, 对应 css 属性和缓动函数,
transform 包含 7 种属性: animation-nameanimation-durationanimation-timing-functionanimation-delayanimation-directionanimation-iteration-countanimation-fill-modeanimation-play-state, 它们可以定义动画名称, 持续时间, 缓动函数, 动画延迟, 动画方向, 重复次数, 填充模式.
HTML5 动画
- canvas
- svg
- webgl
参考: 前端动画效果实现的简单比较 https://segmentfault.com/a/1190000000594926
####27. 面向对象有哪几个特点?
封装, 继承, 多态
- ####28. 如何判断属性来自自身对象还是原型链?
- hasOwnPrototype
- ####29. ES6 新特性
1) 箭头操作符 inputs=>outputs: 操作符左边是输入的参数, 而右边则是进行的操作以及返回的值
2) 支持类, 引入了 class 关键字. ES6 提供的类实际上就是 JS 原型模式的包装
3) 增强的对象字面量.
1. 可以在对象字面量中定义原型 proto: xxx // 设置其原型为 xxx, 相当于继承 xxx
2. 定义方法可以不用 function 关键字
3. 直接调用父类方法
4) 字符串模板: ES6 中允许使用反引号 ` 来创建字符串, 此种方法创建的字符串里面可以包含由美元符号加花括号包裹的变量 ${vraible}.
5) 自动解析数组或对象中的值. 比如若一个函数要返回多个值, 常规的做法是返回一个对象, 将每个值做为这个对象的属性返回. 但在 ES6 中, 利用解构这一特性, 可以直接返回一个数组, 然后数组中的值会自动被解析到对应接收该值的变量中.
6) 默认参数值: 现在可以在定义函数的时候指定参数的默认值了, 而不用像以前那样通过逻辑或操作符来达到目的了.
7) 不定参数是在函数中使用命名参数同时接收不定数量的未命名参数. 在以前的 JavaScript 代码中我们可以通过 arguments 变量来达到这一目的. 不定参数的格式是三个句点后跟代表所有不定参数的变量名. 比如下面这个例子中,...x 代表了所有传入 add 函数的参数.
8) 拓展参数则是另一种形式的语法糖, 它允许传递数组或者类数组直接做为函数的参数而不用通过 apply.
9) let 和 const 关键字: 可以把 let 看成 var, 只是它定义的变量被限定在了特定范围内才能使用, 而离开这个范围则无效. const 则很直观, 用来定义常量, 即无法被更改值的变量.
10) for of 值遍历 每次循环它提供的不是序号而是值.
11) iterator, generator
12) 模块
13) Map, Set, WeakMap, WeakSet
14) Proxy 可以监听对象身上发生了什么事情, 并在这些事情发生后执行一些相应的操作. 一下子让我们对一个对象有了很强的追踪能力, 同时在数据绑定方面也很有用处.
15) Symbols Symbol 通过调用 symbol 函数产生, 它接收一个可选的名字参数, 该函数返回的 symbol 是唯一的. 之后就可以用这个返回值做为对象的键了. Symbol 还可以用来创建私有属性, 外部无法直接访问由 symbol 做为键的属性值.
16) Math, Number, String, Object 的新 API
17) Promises 是处理异步操作的一种模式
参考: ES6 新特性概览
####30. 如何获取某个 DOM 节点, 节点遍历方式
获取节点: getElementById() getElementsByTagName()
节点遍历: 先序遍历 DOM 树的 5 种方法 http://www.cnblogs.com/tracylin/p/5220867.html
####31. 用 LESS 如何给某些属性加浏览器前缀?
可以自定义一个函数 css 代码
- .border-radius(@values) {
- -webkit-border-radius: @values;
- -moz-border-radius: @values;
- border-radius: @values;
- }
- div {
- .border-radius(10px);
- }
- ####32. js 异步模式如何实现?
参考: JavaScript 异步编程的 Promise 模式 http://www.cnblogs.com/haoyijing/p/5737951.html
33. 图片预加载的实现
使用 jQuery 图片预加载插件 Lazy Load
1. 加载 jQuery, 与 jquery.lazyload.js
2. 设置图片的占位符为 data-original, 给图片一个特别的标签, 比如 class=".lazy"
3. 然后延迟加载: $('img.lazy').lazyload(); 这个函数可以选择一些参数:
3.1. 图片预先加载距离: threshold, 通过设置这个值, 在图片未出现在可视区域的顶部距离这个值时加载.
3.2. 事件绑定加载的方式: event
3.3. 图片限定在某个容器内: container
使用 js 实现图片加载: 就是 new 一个图片对象, 绑定 onload 函数, 赋值 url
用 CSS 实现图片的预加载
写一个 CSS 样式设置一批背景图片, 然后将其隐藏
改进: 使用 js 来推迟预加载时间, 防止与页面其他内容一起加载
用 Ajax 实现预加载
其实就是通过 ajax 请求请求图片地址. 还可以用这种方式加载 css,js 文件等
####34. 如果在同一个元素上绑定了两个 click 事件, 一个在捕获阶段执行, 一个在冒泡阶段执行. 那么当触发 click 条件时, 会执行几个事件? 执行顺序是什么? 我在回答这个题的时候说是两个事件, 先执行捕获的后执行冒泡的. 其实是不对的.
绑定在目标元素上的事件是按照绑定的顺序执行的!!!!
即: 绑定在被点击元素的事件是按照代码顺序发生, 其他元素通过冒泡或者捕获 "感知" 的事件, 按照 W3C 的标准, 先发生捕获事件, 后发生冒泡事件. 所有事件的顺序是: 其他元素捕获阶段事件 -> 本元素代码顺序事件 -> 其他元素冒泡阶段事件 .
参考: JavaScript - 父子 dom 同时绑定两个点击事件, 一个用捕获, 一个用冒泡时执行顺序
####35. js 中怎么实现块级作用域?
使用匿名函数, (立即执行函数)
(function(){...})()
使用 es6
块级作用域引入了两种新的声明形式, 可以用它们定义一个只存在于某个语句块中的变量或常量. 这两种新的声明关键字为:
let: 语法上非常类似于 var, 但定义的变量只存在于当前的语句块中
const: 和 let 类似, 但声明的是一个只读的常量
使用 let 代替 var 可以更容易的定义一个只在某个语句块中存在的局部变量, 而不用担心它和函数体中其他部分的同名变量有冲突. 在 let 语句内部用 var 声明的变量和在 let 语句外部用 var 声明的变量没什么差别, 它们都拥有函数作用域, 而不是块级作用域.
####36. 构造函数里定义 function 和使用 prototype.func 的区别?
直接调用 function, 每一个类的实例都会拷贝这个函数, 弊端就是浪费内存(如上).prototype 方式定义的方式, 函数不会拷贝到每一个实例中, 所有的实例共享 prototype 中的定义, 节省了内存.
但是如果 prototype 的属性是对象的话, 所有实例也会共享一个对象(这里问的是函数应该不会出现这个情况), 如果其中一个实例改变了对象的值, 则所有实例的值都会被改变. 同理的话, 如果使用 prototype 调用的函数, 一旦改变, 所有实例的方法都会改变.-- 不可以对实例使用 prototype 属性, 只能对类和函数用.
####37. js 实现对象的深克隆
因为 js 中数据类型分为基本数据类型 (number, string, boolean, null, undefined) 和引用类型值(对象, 数组, 函数). 这两类对象在复制克隆的时候是有很大区别的. 原始类型存储的是对象的实际数据, 而对象类型存储的是对象的引用地址(对象的实际内容单独存放, 为了减少数据开销通常放在内存中). 此外, 对象的原型也是引用对象, 它把原型的属性和方法放在内存中, 通过原型链的方式来指向这个内存地址.
于是克隆也会分为两类:
浅度克隆:
原始类型为值传递, 对象类型仍为引用传递
深度克隆:
所有元素或属性均完全复制, 与原对象完全脱离, 也就是说所有对于新对象的修改都不会反映到原对象中
深度克隆实现: javascript 代码
- function clone(obj){
- if(typeof(obj)== 'object'){
- var result = obj instanceof Array ? [] : {};
- for(var i in obj){
- var attr = obj[i];
- result[i] = arguments.callee(attr);
- }
- return result;
- } else {
- return obj;
- }
- };
来源: http://www.qdfuns.com/article/40893/9ddac6bdbcd5e02e9a6dad08441ff0a8.html