No abstract.
单例模式^ob_scope_closure 单例模式避免了重复实例化带来的内存开销。 // 单例模式 function Singleton() { this.data = "singleton"; } Singleton.getInstance = (function () { var instance; return function () { if (instance) { return instance; } else { instance = new Singleton(); return instance; } }; })(); var sa = Singleton.getInstance(); var sb = Singleton.getInstance(); console.log(sa === sb); // true console.log(sa.data); // 'singleton' References Learning JavaScript Design Patterns
Closure 闭包:有权访问另一个函数作用域中变量的函数。 一个作用域可以访问另外一个函数内部的局部变量,就产生闭包,局部变量在函数执行完后不会被立即销毁,而是等所有函数调用完该变量后再销毁。 闭包的主要作用:延伸变量的作用范围。 过度使用闭包会造成内存泄漏。 应用 模拟类私有属性 // 模拟私有属性 function getGeneratorFunc() { var _name = "John"; var _age = 22; return function () { return { getName: function () { return _name; }, getAge: function () { return _age; }, }; }; } var obj = getGeneratorFunc()(); obj.getName(); // John obj.getAge(); // 22 obj._age; // undefined 柯里化(currying) 柯里化(currying),是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。 柯里化的优势之一就是参数的复用,它可以在传入参数的基础上生成另一个全新的函数,如函数bind方法的实现。 // bind Function.prototype.myBind = function (context = window) { if (typeof this !== "function") throw new Error("Error"); let selfFunc = this; let args = [...arguments].slice(1); return function F() { // 因为返回了一个函数,可以 new F(),所以需要判断 if (this instanceof F) { return new selfFunc(...args, arguments); } else { // bind 可以实现类似这样的代码 f.bind(obj, 1)(2),所以需要将两边的参数拼接起来 return selfFunc.apply(context, args.concat(arguments)); } }; }; // Example function typeOf(value) { return function (obj) { const toString = Object.prototype.toString; const map = { "[object Boolean]": "boolean", "[object Number]": "number", "[object String]": "string", "[object Function]": "function", "[object Array]": "array", "[object Date]": "date", "[object RegExp]": "regExp", "[object Undefined]": "undefined", "[object Null]": "null", "[object Object]": "object", }; return map[toString.call(obj)] === value; }; } var isNumber = typeOf("number"); var isFunction…
This directory is in the github repository. All new posts will be fetched from the dir.
JS Engine
JS Tips
JS Function
JS 日期格式化
JS 数组
JavaScript
JS 引擎加载脚本文件后:语法分析、预编译、解释执行。 匿名函数不参与预编译,只有在解释执行阶段才会进行变量初始化。 JS 执行线程 javascript 引擎执行的过程的理解--执行阶段 JS 是单线程的是指永远只有 JS 引擎线程在执行 JS 脚本程序,其他的三个线程只协助,不参与代码解析与执行。 JS 引擎线程:也称为 JS 内核,负责解析执行 Javascript 脚本程序的主线程(例如 V8 引擎)。 事件触发线程:归属于浏览器内核进程,不受 JS 引擎线程控制。主要用于控制事件(例如鼠标,键盘等事件),当该事件被触发时候,事件触发线程就会把该事件的处理函数推进事件队列,等待 JS 引擎线程执行。 定时器触发线程:主要控制计时器 setInterval 和延时器 setTimeout,用于定时器的计时,计时完毕,满足定时器的触发条件,则将定时器的处理函数推进事件队列中,等待 JS 引擎线程执行。(注:W3C 在 HTML 标准中规定 setTimeout 低于 4ms 的时间间隔算为 4ms。) HTTP 异步请求线程:通过 XMLHttpRequest 连接后,通过浏览器新开的一个线程,监控 readyState 状态变更时,如果设置了该状态的回调函数,则将该状态的处理函数推进事件队列中,等待 JS 引擎线程执行。 注:浏览器对同一域名请求的并发连接数是有限制的,Chrome 和 Firefox 限制数为 6 个,ie8 则为 10 个 JS 异步执行机制——Even Loop JS 宏任务、微内核 Tasks, microtasks, queues and schedules 内存管理 内存泄漏 可能造成内存泄漏的原因 闭包的使用 全局变量的无意创建 DOM 元素绑定事件未随 DOM 元素的移除而注销 内存泄漏排查 内存泄露的排查手段 内存泄漏的解决方案 使用严格模式,避免不经意间的全局变量泄露 关注 DOM 生命周期,在销毁阶段记得解绑相关事件,或者可以使用事件委托的手段统一处理事件,减少由于事件绑定带来的额外内存开销 避免过度使用闭包 引申 setTimeout 与 setInterval 区别 防抖和节流
任何变量,如果未经声明就赋值,此变量是属于 window 的属性,而且不会做变量提升。(注意,无论在哪个作用域内赋值) function foo() { var a = (b = 100); // a和b的区别 } defer and async The best thing to do to speed up your page loading when using scripts is to put them in the head, and add a defer attribute to your script tag. Efficiently load JavaScript with defer and async
函数声明的方式 function关键字 函数表达式(匿名函数)var 变量名 = function([形参1,形参2...形参N]){} 构造函数var 变量名/函数名 = new Function('形参1', '形参2', '函数体'); 函数调用 func() func.call() (function(){})(); new func() 事件调用 定时调用 函数类数组实参arguments 函数调用隐含传入上下文对象this和封装实参的对象arguments 在递归调用中用arguments.callee代替自身函数名可以接触函数体内代码与函数名的耦合,但会导致函数体内的this对象被更改,同时访问arguments是个很昂贵的操作,因为它是个很大的对象,每次递归调用时都需要重新创建,影响现代浏览器的性能,还会影响闭包。 函数预编译 函数预编译,发生在函数执行的前一刻。 JS 预编译、变量提升 创建 Active Object 对象,即执行期上下文。 寻找函数的形参和变量声明,将变量和形参名作为 AO 对象的属性名,值设定为 undefined. 将形参和实参相统一,即更改形参后的 undefined 为具体的形参值。 寻找函数中的函数声明,将函数名作为 AO 属性名,值为函数体。 JS 函数和变量声明提升 函数声明提升优先于变量声明 函数初始化也会提升 console.log(a); // [Function: a] var a = 1; console.log(a); // 1 function a() {} console.log(a); // 1 function b(a) { console.log(a); // [Function: a] var a = 2; console.log(a); // 2 function a() {} console.log(a); // 2 } b(3); this 指向 以函数的形式(包括普通函数、定时器函数、立即执行函数)调用时,this 的指向永远都是 window。 以方法的形式调用时,this 指向调用方法的那个对象 以构造函数的形式调用时,this 指向实例对象 以事件绑定函数的形式调用时,this 指向绑定事件的对象 使用 call 和 apply 调用时,this 指向指定的那个对象 箭头函数中 this 的指向会继承外层函数调用的 this 绑定(无论 this 绑定到什么) var name = "window"; var obj = { name: "obj", arrowFunc: () => { console.log(this, this.name); }, func: function () { console.log(this, this.name); }, }; function func() { console.log(this, this.name); } // Window "window" || Object "obj" || Window "window" func() || obj.func() || obj.arrowFunc(); call, apply, bind call func.call(thisArg, ...argArray); 调用一个函数,同时可以改变这个函数内部的 this 指向 实现继承 function Father(myName, myAge) { this.name = myName; this.age = myAge; }…
JS日期格式化转换方法 Date.prototype.format = function(fmt) { var o = { "M+" : this.getMonth()+1, //月份 "d+" : this.getDate(), //日 "h+" : this.getHours(), //小时 "m+" : this.getMinutes(), //分 "s+" : this.getSeconds(), //秒 "q+" : Math.floor((this.getMonth()+3)/3), //季度 "S" : this.getMilliseconds() //毫秒 }; if(/(y+)/.test(fmt)) { fmt=fmt.replace(RegExp.$1, (this.getFullYear()+"").substr(4 - RegExp.$1.length)); } for(var k in o) { if(new RegExp("("+ k +")").test(fmt)){ fmt = fmt.replace(RegExp.$1, (RegExp.$1.length==1) ? (o[k]) : (("00"+ o[k]).substr((""+ o[k]).length))); } } return fmt; } 龙恩0707 JS日期格式化转换方法 References Moment.js 轻量级的JavaScript时间库
Fundamental ECMAScript中new Array(len)的操作 判断 len 是否为合法数字(小于 2^32 - 1 的正整数),如果不是则抛出错误; 创建一个 JavaScript Array 实例; 将这个实例对象的 length 属性设置为入参的值; 但该数组此时并没有包含任何实际的元素,而且不能理所当然地认为它包含 len 个值为 undefined 的元素 More empty和undefined的区别 导致数组的map、some、filter、includes、for in、for of、findIndex、sort等方法的差异 稀疏数组、密集数组的互相转换 V8 访问对象有两种模式:字典模式 和 快速模式 JavaScript 之稀疏数组与密集数组 稀疏数组与密集数组 伪数组(ArrayLike) 按索引方式储存数据 length不会动态变化 伪数组的原型链中没有 Array.prototype,因此不具有push、forEach等方法 常见的如arguments、DOM children 元素集。 // 伪数组转真数组 Array.prototype.slice.call(ArrayLike); [].slice.call(ArrayLike); Array.from(ArrayLike); sort方法 默认按 Unicode 编码排序 自定义排序规则:return 大于 0 的值——元素交换位置,return 小于 0 的值——元素位置不变,return 等于 0 的值——不交换位置 // 冒泡排序 arr.sort((a, b) => a - b); forEach会改变原数组吗,map()会吗 :question: arr.reduce(function (previousValue, currentValue, currentIndex, arr) {}, initialValue); e.g. 统计元素出现的次数、找最大值等 清空数组 array.splice(0); //方式1:删除数组中所有项目 array.length = 0; //方式2:length属性可以赋值,在其它语言中length是只读 array = []; //方式3:推荐 join的应用 相比字符串拼接 由于字符串的不变性,str 拼接过多的话,性能差,且容易导致内存溢出(很多个 str 都堆放在栈里)
TODO [ ] 理解浏览器工作原理 [ ] 防抖和节流