JavaScript面试题(一) https://developer.aliyun.com/article/1399303
31.★★★ 如何用 setTImeout 来实现 setInterval?
1.不去关心回调函数是否还在运行 在某些情况下,函数可能需要比间隔时间更长的时间去完成执行。比如说是用setInterval每隔5秒对远端服务器进行轮询,网络延迟,服务器无响应以及其他因素将会阻止请求按时按成。结果会导致返回一串无必要的排成队列请求。 2.忽视错误 因为某些原因,setInterval调用的代码中会出现一个错误,但是代码并不会中止执行而是继续执行错误的代码。 3.缺乏灵活性 除了前面提到的缺点之外,我非常希望setInterval方法能有一个表明执行次数的参数而不是无休止的执行下去。 function interval(func, w, t){ var interv = function(){ if(typeof t === "undefined" || t-- > 0){ setTimeout(interv, w); try{ func.call(null); } catch(e){ t = 0; throw e.toString(); } } }; setTimeout(interv, w); };
32.★★★ 如何判断 user 对象里有没有 a 这个属性?如果把user对象中所有的属性都输出出来?(var user = {‘a’: ‘19’, ‘b’: ‘18’, ‘c’: ‘16’})
如何判断 user 对象里有没有 a 这个属性? js对象的Object.hasOwnProperty()方法 返回一个布尔值,判断对象是否包含特定的自身(非继承)属性。 let obj = new Object(); obj.a = "123"; console.log(obj.hasOwnProperty('a')) // true console.log(obj.hasOwnProperty('b')) // false 把user对象中所有的属性都输出出来 for(item for user){ console.log(item) }
33.★★ 使用 setTimeout 模拟 setInterval 的功能做一个60秒的倒数计时
function setInter(s,fn){ let timeOut = (s,fn)=>{ setTimeout(()=>{ fn(); timeOut(s,fn); },s) } timeOut(s,fn); } //调用上面的方法 setInter(60000,()=>{console.log("hello world!")})
34.★★★ 实现一个函数 add(),运算结果可以满足如下预期结果
add(1)(2) //3
add(1,2,3)(10) //16
add(1)(2)(3,4)(5) //15
function add () { var args = Array.prototype.slice.call(arguments); var fn = function () { var sub_arg = Array.prototype.slice.call(arguments); // 把全部的参数聚集到参数的入口为一个参数: args.concat(sub_arg) return add.apply(null, args.concat(sub_arg)); } fn.valueOf = function () { return args.reduce(function(a, b) { return a + b; }) } return fn; }
add(1,2,3)(10) //16 add(1)(2)(3,4)(5) //15
35.★★★ 如何避免回调地狱?
Promise 对象就是为了解决这个问题而提出的。它不是新的语法功能,而是一种新的写法,允许将回调函数的嵌套,改成链式调用。
promise只有两个状态resolve和reject,当它触发任何一个状态后,它会将当前的值缓存起来,并在有回调函数添加进来的时候尝试调用回调函数,如果这个时候还没有触发resolve或者reject,那么回调函数会被缓存,等待调用,如果已经有了状态(resolve或者reject),则立刻调用回调函数。并且所有回调函数在执行后都立即被销毁。
ES6 co/yield方案
yield: Generator 函数是协程在 ES6 的实现,而yield是 Generator关键字, 异步操作需要暂停的地方,都用yield语句注明。
co: co 模块是著名程序员 TJ Holowaychuk 于 2013 年 6 月发布的一个小工具,用于 Generator 函数的自动执行。
ES7 async/await 方案
async/await是es7的新标准,并且在node7.0中已经得到支持。
它就是 Generator 函数的语法糖,async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await,仅此而已。可以理解官方对co和Generator 封装方案。
36.★★ 写一个 function,清除字符串前后的空格。(兼容所有的浏览器)
function trim(str) { if (str && typeof str === "string") { return str.replace(/(^\s*)|(\s*)$/g,""); //去除前后空白符 } }
37.★★ 使用正则表达式验证邮箱格式。
function fChkMail(emailAddress){ var reg = new RegExp("^[a-z0-9]+([._\\-]*[a-z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$"); var bChk=reg.test(emailAddress); return bChk; }
38.★★★ 简述同步和异步的区别
同步:
同步的思想是:所有的操作都做完,才返回给用户。这样用户在线等待的时间太长,给用户一种卡死了的感觉(就是系统迁移中,点击了迁移,界面就不动了,但是程序还在执行,卡死了的感觉)。这种情况下,用户不能关闭界面,如果关闭了,即迁移程序就中断了。
异步:
将用户请求放入消息队列,并反馈给用户,系统迁移程序已经启动,你可以关闭浏览器了。然后程序再慢慢地去写入数据库去。这就是异步。但是用户没有卡死的感觉,会告诉你,你的请求系统已经响应了。你可以关闭界面了。
同步和异步本身是相对的:
同步就相当于是 当客户端发送请求给服务端,在等待服务端响应的请求时,客户端不做其他的事情。当服务端做完了才返回到客户端。这样的话客户端需要一直等待。用户使用起来会有不友好。
异步就是,当客户端发送给服务端请求时,在等待服务端响应的时候,客户端可以做其他的事情,这样节约了时间,提高了效率。存在就有其道理 异步虽然好 但是有些问题是要用同步用来解决,比如有些东西我们需要的是拿到返回的数据在进行操作的。这些是异步所无法解决的。
39.★★ JavaScript 中 callee 和 caller 的作用
callee
callee是对象的一个属性,该属性是一个指针,指向参数arguments对象的函数
作用:就是用来指向当前对象
返回正被执行的 Function 对象,也就是所指定的 Function 对象的正文.
callee是arguments 的一个属性成员,它表示对函数对象本身的引用,这有利于匿名
函数的递归或者保证函数的封装性
caller
caller是函数对象的一个属性,该属性保存着调用当前函数的函数的引用(指向当前函数的直接父函数)
返回一个对函数的引用,该函数调用了当前函数。
functionName.caller
functionName 对象是所执行函数的名称。
注意:
对于函数来说,caller 属性只有在函数执行时才有定义。 如果函数是由 Javascript 程序的顶层调用的,那么 caller 包含的就是 null 。
40.★★ 统计字符串中字母个数或统计最多的字母数。
function count(str) { var obj = {}; // 统计对象 var i = 0; var len = str.length; for (; i < len; i++){ var curChar = str.charAt(i); // 如果结果对象存在该字符的属性,则自增,否则置为1 if (obj[curChar]) { obj[curChar]++; } else { obj[curChar] = 1; } } // 返回结果 return obj; } var str = "javaScript"; console.log(count(str));
41.★★★ jQuery 的事件委托方法 on,live,delegate之间有区别?
live 把事件委托交给了document(根节点),document 向下去寻找符合条件的元素(),
不用等待document加载结束也可以生效。
delegate可指定事件委托对象,相比于live性能更优,直接锁定指定选择器;
on事件委托对象选填,如果不填,即给对象自身注册事件,填了作用和delegate一致。
42.★★★ 简述下 Promise 对象
Promise是异步编程的一种解决方案,比传统的解决方案(回调函数和事件)更合理更强大。
所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件 (通常是一个异步操作)的结果。
从语法上说,Promise是一个对象,从它可以获取异步操作的消息。
Promise对象有以下2个特点:
1.对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:Pending(进行中)、Resolved(已完成)和Rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
2**.一旦状态改变,就不会再变,任何时候都可以得到这个结果**。Promise对象的状态改变,只有两种可能:从Pending变为Resolved;从Pending变为Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对Promise对象田静回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
有了Promise对象,就可以把异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供了统一的接口,使得控制异步操作更加容易。
43.★★★ 数组扁平化,不用 api
function myFlat(arr){ let res = []; for(let i=0; i<arr.length; i++){ if(arr[i] instanceof Array){ res = res.concat(myFlat(arr[i])); }else { res.push(arr[i]); } } return res; } let arr = [1,[2,3,[4,5]]]; console.log(myFlat(arr))
44.★★★ 用 JavaScript 实现观察者模式
function BusinessOne(name){ this.name = name; //订阅者的集合 this.subscribers = new Array(); } //订阅者的发送消息的方法(推模式) BusinessOne.prototype.delive = function(news){ var self = this; //给每一个订阅者发送消息 this.subscribers.forEach( function(fn){ //调用接受者处理信息的函数 fn(news,self); } ) } //扩展公共订阅的函数,和取消订阅的函数 Function.prototype.subscribe = function(publisher){ var that = this; //some 访问数组度i型并且以参数的形式传回回调函数中 //只要至少有一次返回是true那么some就是true var alreadyExists = publisher.subscribers.some( function(el){ //处理不能重复订阅的功能 if(el == that){ return; } } ); //没用订阅你就可以订阅 if(!alreadyExists){ publisher.subscribers.push(that); } return this; } //取消 Function.prototype.unsubscribe = function(publisher){ var that = this; publisher.subscribers = publisher.subscribers.filter( function(el){ if(el !== that){ return el; } } ); return this; };
45.★★ 简述一下面象对象的六法则
1. 单一职责原则:一个类只做它该做的事情
2. 开闭原则:软件实体应当对扩展开放,对修改关闭
3. 依赖倒转原则:面向接口编程
4. 接口隔离原则:接口要小而专,绝不能大而全
5. 合成聚合复用原则:优先使用聚合或合成关系复用代码
6. 迪米特法则:迪米特法则又叫最少知识原则,一个对象应当对其他对象有尽可能少的了解(低耦合)
46.★★★ 谈谈垃圾回收机制方法以及内存管理
垃圾回收方式
① 标记清除
工作原理:是当变量进入环境时,将这个变量标记为“进入环境”。当变量离开环境时,则将其标记为“离开环境”。标记“离开环境”的就回收内存。
② 引用计数
工作原理:跟踪记录每个值被引用的次数。一旦没有引用,内存就直接释放了。
什么时候触发垃圾回收?
垃圾回收器周期性运行,如果分配的内存非常多,那么回收工作也会很艰巨,确定垃圾回收时间间隔就变成了一个值得思考的问题。
1、合理的GC方案:(1)、遍历所有可访问的对象; (2)、回收已不可访问的对象。
2、GC缺陷: (1)、停止响应其他操作;
3、GC优化策略: (1)、分代回收(Generation GC);(2)、增量GC
47.★★★ 开发过程中遇到内存泄漏的问题都有哪些?
1、当页面中元素被移除或替换时,若元素绑定的事件仍没被移除,在IE中不会作出恰当处理,此时要先手工移除事件,不然会存在内存泄露。
2、由于是函数内定义函数,并且内部函数–事件回调的引用外暴了,形成了闭包。闭包可以维持函数内局部变量,使其得不到释放。
48.★★★ 请编写获取当前窗口地址中查询参数name的值,当前窗口地址为:https://foo.com/?id=1&name=tom
function GetQueryString(name){ var reg = new RegExp("(^|&)"+ name +"=([^&]*)(&|$)"); var r = window.location.search.substr(1).match(reg); if(r!=null) return unescape(r[2]); return null; }
49.★★★ 已知a,b两个构造函数,现在 let c = new a(),如何在c的存储地址不变的情况下,改变c的继承(c->a 转为 c->b)
1. 改变原型链:通过改变C的prototype为b,实现内存地址不动,改变继承
50.★★★ 浏览器有哪些兼容问题,你封装过什么插件
//1.滚动条到顶端的距离(滚动高度) var scrollTop = document.documentElement.scrollTop || document.body.scrollTop; //2.滚动条到左端的距离 var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft; //3. IE9以下byClassName function byClassName(obj,className){ //判断是否支持byClassName if(obj.getElementsByClassName){ //支持 return obj.getElementsByClassName(className); }else{ //不支持 var eles = obj.getElementsByTagName('*'); //获取所有的标签 var arr = []; //空数组,准备放置找到的对象 //遍历所有的标签 for(var i = 0,len = eles.length;i < len;i ++){ //找出与我指定class名相同的对象 if(eles[i].className === className){ arr.push(eles[i]); //存入数组 } } return arr; //返回 } } //4. 获取非行内样式兼容 IE:currentStyle 标准:getComputedStyle function getStyle(obj,attr){ return window.getComputedStyle ? getComputedStyle(obj,true)[attr] : obj.currentStyle[attr]; } //div.style.width = '';设置样式 //obj['属性']: 对象是变量时,必须用对象['属性']获取。 //5. 获取事件对象的兼容 evt = evt || window.event //6. 获取鼠标编码值的兼容 function getButton(evt){ var e = evt || window.event; if(evt){ return e.button; }else if(window.event){ switch(e.button){ case 1 : return 0; case 4 : return 1; case 2 : return 2; } } } //7. 获取键盘按键编码值的兼容 var key = evt.keyCode || evt.charCode || evt.which; //8. 阻止事件冒泡的兼容 e.stopPropagation ? e.stopPropagation() : e.cancelBubble = true; //9. 阻止超链接的默认行为的兼容 evt.preventDefault ? evt.preventDefault() : evt.returnValue = false; //10. 添加事件监听器的兼容 function addEventListener(obj,event,fn,boo){ if(obj.addEventListener){ obj.addEventListener(event,fn,boo); }else if(obj.attachEvent){ obj.attachEvent('on' + event,fn); } } //11. 移除事件监听器的兼容 function removeEventListener(obj,event,fn,boo){ if(obj.removeEventListener){ obj.removeEventListener(event,fn,boo); }else if(obj.detachEvent){ obj.detachEvent('on' + event,fn); } } //12. 获取事件源的兼容 var target = event.target || event.srcElement;
51.★★★ 如何判断一个对象是否为数组,函数
方法一: instanceof: var arr=[]; console.log(arr instanceof Array) //返回true 方法二: constructor: console.log(arr.constructor == Array); //返回true 方法三: Array.isArray() console.log(Array.isArray(arr)); //返回true
52.★★★ 写一个函数,接受可变个数参数,且每个参数均为数字,返回参数的最大值。
function myMax(){ return Math.max(arguments) }
53.★★★ 请写出 ES6 Array.isArray()
if (!Array.isArray){ Array.isArray = function(arg){ return Object.prototype.toString.call(arg) === '[object Array]'; }; }
54.★★★ 实现一个函数 clone,可以对 JavaScript 中的5种主要数据类型进行值复制。
// 方法一: Object.prototype.clone = function() { var o = this.constructor === Array ? [] : {}; for (var e in this) { o[e] = typeof this[e] === "object" ? this[e].clone() : this[e]; } return o; }; //方法二: /** * 克隆一个对象 * @param Obj * @returns */ function clone(Obj) { var buf; if (Obj instanceof Array) { buf = []; //创建一个空的数组 var i = Obj.length; while (i--) { buf[i] = clone(Obj[i]); } return buf; } else if (Obj instanceof Object) { buf = {}; //创建一个空对象 for (var k in Obj) { //为这个对象添加新的属性 buf[k] = clone(Obj[k]); } return buf; } else { //普通变量直接赋值 return Obj; } }
55.★★★ 假如A页面我定义了一个定时器,然后跳到B页面如果让A页面的定时器暂停
方法1:在beforeDestroy()等生命周期结束阶段内清除定时器: beforeDestroy() { clearInterval(this.timer); this.timer = null; } 方法2:通过$once这个事件侦听器器在定义完定时器之后的位置来清除定时器。 const timer = setInterval(() =>{ // 某些定时器操作 }, 500); // 通过$once来监听定时器,在beforeDestroy钩子可以被清除。 this.$once('hook:beforeDestroy', () => { clearInterval(timer); })
56.★★★ promise的实现原理,如果我现在向服务器发送一个请求,但是我后悔了,不想让服务器返回数据,去实现一个delay
取消结束Promise的方法? 1. 返回一个pending状态的Promise,原Promise链会终止 Promise.resolve().then(() => { console.log('ok1') return new Promise(()=>{}) // 返回“pending”状态的Promise对象 }).then(() => { // 后续的函数不会被调用 console.log('ok2') }).catch(err => { console.log('err->', err) }) 2. Promise.race竞速方法 let p1 = new Promise((resolve, reject) => { resolve('ok1') }) let p2 = new Promise((resolve, reject) => { setTimeout(() => {resolve('ok2')}, 10) }) Promise.race([p2, p1]).then((result) => { console.log(result) //ok1 }).catch((error) => { console.log(error) }) 3. 当Promise链中抛出错误时,错误信息沿着链路向后传递,直至捕获 Promise.resolve().then(() => { console.log('ok1') throw 'throw error1' }).then(() => { console.log('ok2') }, err => { // 捕获错误 console.log('err->', err) }).then(() => { // 该函数将被调用 console.log('ok3') throw 'throw error3' }).then(() => { // 错误捕获前的函数不会被调用 console.log('ok4') }).catch(err => { console.log('err->', err) Axios如何取消请求? 第一种通过CancelToken.source工厂方法创建cancel token var CancelToken = axios.CancelToken; var source = CancelToken.source(); axios.get('/user/12345', { cancelToken: source.token }).catch(function(thrown) { if (axios.isCancel(thrown)) { console.log('Request canceled', thrown.message); } else { // 处理错误 } }); // 取消请求(message 参数是可选的) source.cancel('Operation canceled by the user.'); 第二种通过传递executor函数到CancelToken的构造函数来创建cancel token var CancelToken = axios.CancelToken; var cancel; axios.get('/user/12345', { cancelToken: new CancelToken(function executor(c) { // executor 函数接收一个 cancel 函数作为参数 cancel = c; }) }); // 取消请求 cancel();
57.★★★ CommonJS 和 RequireJS 的实现原理
commonjs是通过module.exports导出模块,用require引入一个模块,原理:闭包 requirejs是通过define定义导出模块,用require引入模块。 define('name',[],function(){ return 'requirejs' }) define('say',['name'].function(name){ return "my name is" + name }) require(['say'],function(text){ console.log(text) })
58.★★★ 面向对象编程与面向过程编程的区别?
面向过程的程序设计把计算机程序视为一系列的命令集合,即一组函数的顺序执行。
为了简化程序设计,面向过程把函数继续切分为子函数,
即把大块函数通过切割成小块函数来降低系统的复杂度。
面向对象的程序设计把计算机程序视为一组对象的集合,
而每个对象都可以接收其他对象发过来的消息,并处理这些消息,
计算机程序的执行就是一系列消息在各个对象之间传递。
59.★★★ eval 是做什么的?性能怎么样?安全如何?
它的功能是把对应的字符串解析成js代码并运行,
应该避免使用eval,因为不安全,非常耗性能(2次,一次解析成js语句,一次执行)
注意:在项目里写js代码的时候,禁止使用的,因为有安全因素。
60.★★★★★ 函数节流、防抖。scroll resize 使用函数节流实现不要频繁触发事件的需求。
防抖: //scroll方法中的do somthing至少间隔500毫秒执行一次 window.addEventListener('scroll',function(){ var timer;//使用闭包,缓存变量 return function(){ if(timer) clearTimeout(timer); timer = setTimeout(function(){ console.log('do somthing') },500) } }());//此处()作用 - 立即调用return后面函数,形成闭包 节流: //scroll方法中当间隔时间大于2s,do somthing执行一次 window.addEventListener('scroll',function(){ var timer ;//使用闭包,缓存变量 var startTime = new Date(); return function(){ var curTime = new Date(); if(curTime - startTime >= 2000){ timer = setTimeout(function(){ console.log('do somthing') },500); startTime = curTime; } } }());//此处()作用 - 立即调用return后面函数,形成闭包
JavaScript面试题(三) https://developer.aliyun.com/article/1399381