js面试题
本篇文章是小白自己对面试题的一个记录与积累。
如若发下不对或则不完善的地方,望指出我好及时改正。谢谢
持续更新
1.简述同步和异步的区别
javascript是单线程的语言,所谓的单线程,就是从上至下,依次执行,当然这里的依次执行要抛开javascript的预解析机制。
同步就是所有的任务都处在同一队列中,不可以插队,一个任务执行完接着开始执行下一个,相对于浏览器而言,同步的效率过低,一些耗费时间比较长的任务应该用异步来执行。
异步就是将一个任务放入到异步队列中,当这个任务执行完成之后,再从异步队列中提取出来,插队到同步队列中,拿到异步任务的结果,可以提升代码执行的效率,不需要因为一个耗费时长的代码而一直等待。
2.怎么添加、移除、复制、创建、和查找节点
1)创建新节点
createDocumentFragment() // 创建一个DOM片段
createElement() // 创建一个具体的元素
createTextNode() // 创建一个文本节点2)添加、移除、替换、插入
appendChild() // 添加
removeChild() // 移除
replaceChild() // 替换
insertBefore() // 插入3)查找
getElementsByTagName() // 通过标签名称
getElementsByName() // 通过元素的Name属性的值
getElementById() // 通过元素Id,唯一性
3.实现一个函数clone 可以对Javascript中的五种主要数据类型(Number、string、Object、Array、Boolean)进行复制
function clone(obj) { var o; switch (typeof obj) { case "undefined": break; case "string": o = obj + ""; break; case "number": o = obj - 0; break; case "boolean": o = obj; break; case "object": // object 分为两种情况 对象(Object)或数组(Array) if (obj === null) { o = null; } else { if (Object.prototype.toString.call(obj).slice(8, -1) === "Array") { o = []; for (var i = 0; i < obj.length; i++) { o.push(clone(obj[i])); } } else { o = {}; for (var k in obj) { o[k] = clone(obj[k]); } } } break; default: o = obj; break; } return o; }
4.如何消除一个数组里面重复的元素
5.写一个返回闭包的函数
1.闭包函数是指有权访问另一个函数作用域中的变量的函数 2.创建闭包函数最常见的方式是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量 3.闭包的特点:1函数嵌套函数, 2 函数内部可以引用外部的参数和变量 3 参数和变量不会被垃圾回收机制回收 4.闭包的优点:1 希望一个变量长期驻扎在内存中 * 2 避免全局变量的污染 * 3 私有变量存在 5.闭包的实现 1:函数嵌套函数 * 2 外层函数返回内层函数 * 3 外面有一全局变量接受外层函数
function fun1() { var sum=0; function fun2() { sum++; return sum } return fun2 } var s=fun1(); console.log(s());
6.使用递归完成1到100的累加
function sum(num) { if( num==1 ){ return 1; } return num+sum(num-1); } console.log(sum(100))
7.Javascript有哪几种数据类型
在ECMAScript中,将数据类型分为6种,分别是Undefined、Null、Boolean、Number和String,还有一个复杂数据类型Object。
Undefined、Null、Boolean、Number都属于基本类型。Object、Array和Function则属于引用类型,String有些特殊,具体的会在下面展开分析。
8.如何判断数据类型
1、typeof
2、instanceof
var arr = new Array( );
alert(arr instanceof Array); // 返回true
3、constructor
alert(str.constructor === String); // true
4、prototype
alert(Object.prototype.toString.call(“字符串”) === ‘[object String]’) -------> true; alert(Object.prototype.toString.call(123) === ‘[object Number]’) -------> true; alert(Object.prototype.toString.call([1,2,3]) === ‘[object Array]’) -------> true; alert(Object.prototype.toString.call(new Date()) === ‘[object Date]’) -------> true; alert(Object.prototype.toString.call(function a(){}) === ‘[object Function]’) -------> true; alert(Object.prototype.toString.call({}) === ‘[object Object]’) -------> true;
9.console.log(1+‘2’)、console.log(1-‘2’)和console.log(1+ +‘2’)的打印结果
12
-1
3
10.Js的事件委托是什么,原理是什么
事件委托: 利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。
原理:利用事件的 冒泡原理
事件冒泡:就是事件从最深的节点开始,然后逐步向上传播事件。作用:
提高性能:每一个函数都会占用内存空间,只需添加一个事件处理程序代理所有事件,所占用的内存空间更少;
动态监听:使用事件委托可以自动绑定动态添加的元素,即新增的节点不需要主动添加也可以具有和其它元素一样的事件。
11.如何改变函数内部的this指针的指向
每个函数都包含两个非继承来的方法call()和apply();
使用call()或者apply(),可以改变this的指向;
假设要改变fn函数内部的this的指向,指向obj,那么可以fn.call(obj);或者fn.apply(obj);
call和apply的区别:
call和apply的区别在于参数,他们两个的第一个参数都是一样的,表示调用该函数的对象;
apply的第二个参数是数组,是[arg1,arg2,arg3]这种形式,而call是arg1,arg2,arg3这样的形式。
另外还可以用bind函数:
var bar=fn.bind(obj);
那么fn中的this就指向obj对象了,bind函数返回新的函数,这个函数内的this指针指向obj对象。
12.列举几种解决跨域问题的方式,且说明原理
https://segmentfault.com/a/1190000018017118
跨域并不是请求发不出去,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。
1.jsonp 1) JSONP原理 利用 <script> 标签没有跨域限制的漏洞,网页可以得到从其他来源动态产生的 JSON 数据。JSONP请求一定需要对方的服务器做支持才可以。 2) JSONP和AJAX对比 JSONP和AJAX相同,都是客户端向服务器端发送请求,从服务器端获取数据的方式。但AJAX属于同源策略,JSONP属于非同源策略(跨域请求) 3) JSONP优缺点 JSONP优点是简单兼容性好,可用于解决主流浏览器的跨域数据访问的问题。缺点是仅支持get方法具有局限性,不安全可能会遭受XSS攻击。 4) JSONP的实现流程 声明一个回调函数,其函数名(如show)当做参数值,要传递给跨域请求数据的服务器,函数形参为要获取目标数据(服务器返回的data)。 创建一个<script>标签,把那个跨域的API数据接口地址,赋值给script的src,还要在这个地址中向服务器传递该函数名(可以通过问号传参:?callback=show)。 服务器接收到请求后,需要进行特殊的处理:把传递进来的函数名和它需要给你的数据拼接成一个字符串,例如:传递进去的函数名是show,它准备好的数据是show('我不爱你')。 最后服务器把准备的数据通过HTTP协议返回给客户端,客户端再调用执行之前声明的回调函数(show),对返回的数据进行操作。 5) jQuery的jsonp形式 JSONP都是GET和异步请求的,不存在其他的请求方式和同步请求,且jQuery默认就会给JSONP的请求清除缓存。
2.cors CORS 需要浏览器和后端同时支持。IE 8 和 9 需要通过 XDomainRequest 来实现。 浏览器会自动进行 CORS 通信,实现 CORS 通信的关键是后端。只要后端实现了 CORS,就实现了跨域。 服务端设置 Access-Control-Allow-Origin 就可以开启 CORS。 该属性表示哪些域名可以访问资源,如果设置通配符则表示所有网站都可以访问资源。 虽然设置 CORS 和前端没什么关系,但是通过这种方式解决跨域问题的话,会在发送请求时出现两种情况,分别为简单请求和复杂请求。 1) 简单请求 只要同时满足以下两大条件,就属于简单请求 条件1:使用下列方法之一: GET HEAD POST 条件2:Content-Type 的值仅限于下列三者之一: text/plain multipart/form-data application/x-www-form-urlencoded 请求中的任意 XMLHttpRequestUpload 对象均没有注册任何事件监听器; XMLHttpRequestUpload 对象可以使用 XMLHttpRequest.upload 属性访问。 2) 复杂请求 不符合以上条件的请求就肯定是复杂请求了。 复杂请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求,该请求是 option 方法的,通过该请求来知道服务端是否允许跨域请求。
3.postMessage postMessage是HTML5 XMLHttpRequest Level 2中的API,且是为数不多可以跨域操作的window属性之一,它可用于解决以下方面的问题: 页面和其打开的新窗口的数据传递 多窗口之间消息传递 页面与嵌套的iframe消息传递 上面三个场景的跨域数据传递 postMessage()方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递。 otherWindow.postMessage(message, targetOrigin, [transfer]); message: 将要发送到其他 window的数据。 targetOrigin:通过窗口的origin属性来指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示无限制)或者一个URI。在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配targetOrigin提供的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送。 transfer(可选):是一串和message 同时传递的 Transferable 对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。
4.websocket Websocket是HTML5的一个持久化的协议,它实现了浏览器与服务器的全双工通信,同时也是跨域的一种解决方案。WebSocket和HTTP都是应用层协议,都基于 TCP 协议。但是 WebSocket 是一种双向通信协议,在建立连接之后,WebSocket 的 server 与 client 都能主动向对方发送或接收数据。同时,WebSocket 在建立连接时需要借助 HTTP 协议,连接建立好了之后 client 与 server 之间的双向通信就与 HTTP 无关了。 原生WebSocket API使用起来不太方便,我们使用Socket.io,它很好地封装了webSocket接口,提供了更简单、灵活的接口,也对不支持webSocket的浏览器提供了向下兼容。
5. Node中间件代理(两次跨域) 实现原理:同源策略是浏览器需要遵循的标准,而如果是服务器向服务器请求就无需遵循同源策略。 代理服务器,需要做以下几个步骤: 接受客户端请求 。 将请求 转发给服务器。 拿到服务器 响应 数据。 将 响应 转发给客户端。
6.nginx反向代理 实现原理类似于Node中间件代理,需要你搭建一个中转nginx服务器,用于转发请求。 使用nginx反向代理实现跨域,是最简单的跨域方式。只需要修改nginx的配置即可解决跨域问题,支持所有浏览器,支持session,不需要修改任何代码,并且不会影响服务器性能。 实现思路:通过nginx配置一个代理服务器(域名与domain1相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录。
7.window.name + iframe window.name属性的独特之处:name值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name 值(2MB)。 总结:通过iframe的src属性由外域转向本地域,跨域数据即由iframe的window.name从外域传递到本地域。这个就巧妙地绕过了浏览器的跨域访问限制,但同时它又是安全操作。
8.location.hash + iframe 实现原理: a.html欲与c.html跨域相互通信,通过中间页b.html来实现。 三个页面,不同域之间利用iframe的location.hash传值,相同域之间直接js访问来通信。 具体实现步骤:一开始a.html给c.html传一个hash值,然后c.html收到hash值后,再把hash值传递给b.html,最后b.html将结果放到a.html的hash值中。
9.document.domain + iframe 该方式只能用于二级域名相同的情况下,比如 a.test.com 和 b.test.com 适用于该方式。 只需要给页面添加 document.domain ='test.com' 表示二级域名都相同就可以实现跨域。 实现原理:两个页面都通过js强制设置document.domain为基础主域,就实现了同域。
- CORS支持所有类型的HTTP请求,是跨域HTTP请求的根本解决方案
- JSONP只支持GET请求,JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。
- 不管是Node中间件代理还是nginx反向代理,主要是通过同源策略对服务器不加限制。
- 日常工作中,用得比较多的跨域方案是cors和nginx反向代理
13.谈谈垃圾回收机制的方式及内存管理
回收机制方式
1、定义和用法:垃圾回收机制(GC:Garbage Collection),执行环境负责管理代码执行过程中使用的内存。
2、原理:垃圾收集器会定期(周期性)找出那些不在继续使用的变量,然后释放其内存。但是这个过程不是实时的,因为其开销比较大,所以垃圾回收器会按照固定的时间间隔周期性的执行。
4、垃圾回收策略:标记清除(较为常用)和引用计数。
标记清除:
定义和用法:当变量进入环境时,将变量标记"进入环境",当变量离开环境时,标记为:“离开环境”。某一个时刻,垃圾回收器会过滤掉环境中的变量,以及被环境变量引用的变量,剩下的就是被视为准备回收的变量。
到目前为止,IE、Firefox、Opera、Chrome、Safari的js实现使用的都是标记清除的垃圾回收策略或类似的策略,只不过垃圾收集的时间间隔互不相同。
引用计数:
定义和用法:引用计数是跟踪记录每个值被引用的次数。
基本原理:就是变量的引用次数,被引用一次则加1,当这个引用计数为0时,被视为准备回收的对象。
内存管理
1、什么时候触发垃圾回收?
垃圾回收器周期性运行,如果分配的内存非常多,那么回收工作也会很艰巨,确定垃圾回收时间间隔就变成了一个值得思考的问题。
IE6的垃圾回收是根据内存分配量运行的,当环境中的变量,对象,字符串达到一定数量时触发垃圾回收。垃圾回收器一直处于工作状态,严重影响浏览器性能。
IE7中,垃圾回收器会根据内存分配量与程序占用内存的比例进行动态调整,开始回收工作。
2、合理的GC方案:(1)、遍历所有可访问的对象; (2)、回收已不可访问的对象。
3、GC缺陷:(1)、停止响应其他操作;
4、GC优化策略:(1)、分代回收(Generation GC);(2)、增量GC
14.写一个function ,清除字符串前后的空格
String.trimStart()和String.trimEnd() ### Es 10
15.js实现继承的方法有哪些
原型链继承
将父类的实例作为子类的原型
function Cat(){ } Cat.prototype = new Animal(); Cat.prototype.name = 'cat'; // Test Code var cat = new Cat(); console.log(cat.name); console.log(cat.eat('fish')); console.log(cat.sleep()); console.log(cat instanceof Animal); //true console.log(cat instanceof Cat); //true
特点:
- 非常纯粹的继承关系,实例是子类的实例,也是父类的实例
- 父类新增原型方法/原型属性,子类都能访问到
- 简单,易于实现
缺点:
- 要想为子类新增属性和方法,必须要在
new Animal()
这样的语句之后执行,不能放到构造器中- 无法实现多继承
- 来自原型对象的所有属性被所有实例共享(来自原型对象的引用属性是所有实例共享的)(详细请看附录代码: 示例1)
- 创建子类实例时,无法向父类构造函数传参
构造继承
使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)
function Cat(name){ Animal.call(this); this.name = name || 'Tom'; } // Test Code var cat = new Cat(); console.log(cat.name); console.log(cat.sleep()); console.log(cat instanceof Animal); // false console.log(cat instanceof Cat); // true
特点:
缺点:
- 实例并不是父类的实例,只是子类的实例
- 只能继承父类的实例属性和方法,不能继承原型属性/方法
- 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
实例继承
为父类实例添加新特性,作为子类实例返回
function Cat(name){ var instance = new Animal(); instance.name = name || 'Tom'; return instance; } // Test Code var cat = new Cat(); console.log(cat.name); console.log(cat.sleep()); console.log(cat instanceof Animal); // true console.log(cat instanceof Cat); // false
特点:
- 不限制调用方式,不管是
new 子类()
还是子类()
,返回的对象具有相同的效果缺点:
- 实例是父类的实例,不是子类的实例
- 不支持多继承
拷贝继承
function Cat(name){ var animal = new Animal(); for(var p in animal){ Cat.prototype[p] = animal[p]; } // 2020年10月10日21点36分:感谢 @baclt 的指出,如下实现修改了原型对象,会导致单个实例修改name,会影响所有实例的name值 // Cat.prototype.name = name || 'Tom'; 错误的语句,下一句为正确的实现 this.name = name || 'Tom'; } // Test Code var cat = new Cat(); console.log(cat.name); console.log(cat.sleep()); console.log(cat instanceof Animal); // false console.log(cat instanceof Cat); // true
特点:
- 支持多继承
缺点:
- 效率较低,内存占用高(因为要拷贝父类的属性)
- 无法获取父类不可枚举的方法(不可枚举方法,不能使用for in 访问到)
组合继承
通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用
function Cat(name){ Animal.call(this); this.name = name || 'Tom'; } Cat.prototype = new Animal(); // 感谢 @学无止境c 的提醒,组合继承也是需要修复构造函数指向的。 Cat.prototype.constructor = Cat; // Test Code var cat = new Cat(); console.log(cat.name); console.log(cat.sleep()); console.log(cat instanceof Animal); // true console.log(cat instanceof Cat); // true
特点:
- 弥补了方式2的缺陷,可以继承实例属性/方法,也可以继承原型属性/方法
- 既是子类的实例,也是父类的实例
- 不存在引用属性共享问题
- 可传参
- 函数可复用
缺点:
- 调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)
寄生组合继承
通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点
function Cat(name){ Animal.call(this); this.name = name || 'Tom'; } (function(){ // 创建一个没有实例方法的类 var Super = function(){}; Super.prototype = Animal.prototype; //将实例作为子类的原型 Cat.prototype = new Super(); })(); // Test Code var cat = new Cat(); console.log(cat.name); console.log(cat.sleep()); console.log(cat instanceof Animal); // true console.log(cat instanceof Cat); //true 感谢 @bluedrink 提醒,该实现没有修复constructor。 Cat.prototype.constructor = Cat; // 需要修复下构造函数
特点:
- 堪称完美
缺点:
- 实现较为复杂
16.判断一个变量是否是数组,有哪些办法
instanceof
function isArray (obj) { return obj instanceof Array; }
Array对象的 isArray方法
function isArray (obj) { return Array.isArray(obj); }
Object.prototype.toString
function isArray (obj) { return Object.prototype.toString.call(obj) === '[object Array]'; }
17.let ,const ,var 有什么区别
var:全局变量
var定义变量存在变量提升:只提升声明语句,不提升赋值语句
let:块级作用域
不存在变量提升,其所声明的变量一定要在声明语句之后使用。
const:块级作用域
声明变量时必须赋值,完成初始化后,值不得更改。
18.箭头函数与普通函数有什么区别
- 普通函数的this指向调用者,箭头函数的 this 永远指向其上下文的 this,如上面代码没有为subclassA指定调用者,它就默认指向了window,而subclassB则指向其上下文 。而且任何方法都改变不了箭头函数this指向,如 call() , bind() , apply()
- 箭头函数不具有prototype属性
- 箭头函数不能用于构造函数:也就是箭头函数不能被new
19.随机取1-10之间的整数
20.new操作符具体干了什么
1.创建一个空对象
2.链接到原型
把 obj 的proto 指向构造函数Func的原型对象 prototype,此时便建立了 obj 对象的原型链:
obj->Func.prototype->Object.prototype->null3.绑定this值(让Func中的this指向obj,并执行Func的函数体。)
4.返回新对象
(判断Func的返回值类型:
如果无返回值 或者 返回一个非对象值,则将 obj 作为新对象返回;否则会将 result 作为新对象返回。)
21.Ajax原理
22.模块化开发怎么做
23.异步加载Js的方式有哪些
async
在script标签中使用async
不写async 不写defer放在头部,则解析html的时候,在浏览器解析页面之前会直接读取并执行脚本
如果async = “async”:脚本相对于页面其他部分异步执行(页面一边解析一边执行脚本)
不使用async 且defer=“defer”:脚本将在页面解析完毕后执行;
<script src='../Scripts/js1.js' async></script>
动态创建 script DOM:document.createElement(‘script’);
XmlHttpRequest 脚本注入
function loadScript(url){ let xhr = new XMLHttpRequest() xhr.open('get',url,true) xhr.onreadystatechange = function(){ if(xhr.readyState == 4){ if(xhr.status >= 200 && xhr.status <= 300 || xhr.status == 304){ let script = document.createElement('script') script.type = 'text/javascript' script.text = xhr.responseText document.body.appendChild(script) } } } xhr.send(null) }
es6模块type="module"属性
浏览器对于带有type=”module”的
24.xml和 json的区别
XML定义
扩展标记语言 (Extensible Markup Language, XML) ,用于标记电子文件使其具有结构性的标记语言,可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。 XML使用DTD(document type definition)文档类型定义来组织数据;格式统一,跨平台和语言,早已成为业界公认的标准。
XML是标准通用标记语言 (SGML) 的子集,非常适合 Web 传输。XML 提供统一的方法来描述和交换独立于应用程序或供应商的结构化数据。<1>.XML的优点
A.格式统一,符合标准;
B.容易与其他系统进行远程交互,数据共享比较方便。
<2>.XML的缺点
A.XML文件庞大,文件格式复杂,传输占带宽;
B.服务器端和客户端都需要花费大量代码来解析XML,导致服务器端和客户端代码变得异常复杂且不易维护;
C.客户端不同浏览器之间解析XML的方式不一致,需要重复编写很多代码;
D.服务器端和客户端解析XML花费较多的资源和时间。JSON定义
JSON(JavaScript Object Notation)一种轻量级的数据交换格式,具有良好的可读和便于快速编写的特性。可在不同平台之间进行数据交换。JSON采用兼容性很高的、完全独立于语言文本格式,同时也具备类似于C语言的习惯(包括C, C++, C#, Java, JavaScript, Perl, Python等)体系的行为。这些特性使JSON成为理想的数据交换语言。
JSON基于JavaScript Programming Language , Standard ECMA-262 3rd Edition - December 1999 的一个子集。<1>.JSON的优点:
A.数据格式比较简单,易于读写,格式都是压缩的,占用带宽小;
B.易于解析,客户端JavaScript可以简单的通过eval()进行JSON数据的读取;
C.支持多种语言,包括ActionScript, C, C#, ColdFusion, Java, JavaScript, Perl, PHP, Python, Ruby等服务器端语言,便于服务器端的解析;
D.在PHP世界,已经有PHP-JSON和JSON-PHP出现了,偏于PHP序列化后的程序直接调用,PHP服务器端的对象、数组等能直接生成JSON格式,便于客户端的访问提取;
E.因为JSON格式能直接为服务器端代码使用,大大简化了服务器端和客户端的代码开发量,且完成任务不变,并且易于维护。
<2>.JSON的缺点
A.没有XML格式这么推广的深入人心和喜用广泛,没有XML那么通用性;
B.JSON格式目前在Web Service中推广还属于初级阶段。
25.webpack如何实现打包的
26.常见web安全及防护原理
sql注入原理
是将sql代码伪装到输入参数中,传递到服务器解析并执行的一种攻击手法。也就是说,在一些对server端发起的请求参数中植入一些sql代码,server端在执行sql操作时,会拼接对应参数,同时也将一些sql注入攻击的“sql”拼接起来,导致会执行一些预期之外的操作。
防范
- 对用户输入进行校验
- 不适用动态拼接sql
XSS(跨站脚本攻击)
往web页面插入恶意的html标签或者js代码。
防范
- 尽量采用post而不使用get提交表单
- 避免cookie中泄漏用户的隐式
CSRF(跨站请求伪装)
通过伪装来自受信任用户的请求
防范
- 在客服端页面增加伪随机数,通过验证码
XSS和CSRF的区别:
- XSS是获取信息,不需要提前知道其他用户页面的代码和数据包
- CSRF代替用户完成指定的动作,需要知道其他页面的代码和数据包
27.用过哪些设计模式
简单工厂模式
单例模式
模板模式
策略模式
适配器模式
28.为什么要同源限制
同源策略限制从一个源加载的文档或者脚本如何与来自另一个源的资源进行交互,这是一个用于隔离潜在恶意文件的关键的安全机制。
29.offsetWidth/offsetHeight,clientWidth/clientHeight与scrollWidth/scrollHeight的区别
offsetWidth/offsetHeight返回值包含content + padding + border ,如果有滚动条,也不包含滚动条
clientWidth/clientHeight返回值只包含content + padding,如果有滚动条,也不包含滚动条
scrollWidth/scrollHeight返回值包含content + padding + 溢出内容的尺寸
30.javascript有哪些方法定义对象
对象字面量 var obj={};
构造函数:var obj=new Object();
Object create(): var obj = Object.create(Object.prototype)
31.说说你对promise的了解
Promise是为了解决Javascript回调嵌套过多导致回调地狱(callbackhell)而产生的。目前已被纳入了es2015规范,主流浏览器都支持Promise。为了在工作中更好的运用Promise,我们需要理解Promise规范与内部实现机制,下面我们来手动实现一个Promise。
在写代码之前让我们先了解下 Promise/A+规范。
一个promise有三种状态:
- pending:表示初始状态,可以转移到 fullfilled 或者 rejected 状态
- fulfilled:表示操作成功,不可转移状态
- rejected:表示操作失败,不可转移状态
- 必须有一个 then 异步执行方法,then 接受两个参数且必须返回一个promise:
32.谈谈你对AMD、CMD的理解
1.AMD
异步模块定义 require JS 在推广过程中的对模块定义的规范化产出物(依赖前置 在回调里面加载)在标准之下 对其实现。就像ECMAScript是JavaScript的规范,JavaScript是ECMAScript的实现。
requireJS 动态异步加载js 文件,按照模块加载方法。通过define 定义,依赖 package/lib 加载库,通过回调函数接收lib,调用lib里面的方法 ,return 输出。
2 CMD
同步模块定义 SeaJs 在推广过程中的对模块定义的规范化产出物。 依赖就近,在何处用到,即加载即可。
- Common JS规范 module.exports{ } ,exports.name 定义输出。在前端浏览器并不支持。在node.js 后台端使用。
4.es6 ECMAScript是JavaScript的规范,JavaScript是ECMAScript的实现。
33.web开发中会话跟踪的方法有哪些
当用户发出请求时,服务器就会做出响应,客户端与服务器之间的联系是离散的、非连续的。当用户在同一网站的多个页面之间转换时,根本无法确定是否是同一个客户,会话跟踪技术就可以解决这个问题。当一个客户在多个页面间切换时,服务器会保存该用户的信息。
1.Cookie:
可以使用 cookie 存储购物会话的 ID;在后续连接中,取出当前的会话 ID,并使用这个 ID 从服务器上的查找表(lookup table)中提取出会话的相关信息。 以这种方式使用 cookie 是一种绝佳的解决方案,也是在处理会话时最常使用的方式。但是,sevlet 中最好有一种高级的 API 来处理所有这些任务,以及下面这些冗长乏味的任务:从众多的其他cookie中(毕竟可能会存在许多cookie)提取出存储会话标识符的 cookie;确定空闲会话什么时候过期,并回收它们;将散列表与每个请求关联起来;生成惟一的会话标识符.
2.URL重写: 采用这种方式时,客户程序在每个URL的尾部添加一些额外数据。这些数据标识当前的会话,服务器将这个标识符与它存储的用户相关数据关联起来。 URL重写是比较不错的会话跟踪解决方案,即使浏览器不支持 cookie 或在用户禁用 cookie 的情况下,这种方案也能够工作。
URL 重写具有 cookie 所具有的同样缺点,也就是说,服务器端程序要做许多简单但是冗长乏味的处理任务。即使有高层的 API 可以处理大部分的细节,仍须十分小心每个引用你的站点的 URL ,以及那些返回给用户的 URL。即使通过间接手段,比如服务器重定向中的 Location 字段,都要添加额外的信息。这种限制意味着,在你的站点上不能有任何静态 HTML 页面(至少静态页面中不能有任何链接到站点动态页面的链接)。因此,每个页面都必须使用 servlet 或 JSP 动态生成。即使所有的页面都动态生成,如果用户离开了会话并通过书签或链接再次回来,会话的信息也会丢失,因为存储下来的链接含有错误的标识信息。
3.隐藏的表单域:
HTML 表单中可以含有如下的条目:
这个条目的意思是:在提交表单时,要将指定的名称和值自动包括在 GET 或 POST 数据中。这个隐藏域可以用来存储有关会话的信息,但它的主要缺点是:仅当每个页面都是由表单提交而动态生成时,才能使用这种方法。单击常规的超文本链接并不产生表单提交,因此隐藏的表单域不能支持通常的会话跟踪,只能用于一系列特定的操作中,比如在线商店的结账过程。
4.session:
信息保存在服务器端
使用 setAttribute(String str,Object obj)方法将对象捆绑到一个会话
34.介绍js有哪些内置对象?
JS内置对象分为数据封装类对象和其他对象
数据封装类对象:String,Boolean,Number,Array,和Object;
其他对象:Function,Arguments,Math,Date,RegExp,Error
35.说几条写JavaScript的基本规范?
1、不要在同一行声明多个变量
2、使用===或!==来比较
3、使用字面量的方式来创建对象、数组,替代new Array这种形式
4、不要使用全局函数
5、switch语句必须要带default分支
6、函数不应该有的时候有return,有的时候没有return
7、fon-in循环中的变量,用var关键字说明作用域,防止变量污染
8、变量的声明遵循驼峰命名法,用let替代val,声明构造函数时首字母大写,定义常量的时候尽量用大写字母,用_分割
9、三元表达式可以替代if语句
10、&&和||是可以短路的,使用&&时如果前面一个值是错的,那么后面的值不用判断,使用||时,如果前面一个值是对的,那么后面的值不用判断
11、比较数据类型以下6中情况是false,其他都是true------false、""、0、null、undefined、NaN
12、数据类型检测用typeof,对象类型检测用instanceof
13、异步加载第三方的内容
14、单行注释//,多行注释/**/
15、使用命名空间解决变量名冲突
16、多人协作开发,新建一个js文件,const声明常量,在js文件中引用,用常量名替代方法名,这样做可以防止命名冲突
36.javascript创建对象的几种方式?
37.eval是做什么的?
把字符串参数解析成JS代码并运行,并返回执行的结果;
38.null,undefined 的区别?
39.[“1”, “2”, “3”].map(parseInt) 答案是多少?
[1, NaN, NaN]
map() 方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。
map() 方法按照原始数组元素顺序依次处理元素。
注意: map() 不会对空数组进行检测。
注意: map() 不会改变原始数组。
40.javascript 代码中的”use strict”;是什么意思 ? 使用它区别是什么?
消除 Javascript 语法的一些不合理、不严谨之处,减少一些怪异行为;
消除代码运行的一些不安全之处,保证代码运行的安全;
提高编译器效率,增加运行速度;
为未来新版本的 Javascript 做好铺垫。
区别
1.禁止使用 with 语句。 2.禁止 this 关键字指向全局对象。 3.对象不能有重名的属性。
41.js延迟加载的方式有哪些?
42.defer和async
defer: 表明脚本在执行时不会影响页面的构造。也就是说,脚本会被延迟到整个页面都解析完毕之后再执行。
defer
属性只适用于外部脚本文件async: 只适用于外部脚本文件。
目的:不让页面等待脚本下载和执行,从而异步加载页面其他内容。async和defer一样,都不会阻塞其他资源下载,所以不会影响页面的加载。
缺点:不能控制加载的顺序
43.说说严格模式的限制
变量必须声明后再使用
函数的参数不能有同名属性,否则报错
不能使用with语句
不能对只读属性赋值,否则报错
不能使用前缀0表示八进制数,否则报错
不能删除不可删除的属性,否则报错
不能删除变量delete prop,会报错,只能删除属性delete global[prop]
eval不会在它的外层作用域引入变量
eval和arguments不能被重新赋值
arguments不会自动反映函数参数的变化
不能使用arguments.callee
不能使用arguments.caller
禁止this指向全局对象
不能使用fn.caller和fn.arguments获取函数调用的堆栈
增加了保留字(比如protected、static和interface)
设立"严格模式"的目的,主要有以下几个:
消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为;
消除代码运行的一些不安全之处,保证代码运行的安全;
提高编译器效率,增加运行速度;
为未来新版本的Javascript做好铺垫。
44.attribute和property的区别是什么?
property 和 attribute非常容易混淆,两个单词的中文翻译也都非常相近(property:属性,attribute:特性),但实际上,二者是不同的东西,属于不同的范畴。
property是DOM中的属性,是JavaScript里的对象;
attribute是HTML标签上的特性,它的值只能够是字符串;
简单理解,Attribute就是dom节点自带的属性,例如html中常用的id、class、title、align等。
而Property是这个DOM元素作为对象,其附加的内容,例如childNodes、firstChild等。
45.ECMAScript6 怎么写class么,为什么会出现class这种东西?
缺点主要有:
1、语法和语义难对上了,也就是说从书写出来的代码是看不出来内部是通过原型实现相关功能点的。
2、类的写法有一定的限制性,不再像原型那么灵活了。优点主要有:
1、语法更加紧凑,更加符合传统面相对象的写法了。
2、使 IDE 、类型检查器、代码风格检查器等工具更方便地检测代码语法,做静态分析。
3、 ES6 的类可以继承内置类,并使某些属性和内置类有一样的行为。比如可以继承 Array ,并且子类的 length 属性和 Array 的 length 属性一样会随着元素个数的变化而自动变化。
4、兼容以前的代码。
5、使初学者入门更快。
6、不再需要第三方的继承库。
46.常见兼容性问题
47.函数防抖节流的原理
节流
一般是用在必须执行这个动作,但是不能够执行太频繁的情况下,例如滚动条滚动时函数的处理,可以通过节流适当减少响应次数;
防抖
一般是用来,用户输入有操作时,暂时不执行动作,等待没有新操作时,进行相应响应,例如用户名输入校验的情况,可以等待用户输入完成后再发送请求去校验。总的来说,函数节流与函数防抖的原理非常简单,巧妙地使用 setTimeout 来存放待执行的函数,这样可以很方便的利用 clearTimeout 在合适的时机来清除待执行的函数。
- 函数节流: 指定时间间隔内只会执行一次任务;
- 函数防抖: 任务频繁触发的情况下,只有任务触发的间隔超过指定间隔的时候,任务才会执行。
48.原始类型有哪几种?null是对象吗?
49.为什么console.log(0.2+0.1==0.3) //false
Number 类型表示我们通常意义上的“数字”。这个数字大致对应数学中的有理数。当然,在计算机中,我们有一定的精度限制。
JavaScript 中的 Number 类型基本符合 IEEE 754-2008 规定的双精度浮点数(64位二进制)规则,根据双精度浮点数的定义,Number 类型中有效的整数范围是 -0x1fffffffffffff 至 0x1fffffffffffff,所以 Number 无法精确表示此范围外的整数。
十进制转二进制的小数部分的原则是乘2取整顺序表达,这边会发现0.1 0.2 0.3这三个数都不能有限表达,会产生无限位数。 2. 固定位数二进制无法表示无限循环序列(截断部分会进行进位或者舍去,这边会产生误差)。因此 js的无法用 == 和 === 来比较
50.说一下JS中类型转换的规则?
- 转换为数值类型:Number(mix)、parseInt(string,radix)、parseFloat(string)
- 转换为字符串类型:toString(radix)、String(mix)
- 转换为布尔类型:Boolean(mix)
51.深拷贝和浅拷贝的区别?如何实现
深拷贝和浅拷贝最根本的区别在于: 是否真正获取了一个对象的复制实体,而不是引用。只针对Object和Array这样的引用数据类型。
浅拷贝:只是拷贝了基本类型的数据;而引用类型数据,复制之后也是会发生引用。换句话说,浅拷贝仅仅是指向被复制的内存地址,如果原地址中对象被改变,那么浅拷贝出来的对象也会相应改变;
深拷贝:在计算机中开辟一块新的内存地址用于存放复制的对象;
深拷贝:
1. 递归去复制所有层级属性,即:遍历对象、数组直到里边都是基本数据类型,然后再去复制。
2. 借用 JSON 对象的 parse 和 stringify : 这种方法虽然可以实现数组或对象深拷贝,但不能处理函数。
3. 借用 jQuery 中 extend 方法:
$.extend(true,target,a,b,...) // 不管后面有多少个对象,都将成为第一个对象 target 的属性; // true:表示为深拷贝;
52.如何判断this?箭头函数的this是什么
53.== 和 ===的区别
54.什么是闭包
55.JavaScript原型,原型链 ? 有什么特点?
每个对象都会在其内部初始化一个属性,就是prototype(原型),当我们访问一个对象的属性时,
如果这个对象内部不存在这个属性,那么他就会去prototype里找这个属性,这个prototype又会有自己的prototype,
于是就这样一直找下去,也就是我们平时所说的原型链的概念。
关系:instance.constructor.prototype = instance.proto
特点:
JavaScript对象是通过引用来传递的,我们创建的每个新对象实体中并没有一份属于自己的原型副本。当我们修改原型时,与之相关的对象也会继承这一改变。
56.typeof()和instanceof()的用法区别
typeof() 是一个一元运算,放在一个运算数之前,运算数可以是任意类型。
它返回值是一个字符串,该字符串说明运算数的类型。,typeof一般只能返回如下几个结果:number,boolean,string,function,object,undefined。 我们可以使用typeof来获取一个变量是否存在,如if(typeof a!=“undefined”){alert(“ok”)},而不要去使用if(a)因为如果a不存在(未声明)则会出错,对于Array,Null,DOM对象等特 殊对象使用typeof一律返回object,这正是typeof的局限性。instanceof 用于判断一个变量是否某个对象的实例,如var a=new Array();alert(a instanceof Array);会返回true,同时alert(a instanceof Object)也会返回true;这是因为Array是object的子类。再如:function test(){};var a=new test();alert(a instanceof test)会返回
57.什么是变量提升
58.call、apply以及bind函数内部实现是怎么样的
call, apply, bind都是改变函数执行的上下文,说的直白点就是改变了函数this的指向。不同的是:call和apply改变了函数的this,并且执行了该函数,而bind是改变了函数的this,并返回一个函数,但不执行该函数。
59.为什么会出现setTimeout倒计时误差?如何减少
线程,进程
两个名词都是 CPU 工作时间片的一个描述。
进程(process) 指的是CPU 在 运行指令及加载和保存上下文所需的时间,放在应用上是指计算机中已运行的程序。
线程(thread) 是操作系统能够进行运算的最小单位。它被包含在 进程 之中,描述了执行一段指令所需的时间。
- 单线程:按代码书写顺序从头到尾,一行一行地执行代码,如果其中一行代码报错,那么剩下代码将不再执行。容易阻塞代码。
- 多线程:代码运行的环境不同,各线程独立,互不影响,避免阻塞。
setTimeout误差
定时器是属于 宏任务(macrotask) 。如果当前 执行栈 所花费的时间大于 定时器 时间,那么定时器的回调在 宏任务(macrotask) 里,来不及去调用,所有这个时间会有误差。
60.谈谈你对JS执行上下文栈和作用域链的理解
61.new的原理是什么?通过new的方式创建对象和通过字面量创建有什么区别?
62.prototype 和 proto 区别是什么?
63.使用ES5实现一个继承?
64.取数组的最大值(ES5、ES6)
65.ES6新的特性有哪些?
66.promise 有几种状态, Promise 有什么优缺点 ?
67.Promise构造函数是同步还是异步执行,then呢 ?promise如何实现then处理 ?
68.Promise和setTimeout的区别 ?
69.如何实现 Promise.all ?
70.如何实现 Promise.finally ?
71.如何判断img加载完成
72.如何阻止冒泡?
73.如何阻止默认事件?
74.ajax请求时,如何解释json数据
75.json和jsonp的区别?
76.如何用原生js给一个按钮绑定两个onclick事件?
77.拖拽会用到哪些事件
78.document.write和innerHTML的区别
79.jQuery的事件委托方法bind 、live、delegate、on之间有什么区别?
80.浏览器是如何渲染页面的?
81.$(document).ready()方法和window.onload有什么区别?
82.jquery中.get()提交和post()提交有区别吗?
83.对前端路由的理解?前后端路由的区别?
84.手写一个类的继承
85.XMLHttpRequest:XMLHttpRequest.readyState;状态码的意思
86.列出JS中的一些设计模式
设计模式是软件设计中常见问题的通用可重用解决方案,以下是一些设计模式是:
创建模式:该模式抽象了对象实例化过程。
结构型模式:这些模式处理不同的类和对象以提供新功能。
行为模式:也称发布-订阅模式,定义了一个被观察者和多个观察者的、一对多的对象关系。
并行设计模式:这些模式处理多线程编程范例。
架构设计模式:这些模式用于处理架构设计。
87.数组去重复的方法有哪些
使用
set
functionuniquearray(array) { let unique_array= Array.from(set(array)) return unique_array; }
使用
filter
function unque_array (arr) { let unique_array = arr.filter(function(elem, index, self) { return index == self.indexOf(elem); }) return unique_array; } console.log(unique_array(array_with_duplicates));
使用
for
循环Array dups_names = ['Ron', 'Pal', 'Fred', 'Rongo', 'Ron']; function dups_array(dups_names) { let unique = {}; names.forEach(function(i) { If (!unique[i]) { unique[i] = true; } }); return Object.keys(unique);} // Ron, Pal, Fred, Rongo Dups_array(names);
88.JS中的宿主对象与原生对象有何不同?
宿主对象:这些是运行环境提供的对象。这意味着它们在不同的环境下是不同的。例如,浏览器包含像
windows
这样的对象,但是Node.js环境提供像Node List
这样的对象。原生对象:这些是JS中的内置对象。它们也被称为全局对象,因为如果使用JS,内置对象不受是运行环境影响。
89.JS中的Array.splice()
和Array.slice()
方法有什么区别
//第一个例子: var arr=[0,1,2,3,4,5,6,7,8,9];//设置一个数组 console.log(arr.slice(2,7));//2,3,4,5,6 console.log(arr.splice(2,7));//2,3,4,5,6,7,8 //由此我们简单推测数量两个函数参数的意义, slice(start,end)第一个参数表示开始位置,第二个表示截取到的位置(不包含该位置) splice(start,length)第一个参数开始位置,第二个参数截取长度 //第二个: var x=y=[0,1,2,3,4,5,6,7,8,9] console.log(x.slice(2,5));//2,3,4 console.log(x);[0,1,2,3,4,5,6,7,8,9]原数组并未改变 //接下来用同样方式测试splice console.log(y.splice(2,5));//2,3,4,5,6 console.log(y);//[0,1,7,8,9]显示原数组中的数值被剔除掉了
slice
和splice
虽然都是对于数组对象进行截取,但是二者还是存在明显区别,函数参数上slice
和splice
第一个参数都是截取开始位置,slice
第二个参数是截取的结束位置(不包含),而splice
第二个参数(表示这个从开始位置截取的长度),slice
不会对原数组产生变化,而splice
会直接剔除原数组中的截取数据!
90.JS中的 substr()
和substring()
函数有什么区别
substr()
函数的形式为substr(startIndex,length)
。它从startIndex
返回子字符串并返回’length
'个字符数。var s = "hello"; ( s.substr(1,4) == "ello" ) // true
substring()
函数的形式为substring(startIndex,endIndex)
。 它返回从startIndex
到endIndex - 1
的子字符串。var s = "hello"; ( s.substring(1,4) == "ell" ) // true
91、call apply bind
**call **
- 可以调用函数,改变函数this指向
- 主要可以实现继承
var o = { name:'wolf' } function fn(a,b){ console.log(a,b,this) } fn.call(o,1,2) function father(name){ this.name = name } function sun(name){ father.call(this,name) } var s = new sun('lalal')
apply
- 可以调用函数,改变函数this指向。不过传参必须维数组
- 借用apply 使用数学内置对象
var o = { name:'wolf' } function fn(a,b){ console.log(a,b,this) } fn.apply(o,[1,2]) const arr = [1,4,6,8,2343,422,12] Math.max.apply(arr)
bind
- 改变this指向,不调用函数。
- 返回的是原函数改变this指向后的新函数
var o = { name:'wolf' } function fn(a,b){ console.log(a,b,this) } var f = fn.bind(o)