let a = 100; typeof a;//number a = undefined; typeof a;//undefined a = null; typeof a;//object a instanceof Object;//false. instanceof 运算符检测 Object.prototype 是否存在于 a 的原型链上
5 == true;//false。true会先转换为1,false转换为0 '12' + 1;//'123'。数字先转换成字串 '12' - 1;//11。字串先转换成数字 [] == false;//true。数组转换为其余类型时,取决于另外一个比较者的类型 [] == 0;//true [2] == '2';//true [2] == 2;//true [] - 2;//-2 12 + {a:1};//"12[object Object]"。这里对象先被转换成了字串 null == false;//false null == 0;//false null - 1;//-1
> a = {"name":"cenze","age":28} > JSON.stringify(a,null,2) { "name": "cenze", "age": 28 }
let str = '23abGdH4d4Kd'; str = str.replace(/([a-z]*)([A-Z]*)/g, function(match, $1 , $2){return $1.toUpperCase() + $2.toLowerCase()});//"23ABgDh4D4kD" str = 'web-application-development'; str = str.replace(/-([a-z])/g, (replace)=>replace[1].toUpperCase());//"webApplicationDevelopment"(驼峰转换)
str = Math.random().toString(36).substring(2)
3.2 / 1.7 | 0 = 1; ~~(3.2 / 1.7) = 1;//~~可还原原数 3.2 / 1.7 >> 0 = 1;
3.2 / 1.7 ^ 0 = 1;
-3.2 / 1.7 & 1 = 1;//& 1操做没法保持符号不变 if (!~str.indexOf(substr)) {//~按位取反,等价于符号取反而后减一 //do some things }
let a = 2, b = 2.3; //方法一:Math.round() Math.round(a) === a;//true Math.round(b) === b;//false //方法二:模1运算 typeof a == "number" && (a % 1) === 0;//true typeof b == "number" && (b % 1) === 0;//false //方法三:ES6 Number.isInteger() Number.isInteger(a);//true Number.isInteger(b);//false //方法四:parseInt() parseInt(a, 10) === a;//true parseInt(b, 10) === b;//false //方法五:异或0运算 a ^ 0 === a;//true b ^ 0 === b;//false
true && 0;//0
false && 2;//false
1 && 2;//2 1 || 2;//1 0 || 1;//1
![] || !{} || 3;//3
[] == ![];//true
!NaN && !undefined && !null && !0 && !'' && 6;//6
//方法一:经过中间数组完成交换 let a = 1, b = 2; a = [b, b = a][0]; //方法二:经过加减运算完成交换 let a = 1, b = 2; a = a + b; b = a - b; a = a - b; //方法三:经过加减运算完成交换 let a = 1, b = 2; a = a - b; b = a + b; a = b - a; //方法四:经过两次异或还原完成交换。另外,这里不会产生溢出 let a = 1, b = 2; a = a ^ b; b = a ^ b; a = a ^ b; //方法五:经过乘法运算完成交换 let a = 1, b = 2; a = b + (b = a) * 0; //方法六:经过ES6解构赋值 let a = 1, b = 2; [a, b] = [b, a];
/、%、**运算符:html
5 / 3;//1.6666666666666667 4.53 % 2;//0.5300000000000002 5 % 3;//2 2 ** 3;//8,相比于Math.pow(2,3)要简洁多了
compareFunction
可选,用来指定按某种顺序进行排列的函数。若是省略,元素按照转换为的字符串的诸个字符的Unicode位点进行排序。好比:
[1, 10, 21, 2].sort();//[1, 10, 2, 21] [1, 10, 21, 2].sort((a, b) => a - b);//[1, 2, 10, 21]
// 须要被排序的数组 var list = ['Delta', 'alpha', 'CHARLIE', 'bravo']; // 对须要排序的数字和位置的临时存储 var mapped = list.map(function(el, i) { return { index: i, value: el.toLowerCase() }; }) // 按照多个值排序数组 mapped.sort(function(a, b) { return +(a.value > b.value) || +(a.value === b.value) - 1; }); // 根据索引获得排序的结果 var result = mapped.map(function(el){ return list[el.index]; });
//方法一:先排序后去重 Array.prototype.unique = function(){ this.sort(); //先排序 let res = [this[0]]; for(let i = 1; i < this.length; i++){ if(this[i] !== res[res.length - 1]){ res.push(this[i]); } } return res; } //方法二:利用对象属性惟一性避免重复元素 Array.prototype.unique = function(){ let res = [], deduplication = {}; for(let i = 0; i < this.length; i++){ if(!deduplication[this[i]]){ res.push(this[i]); deduplication[this[i]] = 1; } } return res; } //方法三:利用过滤函数递代去重处理,该方法可保持数组原序不变 Array.prototype.unique = function () { let self = this, res = [], target; while (self.length) { target = self.shift(); res.push(target); self = self.filter(item => target !== item); } return res; } //方法四:利用ES6新增长的Set数据类型作中间转换可去重 Array.prototype.unique = function () { return [...new Set(this)]; }
Math.max.apply(null, [2,5,30,1,20]);//30 Math.min.apply(null, [2,5,30,1,20]);//1
Math.min(...[2,5,30,1,20]);//1.ES6
1. encodeURI()对 URI 进行完整的编码,但不会对“;/?:@&=+$,#”这些在 URI 中具备特殊含义的 ASCII 标点符号进行编码,同时也不编码“- _ . ! ~ * ' ( )”这些ASCII字符。但decodeURI("%27") == unescape("%27") == "'"; 2. encodeURIComponent()可把字符串做为 URI 组件进行编码,会对“;/?:@&=+$,#”这些在 URI 中具备特殊含义的 ASCII 标点符号进行编码,但不编码“- _ . ! ~ * ' ( )”这些ASCII字符。
setTimeout
函数会在一个时间段过去后在队列中添加一个消息,这个时间段做为函数的第二个参数被传入。若是队列中没有其它消息,消息会被立刻处理;若是有其它消息,setTimeout
消息必须等待其它消息处理完。所以第二个参数仅仅表示最少的时间而非确切的时间。
浏览器的内核是多线程的,它们在内核控制下相互配合以保持同步,一个浏览器至少实现三个常驻线程:JavaScript引擎线程、GUI渲染线程、事件触发线程
JavaScript引擎线程:一直等待着任务队列中任务的到来,而后按优先级加以处理。浏览器不管何时都只有一个JavaScript线程在运行JavaScript程序。 GUI渲染线程:负责渲染浏览器界面,当界面须要重绘(Repaint)或因为某种操做引起回流(Reflow)时,该线程就会执行。但须要注意,GUI渲染线程与JavaScript引擎是互斥的,当JavaScript引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到JavaScript引擎空闲时当即被执行。 事件触发线程:在一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待JavaScript引擎的处理。这些事件可来自JavaScript引擎当前执行的代码块如setTimeout、也可来自浏览器内核的其余线程如鼠标点击、Ajax异步请求等,但因为JavaScript的单线程关系全部这些事件都得排队等待JavaScript引擎处理(当线程中没有执行任何同步代码的前提下才会执行异步代码)。
1. setTimeout()和setInterval()共用一个编号池,clearTimeout()和 clearInterval() 在技术上能够互换;可是为了不混淆,不要混用取消定时函数 2. 须要注意的是setTimeout()和setInterval()延迟执行的函数或代码中的this会指向一个错误的值,由于那些函数或代码是在一个分离的环境空间里被执行的,这些代码中包含的 this 关键字在非严格模式会指向 window (或全局)对象,严格模式下为 undefined
3. IE < 9环境下setTimeout() 或者 setInterval()都不支持传递额外的参数,能够考虑自行从新定义兼容代码同名取代这两个函数
4. setTimeout()的最小延迟时间与浏览器及操做系统有关,但不是0。在John Resig的《Javascript忍者的秘密》中:Browsers all have a 10ms minimum delay on OSX and a(approximately) 15ms delay on Windows.另外Firefox中定义的最小时间间隔(DOM_MIN_TIMEOUT_VALUE)是10毫秒,HTML5定义的最小时间间隔是4毫秒
5. 延迟只是代表可能的最小延迟时间,而不保证确切延迟时间。若是任务队列很长,耗时不少,最后延迟时间可能远远超出参数中指定的值
function ClassA(sColor) { this.color = sColor; } ClassA.prototype.sayColor = function () { alert(this.color); }; function ClassB(sColor, sName) { ClassA.call(this, sColor); this.name = sName; } ClassB.prototype = new ClassA(); ClassB.prototype.sayName = function () { alert(this.name); }; var objA = new ClassA("blue"); var objB = new ClassB("red", "John"); objA.sayColor(); //输出 "blue" objB.sayColor(); //输出 "red" objB.sayName(); //输出 "John"
var obj = { foo: 1, get bar() { return 2; } }; var copy = Object.assign({}, obj); // { foo: 1, bar: 2 } // copy.bar的值来自obj.bar的getter函数的返回值
call、apply和bind:call和apply的用途都是用来调用当前函数,且用法类似,它们的第一个参数都用做 this 对象,但其余参数call要分开列举,而apply要以一个数组形式传递;bind给当前函数定义预设参数后返回这个新的函数(初始化参数改造后的原函数拷贝),其中预设参数的第一个参数是this指定(当使用new
操做符调用新函数时,该this指定无效),新函数调用时传递的参数将位于预设参数以后与预设参数一块儿构成其所有参数,bind
最简单的用法是让一个函数不论怎么调用都有一样的 this
值。下边的list()也称偏函数(Partial Functions):node
function list() { return Array.prototype.slice.call(arguments); } var list1 = list(1, 2, 3); // [1, 2, 3] // Create a function with a preset leading argument var leadingThirtysevenList = list.bind(undefined, 37); var list2 = leadingThirtysevenList(); // [37] var list3 = leadingThirtysevenList(1, 2, 3); // [37, 1, 2, 3]
Memoization技术: 替代函数中太多的递归调用,是一种能够缓存以前运算结果的技术,这样咱们就不须要从新计算那些已经计算过的结果。In computing, memoization or memoisation is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again. Although related to caching, memoization refers to a specific case of this optimization, distinguishing it from forms of caching such as buffering or page replacement. In the context of some logic programming languages, memoization is also known as tabling.web
function memoizer(fundamental, cache) { let cache = cache || {}, shell = function(arg) { if (! (arg in cache)) { cache[arg] = fundamental(shell, arg); } return cache[arg]; }; return shell; }
let singleton = function(){ let obj; return function(){ return obj || (obj = new Object()); } }();
let variables = { key1: 'value1', key2: 'value2' }, fnBody = 'return this.key1 + ":" + this.key2', fn = new Function(fnBody); console.log(fn.apply(variables));
var currying = function (fn) { var _args = []; return function () { if (arguments.length === 0) { return fn.apply(this, _args); } Array.prototype.push.apply(_args, [].slice.call(arguments)); return arguments.callee; } }; var add=function () { var total = 0; for (var i = 0, c; c = arguments[i++];) { total += c; } return total; }; var sum = currying(add); sum(100,200)(300);//ƒ () { ... } sum(400);//ƒ () { ... } sum();//1000
Function.prototype.uncurrying = function (){ var _this = this; return function (){ return Function.prototype.call.apply(_this,arguments); } }; function fn(){ console.log(`Hello ${this.value},${[].slice.call(arguments)}`); } var uncurryfn = fn.uncurrying(); uncurryfn({value:'World'},'Javascript'); //Hello World,Javascript
DocumentFragment: Roughly speaking, a DocumentFragment is a lightweight container that can hold DOM nodes. 在维护页面DOM树时,使用文档片断document fragments 一般会起到优化性能的做用shell
let ul = document.getElementsByTagName("ul")[0], docfrag = document.createDocumentFragment(); const browserList = [ "Internet Explorer", "Mozilla Firefox", "Safari", "Chrome", "Opera" ]; browserList.forEach((e) => { let li = document.createElement("li"); li.textContent = e; docfrag.appendChild(li); }); ul.appendChild(docfrag);
forEach()遍历:另外,适当时候也能够考虑使用for 或 for ... in 或 for ... of 语句结构后端
1. 数组实例遍历: arr.forEach(function(item, key){ //do some things }) 2. 非数组实例遍历: Array.prototype.forEach.call(obj, function(item, key){ //do some things }) 3. 非数组实例遍历: Array.from(document.body.childNodes[0].attributes).forEach(function(item, key){ //do some things. Array.from()是ES6新增长的 })
DOM事件流:Graphical representation of an event dispatched in a DOM tree using the DOM event flow.If the bubbles
attribute is set to false, the bubble phase will be skipped, and if stopPropagation()
has been called prior to the dispatch, all phases will be skipped.数组
(1) 事件捕捉(Capturing Phase):event经过target的祖先从window传播到目标的父节点。IE不支持Capturing
(2) 目标阶段(Target Phase):event到达event的target。若是事件类型指示事件不冒泡,则event在该阶段完成后将中止
(3) 事件冒泡(Bubbling Phase):event以相反的顺序在目标祖先中传播,从target的父节点开始,到window结束浏览器
事件绑定:缓存
1. Tag事件属性绑定:<button onclick="do some things"></button> 2. 元素Node方法属性绑定:btnNode.onclick = function(){//do some things} 3. 注册EventListener:btnNode.addEventListener('click', eventHandler, bubble);另外,IE下实现与W3C有点不一样,btnNode.attachEvent(‘onclick’, eventHandler)
递归与栈溢出(Stack Overflow):递归很是耗费内存,由于须要同时保存成千上百个调用帧,很容易发生“栈溢出”错误;而尾递归优化后,函数的调用栈会改写,只保留一个调用记录,但这两个变量(func.arguments、func.caller,严格模式“use strict”会禁用这两个变量,因此尾调用模式仅在严格模式下生效)就会失真。在正常模式下或者那些不支持该功能的环境中,采用“循环”替换“递归”,减小调用栈,就不会溢出多线程
function Fibonacci (n) { if ( n <= 1 ) {return 1}; return Fibonacci(n - 1) + Fibonacci(n - 2); } Fibonacci(10); // 89 Fibonacci(50);// 20365011074,耗时10分钟左右才能出结果 Fibonacci(100);// 这里就一直出不告终果,也没有错误提示 function Fibonacci2 (n , ac1 = 1 , ac2 = 1) { if( n <= 1 ) {return ac2}; return Fibonacci2 (n - 1, ac2, ac1 + ac2); } Fibonacci2(100) // 573147844013817200000 Fibonacci2(1000) // 7.0330367711422765e+208 Fibonacci2(10000) // Infinity. "Uncaught RangeError:Maximum call stack size exceeded"
可见,经尾递归优化以后,性能明显提高。若是不能使用尾递归优化,可以使用蹦床函数(trampoline)将递归转换为循环:蹦床函数中循环的做用是申请第三者函数来继续执行未完成的任务,而保证本身函数能够顺利退出。另外,这里的第三者和本身多是同一函数定义闭包
function trampoline(f) { while (f && f instanceof Function) { f = f(); } return f; } //改造Fibonacci2()的定义 function Fibonacci2 (n , ac1 = 1 , ac2 = 1) { if( n <= 1 ) {return ac2}; return Fibonacci2.bind(undefined, n - 1, ac2, ac1 + ac2); } trampoline(Fibonacci2(100));//573147844013817200000
预取与延迟加载:预取如提早加载图片,当用户须要查看时可直接从本地渲染,而不须要去服务端下载,它保证了图片快速、无缝地发布,让用户在浏览时得到更好的用户体验;延迟加载如先加载小图或示例图做为占位图,只有进入浏览器可视区域内时,才加载完整图片并显示出来,在页面图片数量多且比较大的时候很是有用
mousemove
事件,执行屡次监听函数;但若是对监听函数用 100ms 的Debounce去弹跳后,那么浏览器只会在第 3.1s 的时候执行这个监听函数一次。下边是Debounce的实现:
/** * * @param fn {Function} 该函数定义了事件触发后的业务处理逻辑 * @param delay {Number} 延迟时间,单位是毫秒(ms) * * @return {Function} 目标事件将要触发执行的监听函数 */ function debounce(fn, delay) { // 定时器,用来 setTimeout let timer // 返回一个“去弹跳”了的监听函数 return function () { // 保存函数调用时的上下文和参数,传递给 fn let context = this, args = arguments; // 先清除定时器,保证 delay 毫秒内 fn 不执行屡次 clearTimeout(timer) // 用户中止当前连续操做后的第 delay 毫秒执行 fn timer = setTimeout(function () { fn.apply(context, args) }, delay) } }
/** * * @param fn {Function} 该函数定义了事件触发后的业务处理逻辑 * @param delay {Number} 执行间隔,单位是毫秒(ms) * * @return {Function} 目标事件将要触发执行的监听函数 */ function throttle(fn, threshhold) { // 记录上次执行的时间 let last, // 定时器 timer; // 默认间隔为 250ms threshhold || (threshhold = 250);
// 返回一个“经节流”了的监听函数 return function () { // 保存函数调用时的上下文和参数,传递给 fn let context = this, args = arguments, now = +new Date(); // 若是距离上次执行 fn 函数的时间小于 threshhold,那么就放弃 // 执行 fn,并从新计时 if (last && now < last + threshhold) { clearTimeout(timer) timer = setTimeout(function () { last = now fn.apply(context, args) }, threshhold) } else {// 在连续动做刚开始或超过threshhold间隔时当即执行 fn last = now fn.apply(context, args) } } }