一道异步好题
setTimeout(() => { console.log('异步1任务time1'); new Promise(function (resolve, reject) { console.log('异步1宏任务promise'); setTimeout(() => { console.log('异步1任务time2'); }, 0); resolve(); }).then(function () { console.log('异步1微任务then') }) }, 0); console.log('主线程宏任务'); setTimeout(() => { console.log('异步2任务time2'); }, 0); new Promise(function (resolve, reject) { console.log('宏任务promise'); // reject(); resolve(); }).then(function () { console.log('微任务then') }).catch(function () { console.log('微任务catch') }) console.log('主线程宏任务2');最后结果为:
宏任务微任务的问题 脑海中一定要有那三个数据结构:stack macrotask queue microtask queue
例中需注意第9行的then是在第14行的setTimeout之前执行的,而第5行的setTimeout在第14行之后执行。
例中需注意第9行的then是在第14行的setTimeout之前执行的,而第5行的setTimeout在第14行之后执行。
也就是在一个异步任务代码块中,会先执行完所有同步语句和微任务语句,然后去执行整个代码中的其他同级别的异步任务(在macrotask queue里排队呢)
而第5行的setTimeout因是第二层异步语句,会被放到之后才执行。
JSONP
多看几次 不要再在这个上面犯错了
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)); }
W3C中定义了事件的发生经历三个阶段:
1:捕获阶段 ---> 2:目标阶段 ---> 3:冒泡阶段
document ---> target目标 ----> document
通过addEventListener的第三个参数来设置:
document ---> target目标 ----> document
通过addEventListener的第三个参数来设置:
true 表示该元素在事件的“捕获阶段”(由外往内传递时)响应事件
false 表示该元素在事件的“冒泡阶段”(由内向外传递时)响应事件 【默认是flase】
冒泡型事件:当你使用事件冒泡时,子级元素先触发,父级元素后触发 阻止冒泡使用 stopPropagation()
捕获型事件:当你使用事件捕获时,父级元素先触发,子级元素后触发 阻止捕获使用 preventDefault()
事件执行顺序:判断的关键是否目标元素:
- 非目标元素:根据W3C的标准执行:捕获->目标元素->冒泡(不依据事件绑定顺序)
- 目标元素:依据事件绑定顺序:先绑定的事件先执行(不依据捕获冒泡标准)
- 最终顺序:父元素捕获->目标元素事件1->目标元素事件2->子元素捕获->子元素冒泡->父元素冒泡
- 注意:子元素事件执行前提 事件确实“落”到子元素布局区域上,而不是简单的具有嵌套关系
你觉得jQuery源码有哪些写的好的地方
- jquery源码封装在一个匿名函数的自执行环境中,有助于防止变量的全局污染,然后通过传入window对象参数,可以使window对象作为局部变量使用,好处是当jquery中访问window对象的时候,就不用将作用域链退回到顶层作用域了,从而可以更快的访问window对象。同样,传入undefined参数,可以缩短查找undefined时的作用域链
- jquery将一些原型属性和方法封装在了jquery.prototype中,为了缩短名称,又赋值给了jquery.fn,这是很形象的写法
- 有一些数组或对象的方法经常能使用到,jQuery将其保存为局部变量以提高访问速度
- jquery实现的链式调用可以节约代码,所返回的都是同一个对象,可以提高代码效率
Node的应用场景
特点:
|
优点:高并发(最重要的优点) |
缺点: 1、只支持单核CPU,不能充分利用CPU 2、可靠性低,一旦代码某个环节崩溃,整个系统都崩 |
es6模块 CommonJS、AMD、CMD
commonJS | CommonJS是服务器端模块的规范 加载模块是同步的 demo: //模块定义 myModel.js var ...... module.exports = { printName: printName} //加载模块 var nameModule = require('./myModel.js'); nameModule.printName();在浏览器中会出现堵塞情况 | |
AMD | 异步模块定义 解决了同步加载的问题 需要定义回调define方式 demo: // 定义模块 myModule.js define(['dependency'], function(){ var name = 'Byron'; function printName(){ console.log(name); } return { printName: printName }; }); // 加载模块 require(['myModule'], function (my){ my.printName(); }); | AMD和CMD的对比: AMD在加载完成定义好的模块 就会立即执行,所有执行完成后,遇到require才会执行主逻辑。(提前加载) CMD在加载完成定义好的模块,仅仅是下载不执行,在遇到require才会执行对应的模块。(按需加载) |
CMD | 通用模块定义 和AMD解决同样的问题 但运行机制不同 demo: // 定义模块 myModule.js define(function(require, exports, module) { var $ = require('jquery.js') $('div').addClass('active'); }); // 加载模块 seajs.use(['myModule.js'], function(my){ }); | |
es6 | 结合了commonjs和AMD的优点 也就是一直在用的import/export export function test (args) { // body... console.log(args); } // 默认导出模块,一个文件中只能定义一个 export default function() {...}; export const name = "lyn"; // _代表引入的export default的内容 import _, { test, name } from './a.js'; test(`my name is ${name}`);es6输出的是值的引用 并且是静态引入 编译时就引入了 | |
造成内存泄漏的操作
内存泄漏指任何对象在您不再拥有或需要它之后仍然存在
如:
如:
- setTimeout 的第一个参数使用字符串而非函数
- 闭包
- 控制台日志
- 循环(在两个对象彼此引用且彼此保留时,就会产生一个循环)
- 移除存在绑定事件的DOM元素(IE)
javascript创建对象的几种方式
这个就还挺随便的 反正就是把类似class那些方法用一用
字面量直接声明 | person={firstname:"Mark", :"Yun",age:25, eyecolor:"black"}; |
new一下 | var obj = new Object(); |
Object.create( ) | var obj = Object.create(Object.prototype); 用来指定原型 |
用function 模拟无参的构造函数 | function Person(){} var person=new Person(); //定义一个function,如果使用new"实例化" //该function可以看作是一个Class person.name="Mark"; person.age="25"; person.work=function(){ alert(person.name+" hello..."); }(这不就是class吗?) |
模拟参数构造函数 | function Pet(name,age,hobby){ this.name=name;//this作用域:当前对象 this.age=age; this.hobby=hobby; this.eat=function(){ alert("吃了"); } } 实例化: var maidou =new Pet("麦兜",25,"coding"); |
工厂方式 | var wcDog =new Object(); wcDog.name="旺财"; wcDog.age=3; wcDog.work=function(){ alert("我是"+wcDog.name+",汪汪汪......"); } |
原型方式 | function Dog(){} Dog.prototype.name="旺财"; Dog.prototype.eat=function(){ alert(this.name+"是个吃货"); } var wangcai =new Dog(); |
混合方式 | function Car(name,price){ this.name=name; this.price=price; } Car.prototype.sell=function(){ alert ("我是"+this.name+",我现在卖"+this.price+"万元"); } var camry =new Car("凯美瑞",27); |
null undefined
null | null 是一个对象(空对象, 没有任何属性和方法) 在验证null时,一定要使用 === ,因为 ==无法分别null 和 undefined |
undefined | undefined 表示不存在这个值 例如变量被声明了,但没有赋值时,就等于undefined |
script异步加载方式
设置<script>属性 defer="defer"
设置<script>属性 async="async"
动态创建 var script = document.createElement('script')
XmlHttpRequest 脚本注入
异步加载库 LABjs
其中 defer async两者区别是:
其中 defer async两者区别是:
defer要等到整个页面在内存中正常渲染结束(DOM 结构完全生成,以及其他脚本执行完成),才会执行;
async一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染。
一句话,defer是“渲染完再执行”,async是“下载完就执行”。
另外,如果有多个defer脚本,会按照它们在页面出现的顺序加载,而多个async脚本是不能保证加载顺序的。
eval
它的功能是把对应的字符串解析成JS代码并运行
应该避免使用eval,不安全,非常耗性能(2次,一次解析成js语句,一次执行)
由JSON字符串转换为JSON对象的时候可以用eval,var obj =eval('('+ str +')')
应该避免使用eval,不安全,非常耗性能(2次,一次解析成js语句,一次执行)
由JSON字符串转换为JSON对象的时候可以用eval,var obj =eval('('+ str +')')
let var const
之前记错了!气死了!
let const var 都有全局作用域和函数作用域
但是var没有块级作用域罢了
在全局上下文中
- 带var的变量是 声明一个全局变量,不能被delete删除
- 不带var的变量是 创建一个全局对象(window)的属性,可以用delete关键字删除
- 带var的变量是 声明一个私有变量
- 不带var的变量,处理机制是
- 沿作用域链向上查找该变量,是哪个上下文中声明的变量,就改变哪个上下文中的变量
- 如果所有上下文中都没有该变量,则给全局对象 window,添加一个同名属性
let |
|
var |
这个是之前弄错的一点 人家var声明在函数里就是函数作用域 声明在函数外 才是全局作用域 声明一个全局变量 |
const |
demo: 声明 const arr =[1,2,3]后 不能通过 arr=[4,2,3]来重新赋值 但是可以通过arr[0]=4 来进行改变 |
如 内层变量覆盖外层变量 此时声明在if中的tmp会被提升为函数作用域的变量
var tmp = new Date(); function f(){ console.log(tmp); if(false){ var tmp = "hello"; } } f(); // undefined还可能出现 用来记录的循环变量泄露为全局变量
此时的i只是用来记录循环的 但是用var来记录 循环结束后不会消失 反而泄露了
var s = "hello"; for(var i=0;i++) console.log(s[i]); } console.log(i); // 5
attribute和property的区别是什么
- attribute是dom元素在文档中作为html标签拥有的属性 比如如‘type’,'id','value','classNamle'以及自定义属性,它的值只能是字符串。
- property就是dom元素在js中作为对象拥有的属性。可以直接使用DOM属性的方式进行操作 属性值为多类型
- 对于html的标准属性来说,attribute和property是同步的,是会自动更新的
- 但是对于自定义的属性来说,他们是不同步的
如何渲染几万条数据并不卡住界面
这条是重点 之后多看看!
不能一次性将几万条都渲染出来,而应该一次渲染部分 DOM,那么就可以通过 requestAnimationFrame 来每 16 ms 刷新一次
<body> <ul>控件</ul> <script> setTimeout(() => { // 插入十万条数据 const total = 100000 // 一次插入 20 条,如果觉得性能不好就减少 const once = 20 // 渲染数据总共需要几次 const loopCount = total / once let countOfRender = 0 let ul = document.querySelector("ul"); function add() { // 优化性能,插入不会造成回流 const fragment = document.createDocumentFragment(); for (let i = 0; i < once; i++) { const li = document.createElement("li"); li.innerText = Math.floor(Math.random() * total); fragment.appendChild(li); } ul.appendChild(fragment); countOfRender += 1; loop(); } function loop() { if (countOfRender < loopCount) { window.requestAnimationFrame(add); } } loop(); }, 0); </script> </body>
callee caller
callee | 返回正在执行的函数本身的引用,它是arguments的一个属性 demo: function a() { console.log(arguments.callee) }
|
caller | 返回一个对函数的引用,该函数调用了当前函数 demo: function a() { console.log(a.caller) }
|
数组去重汇总
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
垃圾回收问题
标记清除 | 当变量进入执行环境的时 候比如函数中声明一个变量,垃圾回收器将其标记为“进入环境” 当变量离开环境的时候(函数执行结束)将其标记为“离开环境” 垃圾回收器会在运行的时候给存储在内存中的所有变量加上标记 然后去掉环境中的变量以及被环境中变量所引用的变量(闭包) 在这些完成之后仍存在标记的就是要删除的变量了 |
引用计数 | 引用计数的策略是跟踪记录每个值被使用的次数 当声明了一个 变量并将一个引用类型赋值给该变量的时候这个值的引用次数就加1 如果该变量的值变成了另外一个,则这个值得引用次数减1 当这个值的引用次数变为0时,可以将其占用的空间回收 |
this
this,函数执行的上下文,可以通过apply,call,bind改变this的指向。
对于匿名函数或者直接调用的函数来说,this指向全局上下文(浏览器为window,NodeJS为global),
剩下的函数调用,那就是谁调用它,this就指向谁。
箭头函数的指向取决于该箭头函数声明的位置,在哪里声明,this就指向哪里
| |
| |
axios、fetch
axios | demo: axios({ method: 'post', url: '/user/12345', data: { firstName: 'Fred', lastName: 'Flintstone' } }) .then(function (response) { console.log(response); }) .catch(function (error) { console.log(error); });
|
fetch | demo: try { let response = await fetch(url); let data = response.json(); console.log(data); } catch(e) { console.log("Oops, error", e); }
|
检测浏览器版本方法
- 根据 navigator.userAgent UA.toLowerCase().indexOf('chrome')
- 根据 window 对象的成员 'ActiveXObject' in window
JS内置对象
- 数据封装类对象:Object、Array、Boolean、Number、String
- 其他对象:Function、Arguments、Math、Date、RegExp、Error
- ES6新增对象:Symbol、Map、Set、Promises、Proxy、Reflect
如何删除一个cookie
| |
| |