手写 JSONP 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 let jsonpFn = (url, params, callbackName ) => { let getUrl = (params ) => { let temp = '' let isParamsExist = url.indexOf ('?' ) > 0 for (let key in params) { if (params.hasOwnProperty (key)) { temp = isParamsExist ? `&${key} =${params[key]} ` : `?${key} =${params[key]} ` isParamsExist = true } } return temp + (url.indexOf ('?' ) < 0 ? `?callback=${callbackName} ` : `&callback=${callbackName} ` ) } return new Promise ((resolve, reject ) => { var script = document .createElement ('script' ) script.src = getUrl (params) document .body .appendChild (script) window [callbackName] = (data ) => { resolve (data) document .body .removeChild (script) } }) }jsonpFn ('http://127.0.0.1:3000' , {}, 'fn' ).then (res => { console .log (res) })
事件循环 浏览器是多线程 的, 但是js 是异步单线程 的
GUI 渲染线程(给浏览器画画的 DOM / BOM)
JS 引擎线程(web worker)
浏览器事件线程(onclick)
定时器触发线程
http 异步线程
eventLoop(时间循环)处理线程
前三个是常驻线程,后三个是有需求时才会工作
执行顺序 :先同后异 ,先微后宏
关于 setTimeout 的挂载时机需要注意
macro-task
主线程代码(script中的代码
setTimeout setInterval setImmediate
requestAnimationFrame
UI render
I / O流
ajax请求
micro-task
图解完整eventloop
易错题
浏览器点击这个按钮后 问控制台的输出
1 2 3 4 5 6 7 8 9 const btn = document .getElementById ('button' ) btn.addEventListener ('click' , () => { Promise .resolve ().then (() => console .log ('M1' )) console .log ('L1' ) }) btn.addEventListener ('click' , () => { Promise .resolve ().then (() => console .log ('M2' )) console .log ('L2' ) })
答案不是 L1 L2 M1 M2
,而是 L1 M1 L2 M2
、
不用浏览器点击,而是js
模拟点击,btn.click()
,此时答案变回 L1 L2 M1 M2
这是因为在不同的代码作用域(嵌套结构的代码)内,也是遵循先同后异,先微后宏的原则。因此第一个情况,可以理解为有两个宏任务,后者只有一个宏任务(或者理解为主线程任务)
关于 web worker 深入理解JavaScript中的Web Worker
Promise中的then、catch、finally 额,可能你看到下面👇这么多的 1,2,3 脾气就上来了,不是说好了本篇文章没什么屁话嘛,怎么还是 这么多一二三四。
😂,你要理解我的用心良苦啊,我这是帮你把知识点都列举出来,做个总结而已。当然,你也可以先不 看,先去做后面的题,然后再回过头来看这些,你就觉得这些点都好好懂啊,甚至都不需要记。
总结:
Promise 的状态一经改变就不能再改变。(见3.1)
.then 和 .catch 都会返回一个新的 Promise 。(上面的👆1.4证明了)
catch 不管被连接到哪里,都能捕获上层未捕捉过的错误。(见3.2)
在 Promise 中,返回任意一个非 promise 的值都会被包裹成 promise 对象,例如 return 2 会 被包装为 return Promise.resolve(2) 。
Promise 的 .then 或者 .catch 可以被调用多次, 但如果 Promise 内部的状态一经改变,并且有 了一个值,那么后续每次调用 .then 或者 .catch 的时候都会直接拿到该值。(见3.5)
.then 或者 .catch 中 return 一个 error 对象并不会抛出错误,所以不会被后续的 .catch 捕获。(见3.6)
.then 或 .catch 返回的值不能是 promise 本身,否则会造成死循环。(见3.7)
.then 或者 .catch 的参数期望是函数,传入非函数则会发生值透传 。(见3.8)
.then 方法是能接收两个参数的,第一个是处理成功的函数,第二个是处理失败的函数,再某些时 候你可以认为 catch 是 .then 第二个参数的简便写法。(见3.9)
.finally 方法也是返回一个 Promise ,他在 Promise 结束的时候,无论结果为 resolved 还是 rejected ,都会执行里面的回调函数。
节流和防抖 用一句话总结防抖和节流的区别:防抖是将多次执行变为最后一次执行,节流是将多次执行变为每隔一段时间执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 const throttle = (fn, delay ) => { let start = 0 ; return function ( ) { let now = new Date () let context = this if (now - start >= delay) { fn.apply (context, arguments ) start = now } } }const debounce = (fn, delay ) => { let timer return function ( ) { let args = arguments timer && clearTimeout (timer) timer = setTimeout (() => { fn.apply (this , args) timer = null }, delay) } }
单例模式 单例模式 (Singleton) 的实现在于保证一个特定类只有一个实例,第二次使用同一个类创建新对象的时候,应该得到与第一次创建对象完全相同的对象
可以看看这个文章,讲的很详细: js 设计模式之单例模式
简单单例模式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 let Singleton = function (name ) { this .name = name }Singleton .instance = null Singleton .prototype .getName = () { return this .name }Singleton .getInstance = function (name ) { if (!this .instance ) { this .instance = new Singleton (name) } return this .instance }
实现思想 :将第一次创建的实例进行保存,之后再次创建前判断是否已经创建,如果之前创建过则返回已经保存的实例,否则创建一个实例,将实例创建和判断封装到了一个 getInstance 函数中,这种方式相对简单,但增加了类的“不透明性”,用一个函数来获取一个实例,而不是以往通过 new 来创建。
透明单例模式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 let CreateDiv = function ( ) { let instance = null ; let CreateDiv = function ( html ) { if (instance) { return instance } this .html = html this .init () instance = this return instance } CreateDiv .prototype .init = function ( ) { let div = document .createElement ('div' ) div.innerHTML = this .html document .body .appendChild (div) } return CreateDiv }let a = new CreateDiv ('hello' )let b = new CreateDiv ('world' )console .log (a === b)
实现思想 :该方式与前一实现不同的在于用 new 来创建实例,运用了闭包来保存实例标识,从而达到只能创建唯一实例,但是如果有一天想创建多个 div 实例,该代码就不实用了。并且也不满足单一职责的要求
代理单例模式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Creatediv { constructor (html ) { this .init (html); } init (html ) { const divObj = document .createElement ("div" ); divObj.innerHTML = html; document .body .appendChild (divObj); } }const SingletonProxy = (function ( ) { let instance = null ; return function (html ) { if (!instance) { instance = new Creatediv (html); } return instance; }; })();const a = new SingletonProxy ("<span>我是一个span标签1</span>" );const b = new SingletonProxy ("<span>我是一个span标签2</span>" );console .log (a === b);
功能拆解 : 把 CreateDiv 独立出来, 而负责管理单例的逻辑移到代理类中,保证只有一个对象。 这样一来,CreateDiv 就变成了一个普通的类,它跟 代理 组合起来可以达到单例模式的效果。
惰性单例模式 例如创建遮罩层,多个页面共同调用同一个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 var singleton = function ( ) { var instance; return function (fn ) { return instance || (instance = fn.apply (this , arguments )); } }();var createMask = function ( ){ var mask = document .createElement ('div' ); mask.style .position = 'fixed' ; document .body .appendChild (mask); mask.onclick = function ( ){ this .style .display = 'none' ; } return mask; };var createLogin = function ( ) { var login = document .createElement ('div' ); login.style .position = 'fixed' ; login.innerHTML = 'login it' ; document .body .appendChild (login); return login; };document .getElementById ('btn' ).onclick = function ( ) { var oMask = singleton (createMask); oMask.style .display = 'block' ; var oLogin = singleton (createLogin); oLogin.style .display = 'block' ; var w = parseInt (oLogin.clientWidth ); var h = parseInt (oLogin.clientHeight ); }
又例如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 let getSingle = function ( fn ) { let result; return function ( ) { return result || ( result = fn.apply (this , arguments )) } }()let bindEvent = function ( ) { document .getElementById ('div' ).addEventListener ('click' ,()=> { console .log ('我出现了' ) }); }let render = function ( ) { console .log ('开始渲染!' ); getSingle (bindEvent) }render ();render ();render ();
该方式是 js 中常用的单例实现方法,再页面重复渲染三次时,监听事件只绑定了一次,减少了开销。
惰性体现 :惰性体现在实例实在需要时创建,并不会在页面加载好就创建。
实现思想 :首先利用闭包和高阶函数封装了一个返回单一实例的函数,其参数就是一个只执行一次的函数,可以随时改变单例的作用,大大增加了代码的可复用性。
new 的优先级问题 https://www.jianshu.com/p/412ccd8c386e