不要在同一行声明多个变量
请是用 ===/!== 来比较 true/false 或者数值
使用对象字面量替代 new Array 这种形式
不要使用全局函数
Switch 语句必须带有 default 分支
If 语句必须使用大括号
for-in 循环中的变量 应该使用 let 关键字明确限定做用域,从而避免做用域污染javascript
闭包就是可以读取其余函数内部变量的函数
闭包是指有权访问另外一个函数做用域中变量的函数,建立闭包的最多见的方式就是在一个
函数内建立另外一个函数,经过另外一个函数访问这个函数的局部变量,利用闭包能够突破做用链域
闭包的特性:html
函数内再嵌套函数
内部函数能够引用外层的参数和变量
参数和变量不会被垃圾回收机制回收java
优势:可以实现封装和缓存等
缺点:消耗内存、使用不当会内存溢出,
解决方法:在退出函数以前,将不使用的局部变量所有删除node
做用域链的做用是保证执行环境里有权访问的变量和函数是有序的,做用域链的变量只能向上访问,变量访问到 window对象即被终止,做用域链向下访问变量是不被容许的。
简单的说,做用域就是变量与函数的可访问范围,即做用域控制着变量与函数的可见性和生命周期jquery
每一个对象都会在其内部初始化一个属性,就是 prototype (原型),当咱们访问一个对象的属性时,若是这个对象内部不存在这个属性,那么他就会去 prototype 里找这个属性,这个prototype 又会有本身的 prototype ,因而就这样一直找下去,也就是咱们平时所说的原型链的概念
关系: instance.constructor.prototype = instance._proto_
特色:JavaScript 对象是经过引用来传递的,咱们建立的每一个新对象实体中并无一份属于本身的原型副本。当咱们修改原型时,与之相关的对象也会继承这一改变当咱们须要一个属性的时, Javascript 引擎会先看当前对象中是否有这个属性, 若是没有的,就会查找他的 Prototype 对象是否有这个属性,如此递推下去,一直检索到 Object内建对象nginx
原型 prototype 机制或 apply 和 call 方法去实现较简单,建议使用构造函数与原型混合方式es6
function Parent(){ this.name = 'wang'; } function Child(){ this.age = 28; } Child.prototype = new Parent();//继承了Parent,经过原型 var demo = new Child(); alert(demo.age); alert(demo.name);//获得被继承的属性
必要性:因为字符串、对象和数组没有固定大小,全部当他们的大小已知时,才能对他们进行动态的存储分配。JavaScript程序每次建立字符串、数组或对象时,解释器都必须分配内存来存储那个实体。只要像这样动态地分配了内存,最终都要释放这些内存以便他们可以被再用,不然,JavaScript的解释器将会消耗完系统中全部可用的内存,形成系统崩溃。面试
这段话解释了为何须要系统须要垃圾回收,JS不像C/C++,他有本身的一套垃圾回收机制(Garbage Collection)。JavaScript的解释器能够检测到什么时候程序再也不使用一个对象了,当他肯定了一个对象是无用的时候,他就知道再也不须要这个对象,能够把它所占用的内存释放掉了。例如:ajax
var a="hello world"; var b="world"; var a=b; //这时,会释放掉"hello world",释放内存以便再引用
垃圾回收的方法:标记清除、计数引用。chrome
标记清除
这是最多见的垃圾回收方式,当变量进入环境时,就标记这个变量为”进入环境“,从逻辑上讲,永远不能释放进入环境的变量所占的内存,永远不能释放进入环境变量所占用的内存,只要执行流程进入相应的环境,就可能用到他们。当离开环境时,就标记为离开环境。
垃圾回收器在运行的时候会给存储在内存中的变量都加上标记(全部都加),而后去掉环境变量中的变量,以及被环境变量中的变量所引用的变量(条件性去除标记),删除全部被标记的变量,删除的变量没法在环境变量中被访问因此会被删除,最后垃圾回收器,完成了内存的清除工做,并回收他们所占用的内存。
引用计数法
另外一种不太常见的方法就是引用计数法,引用计数法的意思就是每一个值没引用的次数,当声明了一个变量,并用一个引用类型的值赋值给改变量,则这个值的引用次数为1,;相反的,若是包含了对这个值引用的变量又取得了另一个值,则原先的引用值引用次数就减1,当这个值的引用次数为0的时候,说明没有办法再访问这个值了,所以就把所占的内存给回收进来,这样垃圾收集器再次运行的时候,就会释放引用次数为0的这些值。
用引用计数法会存在内存泄露,下面来看缘由:
function problem() { var objA = new Object(); var objB = new Object(); objA.someOtherObject = objB; objB.anotherObject = objA; }
在这个例子里面,objA和objB经过各自的属性相互引用,这样的话,两个对象的引用次数都为2,在采用引用计数的策略中,因为函数执行以后,这两个对象都离开了做用域,函数执行完成以后,由于计数不为0,这样的相互引用若是大量存在就会致使内存泄露。
特别是在DOM对象中,也容易存在这种问题:
var element=document.getElementById(’‘); var myObj=new Object(); myObj.element=element; element.someObject=myObj;
这样就不会有垃圾回收的过程。
在一个函数中,首先填充几个参数,而后再返回一个新的函数的技术,称为函数的柯里化。一般可用于在不侵入函数的前提下,为函数 预置通用参数,供屡次重复调用。
const add = function add(x) { return function (y) { return x + y } } const add1 = add(1) add1(2) === 3 add1(20) === 21
防抖(Debouncing)
防抖技术便是能够把多个顺序地调用合并成一次,也就是在必定时间内,规定事件被触发的次数。
通俗一点来讲,看看下面这个简化的例子:
// 简单的防抖动函数 function debounce(func, wait, immediate) { // 定时器变量 var timeout; return function() { // 每次触发 scroll handler 时先清除定时器 clearTimeout(timeout); // 指定 xx ms 后触发真正想进行的操做 handler timeout = setTimeout(func, wait); }; }; // 实际想绑定在 scroll 事件上的 handler function realFunc(){ console.log("Success"); } // 采用了防抖动 window.addEventListener('scroll',debounce(realFunc,500)); // 没采用防抖动 window.addEventListener('scroll',realFunc);
上面简单的防抖的例子能够拿到浏览器下试一下,大概功能就是若是 500ms 内没有连续触发两次 scroll 事件,那么才会触发咱们真正想在 scroll 事件中触发的函数。
上面的示例能够更好的封装一下
// 防抖动函数 function debounce(func, wait, immediate) { var timeout; return function() { var context = this, args = arguments; var later = function() { timeout = null; if (!immediate) func.apply(context, args); }; var callNow = immediate && !timeout; clearTimeout(timeout); timeout = setTimeout(later, wait); if (callNow) func.apply(context, args); }; }; var myEfficientFn = debounce(function() { // 滚动中的真正的操做 }, 250); // 绑定监听 window.addEventListener('resize', myEfficientFn);
防抖函数确实不错,可是也存在问题,譬如图片的懒加载,我但愿在下滑过程当中图片不断的被加载出来,而不是只有当我中止下滑时候,图片才被加载出来。又或者下滑时候的数据的 ajax 请求加载也是同理。
这个时候,咱们但愿即便页面在不断被滚动,可是滚动 handler 也能够以必定的频率被触发(譬如 250ms 触发一次),这类场景,就要用到另外一种技巧,称为节流函数(throttling)。
节流函数,只容许一个函数在 X 毫秒内执行一次。
与防抖相比,节流函数最主要的不一样在于它保证在 X 毫秒内至少执行一次咱们但愿触发的事件 handler。
与防抖相比,节流函数多了一个 mustRun 属性,表明 mustRun 毫秒内,必然会触发一次 handler ,一样是利用定时器,看看简单的示例:
// 简单的节流函数 function throttle(func, wait, mustRun) { var timeout, startTime = new Date(); return function() { var context = this, args = arguments, curTime = new Date(); clearTimeout(timeout); // 若是达到了规定的触发时间间隔,触发 handler if(curTime - startTime >= mustRun){ func.apply(context,args); startTime = curTime; // 没达到触发间隔,从新设定定时器 }else{ timeout = setTimeout(func, wait); } }; }; // 实际想绑定在 scroll 事件上的 handler function realFunc(){ console.log("Success"); } // 采用了节流函数 window.addEventListener('scroll',throttle(realFunc,500,1000));
上面简单的节流函数的例子能够拿到浏览器下试一下,大概功能就是若是在一段时间内 scroll 触发的间隔一直短于 500ms ,那么能保证事件咱们但愿调用的 handler 至少在 1000ms 内会触发一次。
一个模块是能实现特定功能的文件,有了模块就能够方便的使用别人的代码,想要什么功能就能加载什么模块。
Commonjs:开始于服务器端的模块化,同步定义的模块化,每一个模块都是一个单独的做用域,模块输出,modules.exports,模块加载require()引入模块。
AMD:中文名异步模块定义的意思。
requireJS实现了AMD规范,主要用于解决下述两个问题。
1.多个文件有依赖关系,被依赖的文件须要早于依赖它的文件加载到浏览器
2.加载的时候浏览器会中止页面渲染,加载文件越多,页面失去响应的时间越长。
语法:requireJS定义了一个函数define,它是全局变量,用来定义模块。
requireJS的例子:
//定义模块 define(['dependency'], function(){ var name = 'Byron'; function printName(){ console.log(name); } return { printName: printName }; }); //加载模块 require(['myModule'], function (my){ my.printName(); }
requirejs定义了一个函数define,它是全局变量,用来定义模块:
define(id?dependencies?,factory)
在页面上使用模块加载函数:
require([dependencies],factory);
总结AMD规范:require()函数在加载依赖函数的时候是异步加载的,这样浏览器不会失去响应,它指定的回调函数,只有前面的模块加载成功,才会去执行。
由于网页在加载js的时候会中止渲染,所以咱们能够经过异步的方式去加载js,而若是须要依赖某些,也是异步去依赖,依赖后再执行某些方法。
因为篇幅有限,只能分享部分面试题,更多面试题及答案能够【点击我】阅读下载哦~无偿分享给你们,算是一个感恩回馈吧
事件代理( Event Delegation ),又称之为事件委托。是 JavaScript 中经常使用的绑定事件的经常使用技巧。顾名思义,“事件代理”便是把本来须要绑定的事件委托给父元素,让父元素担当事件监听的职务。事件代理的原理是DOM元素的事件冒泡。使用事件代理的好处是能够提升性能
能够大量节省内存占用,减小事件注册,好比在 table 上代理全部 td 的 click 事件就很是棒
能够实现当新增子对象时无需再次对其绑定
W3C 中定义事件的发生经历三个阶段:捕获阶段( capturing )、目标阶段
( targetin )、冒泡阶段( bubbling )
冒泡型事件:当你使用事件冒泡时,子级元素先触发,父级元素后触发
捕获型事件:当你使用事件捕获时,父级元素先触发,子级元素后触发
DOM 事件流:同时支持两种事件模型:捕获型事件和冒泡型事件
阻止冒泡:在 W3c 中,使用 stopPropagation() 方法;在IE下设置 cancelBubble =true
阻止捕获:阻止事件的默认行为,例如 click - a 后的跳转。在 W3c 中,使用preventDefault() 方法,在 IE 下设置 window.event.returnValue = false
建立一个空对象,而且 this 变量引用该对象,同时还继承了该函数的原型
属性和方法被加入到 this 引用的对象中
新建立的对象由 this 所引用,而且最后隐式的返回 this
Ajax 的原理简单来讲是在用户和服务器之间加了—个中间层( AJAX 引擎),经过XmlHttpRequest 对象来向服务器发异步请求,从服务器得到数据,而后用 javascript来操做 DOM 而更新页面。使用户操做与服务器响应异步化。这其中最关键的一步就是从服务器得到请求数据
Ajax 的过程只涉及 JavaScript 、 XMLHttpRequest 和 DOM 。 XMLHttpRequest 是ajax的核心机制
function deepClone(obj){ var newObj= obj instanceof Array ? []:{}; for(var item in obj){ var temple= typeof obj[item] == 'object' ? deepClone(obj[item]):obj[item]; newObj[item] = temple; } return newObj; }
ES5的经常使用的对象克隆的一种方式。注意数组是对象,可是跟对象又有必定区别,因此咱们一开始判断了一些类型,决定newObj是对象仍是数组~
var myNewAjax=function(url){ return new Promise(function(resolve,reject){ var xhr = new XMLHttpRequest(); xhr.open('get',url); xhr.send(data); xhr.onreadystatechange=function(){ if(xhr.status==200&&readyState==4){ var json=JSON.parse(xhr.responseText); resolve(json) }else if(xhr.readyState==4&&xhr.status!=200){ reject('error'); } } }) }
function ones(func){ var tag=true; return function(){ if(tag==true){ func.apply(null,arguments); tag=false; } return undefined } }
咱们假设这里有一个user对象,
(1)在ES5中能够经过Object.defineProperty来实现已有属性的监听
Object.defineProperty(user,'name',{ set:function(key,value){ } })
缺点:若是id不在user对象中,则不能监听id的变化
(2)在ES6中能够经过Proxy来实现
var user = new Proxy({},{ set:function(target,key,value,receiver){ } })
这样即便有属性在user中不存在,经过user.id来定义也一样能够这样监听这个属性的变化哦~
(1)while循环的方式
function sleep(ms){ var start=Date.now(),expire=start+ms; while(Date.now()<expire); console.log('1111'); return; }
执行sleep(1000)以后,休眠了1000ms以后输出了1111。上述循环的方式缺点很明显,容易形成死循环。
(2)经过promise来实现
function sleep(ms){ var temple=new Promise( (resolve)=>{ console.log(111);setTimeout(resolve,ms) }); return temple } sleep(500).then(function(){ //console.log(222) }) //先输出了111,延迟500ms后输出222
(3)经过async封装
function sleep(ms){ return new Promise((resolve)=>setTimeout(resolve,ms)); } async function test(){ var temple=await sleep(1000); console.log(1111) return temple } test(); //延迟1000ms输出了1111
(4).经过generate来实现
function* sleep(ms){ yield new Promise(function(resolve,reject){ console.log(111); setTimeout(resolve,ms); }) } sleep(500).next().value.then(function(){console.log(2222)})
获取一个对象的原型,在chrome中能够经过_proto_的形式,或者在ES6中能够经过Object.getPrototypeOf的形式。
那么Function.proto是什么么?也就是说Function由什么对象继承而来,咱们来作以下判别。
Function.__proto__==Object.prototype //false
Function.__proto__==Function.prototype//true
咱们发现Function的原型也是Function。
首先了解下浏览器的同源策略 同源策略 /SOP(Same origin policy) 是一种约定,由Netscape公司1995年引入浏览器,它是浏览器最核心也最基本的安全功能,若是缺乏了同源策略,浏览器很容易受到 XSS 、 CSFR 等攻击。所谓同源是指"协议+域名+端口"三者相同,即使两个不一样的域名指向同一个ip地址,也非同源
var script = document.createElement('script'); script.type = 'text/javascript'; // 传参并指定回调执行函数为onBack script.src = 'http://www.....:8080/login?user=admin&callback=onBack'; document.head.appendChild(script); // 回调执行函数 function onBack(res) { alert(JSON.stringify(res)); }
//父窗口:(http://www.domain.com/a.html) <iframe id="iframe" src="http://child.domain.com/b.html"></iframe> <script> document.domain = 'domain.com'; var user = 'admin'; </script> //子窗口:(http://child.domain.com/b.html) document.domain = 'domain.com'; // 获取父窗口中变量 alert('get js data from parent ---> ' + window.parent.user);
Object 是 JavaScript 中全部对象的父对象
数据封装类对象: Object 、 Array 、 Boolean 、 Number 和 String
其余对象: Function 、 Arguments 、 Math 、 Date 、 RegExp 、 Error
对象字面量: var obj = {};
构造函数: var obj = new Object();
Object.create(): var obj = Object.create(Object.prototype);
var arr = []; arr instanceof Array; // true
Array.isArray([]) //true Array.isArray(1) //false
var arr = []; arr.constructor == Array; //true
Object.prototype.toString.call([]) == '[object Array]' // 写个方法 var isType = function (obj) { return Object.prototype.toString.call(obj).slice(8,-1); //return Object.prototype.toString.apply([obj]).slice(8,-1); } isType([]) //Array