font-main
2020-12-15 10:31:48 0 举报
AI智能生成
前端大纲知识点
作者其他创作
大纲/内容
1. 字节跳动前端岗位面试(已拿offer)文末附赠其他面试面经等https://www.jianshu.com/p/d74fceb929c8
review
1.generator 语法糖
1. 回调
2.promise
3.* yield
4. async await
2. 演变
4. async function async1() {console.log('async1 start')await async2()console.log('async1 end')}async function async2() { font color=\"#c41230\
1. async 声明的函数,其返回值必定是 promise 对象,如果没有显式返回 promise 对象,也会用 Promise.resolve() 对结果进行包装,保证返回值为 promise 类型
2. await 会先执行其右侧表达逻辑(从右向左执行),并让出主线程,跳出 async 函数,而去继续执行 async 函数外的同步代码
3. 如果 await 右侧表达逻辑是个 promise,让出主线程,继续执行 async 函数外的同步代码,等待同步任务结束后,且该 promise 被 resolve 时,继续执行 await 后面的逻辑
async function async1() { console.log('async1 start') await async2() console.log('async1 end')}async function async2() { console.log('async2') // 直接打印同步代码 async2,// 并返回一个 resolve 值为 undefined 的 promise}// async2 函数内并没有 await,按顺序执行,同步输出 async2,相当于async function async1() {console.log('async1 start')await Promise.resolve().then(() =>{})console.log('async1 end')}
4. 如果 await 右侧表达逻辑不是 promise 类型,那么仍然异步处理,将其理解包装为 promise, async 函数之外的同步代码执行完毕之后,会回到 async 函数内部,继续执行 await 之后的逻辑
1.pomise
2.async await 、 promise
3. 常见面试题
执行栈
1.同步任务
Promise.thenMutationObserverprocess.nextTick (Node.js)
1.微任务
setTimeoutsetIntervalI/O事件postMessagesetImmediate (Node.js,浏览器端该 API 已经废弃)requestAnimationFrameUI 渲染
2.宏任务
2.异步任务
4. 浏览器js队列
1. async await 你真的用对了吗? https://www.cnblogs.com/kenkofox/
2. ES6 Async/Await 完爆Promise的6个原因 https://segmentfault.com/a/1190000009070711
3. 头条前端笔试题 - 实现一个带并发限制的promise异步调度器 https://blog.csdn.net/zz_jesse/article/details/107293743?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.compare&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.compare
4. js 多个异步 的并发控制 https://blog.csdn.net/anchu7971/article/details/101279805?utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2~all~sobaiduend~default-1-101279805.nonecase&utm_term=js%E6%8E%A7%E5%88%B6%E5%BC%82%E6%AD%A5%E5%B9%B6%E5%8F%91%E6%95%B0%E9%87%8F&spm=1000.2123.3001.4430
5. https://blog.csdn.net/weixin_33928137/article/details/88754909?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.compare&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.compare
5. 使用连接
1.async await
并发控制
1. 图片碎片异步并发上传
2. gitchat 画正方形、 红绿灯
3. gitchat 异步并发数限制、promise.rate
4. gitchat 返回100本书籍
demo
1. js实现并发请求控制 https://www.jianshu.com/p/be92059cd7f2
3. 链接
2. 异步流程控制
1. promisefy
function Promise(executor) {}
Promise 其实就是一个构造函数,我们使用这个构造函数创建一个 Promise 实例。该构造函数很简单,它只有一个参数,按照 Promise/A+ 规范的命名,把 Promise 构造函数的参数叫做 executor,executor 类型为函数。这个函数又“自动”具有 resolve、reject 两个方法作为参数。
step1
Promise 构造函数返回一个 promise 对象实例,这个返回的 promise 对象具有一个 then 方法。then 方法中,调用者可以定义两个参数,分别是 onfulfilled 和 onrejected,它们都是函数类型。其中 onfulfilled 通过参数,可以获取 promise 对象 resolved 的值,onrejected 获得 promise 对象 rejected 的值。通过这个值,我们来处理异步完成后的逻辑
step2 Promise 的实质:
我们在使用 new 关键字调用 Promise 构造函数时,在合适的时机(往往是异步结束时),调用 executor 的参数 resolve 方法,并将 resolved 的值作为 resolve 函数参数执行,这个值便可以后续在 then 方法第一个函数参数(onfulfilled)中拿到;同理,在出现错误时,调用 executor 的参数 reject 方法,并将错误信息作为 reject 函数参数执行,这个错误信息可以在后续的 then 方法第二个函数参数(onrejected)中拿到。
step3
为了保证 onfulfilled、onrejected 能够强健执行,我们为其设置了默认值,其默认值为一个函数元(Function.prototype)。
因此,我们在实现 Promise 时,应该有两个值,分别储存 resolved 的值,以及 rejected 的值(当然,因为 Promise 状态的唯一性,不可能同时出现 resolved 的值和 rejected 的值,因此也可以用一个变量来存储);同时也需要存在一个状态,这个状态就是 promise 实例的状态(pending,fulfilled,rejected);同时还要提供 resolve 方法以及 reject 方法,这两个方法需要作为 executor 的参数提供给开发者使用:
step4
构造函数原型上可以拿到 构造函数内部变量的值
因为 resolve 的最终调用是由开发者在不确定环境下(往往是在全局中)直接调用的。为了在 resolve 函数中能够拿到 promise 实例的值,我们需要对 this 进行保存,上述代码中用 self 变量记录 this,或者使用箭头函数
step5
这涉及到原型、原型链的知识了:每个 promise 实例的 then 方法逻辑是一致的,在实例调用该方法时,可以通过原型(Promise.prototype)找到,而不需要每次实例化都新创建一个 then 方法,这样节省内存,显然更合适。
为什么 then 放在 Promise 构造函数的原型上,而不是放在构造函数内部呢?
setp6
只会输出:data,因为我们知道 promise 实例状态只能从 pending 改变为 fulfilled,或者从 pending 改变为 rejected。状态一旦变更完毕,就不可再次变化或者逆转。也就是说:如果一旦变到 fulfilled,就不能再 rejected,一旦变到 rejected,就不能 fulfilled。而我们的代码实现,显然无法满足这一特性。执行上一段代码时,将会输出 data 以及 error。
测试
step7
这里我们对 Promise.prototype.then 参数 onfulfilled 和 onrejected 进行了判断,当实参不是一个函数类型时,赋予默认函数值。这时候的默认值不再是函数元 Function.prototype 了。为什么要这么更改?后面会有介绍。
状态进行判断和完善
step8
MutationObserver https://developer.mozilla.org/zh-CN/docs/Web/API/MutationObserver
Promise 异步完善
step9
Promise 多个then 处理、错误捕获
step10
链接 https://www.jianshu.com/p/fe42b01ef076
Promise 状态具有凝固性Promise 错误处理Promise 实例添加多个 then 处理
总结:Promise特性
step11
2. code process
1. 你以为我真的想让你手写 Promise 吗(上) https://gitbook.cn/gitchat/column/5c91c813968b1d64b1e08fde/topic/5cbbe876bbbba80861a35bf4
3. 链接
1. 手写Promise
2. 完善手写Promise
3.链接: 阮一峰:promise https://es6.ruanyifeng.com/#docs/promise从零开始手写Promise https://zhuanlan.zhihu.com/p/144058361
3.promise 原理
异步
http://www.ruanyifeng.com/blog/2013/10/event_loop.html
每当遇到I/O的时候,主线程就让Event Loop线程去通知相应的I/O程序,然后接着往后运行,所以不存在红色的等待时间。等到I/O程序完成操作,Event Loop线程再把结果返回主线程。主线程就调用事先设定的回调函数,完成整个任务。
先到的人锁上门,后到的人看到上锁,就在门口排队,等锁打开再进去。这就叫"互斥锁"(Mutual exclusion,缩写 Mutex),防止多个线程同时读写某一块内存区域。
event loop
1. new Promise() 会立即执行 2. 一个setTimeout 会执行一轮宏任务,如果这个setTimeout里面有微任务,先执行setTimeout里面的同步代码,在执行setTimeout里面的微任务。3.再执行下一轮宏任务setTimeout2 ,再执行setTImeout2里面的微任务
0. 这一次,彻底弄懂 JavaScript 执行机制https://juejin.cn/post/6844903512845860872
1. JavaScript 运行机制详解:再谈Event Loop http://www.ruanyifeng.com/blog/2014/10/event-loop.html
2.链接
浏览器event loop
子主题 2
1. NodeJS 事件循环(第一部分)- 事件循环机制概述 https://zhuanlan.zhihu.com/p/37427130
2. 初探nodejs事件循环机制event loop https://www.cnblogs.com/zifayin/p/11419808.html
3. NodeJS 事件循环 https://zhuanlan.zhihu.com/p/37427130
4. 笔记 nodejs-第二章-第二节-nodejs事件循环(2-1) http://note.youdao.com/s/MbA6ISKq
5. Node.js 事件循环机制 https://www.cnblogs.com/onepixel/p/7143769.html
链接
nodejs event loop
将一个 DOM 节点作为对象data的键,但是由于对象只接受字符串作为键名,所以element被自动转为字符串[object HTMLDivElement]
1.定义:map提供了“值---值”的对应,是一种更完善的hash结构,
get/set/has/delete/size/clear
2.属性、方法
1.Map.prototype.keys()
2.Map.prototype.values()
1.Map 结构的默认遍历器接口(Symbol.iterator属性),就是entries方法。2.map[Symbol.iterator] === map.entries // true
3.Map.prototype.entries()
4.Map.prototype.forEach()
3.遍历方法
1.扩展运算符
4.Map转数组结构
1.将数组传入 Map 构造函数,就可以转为 Map。
5.数组转Map
1. Map 的键都是字符串,它可以无损地转为对象
2.如果有非字符串的键名,那么这个键名会被转成字符串,再作为对象的键名。
6.Map 转为对象
1.Map 的键名都是字符串,这时可以选择转为对象 JSON。
2.Map 的键名有非字符串,这时可以选择转为数组 JSON。
8.Map 转为 JSON
let obj = {\"a\
1.对象转为 Map 可以通过Object.entries()。
2.自己实现一个转换函数
7.对象转Map
function jsonToStrMap(jsonStr) { return objToStrMap(JSON.parse(jsonStr));}jsonToStrMap('{\"yes\
1.正常情况下,所有键名都是字符串
2.整个 JSON 就是一个数组,且每个数组成员本身,又是一个有两个成员的数组。这时,它可以一一对应地转为 Map。这往往是 Map 转为数组 JSON 的逆操作。
9.JSON 转为 Map
map
1.WeakMap结构与Map结构类似,也是用于生成键值对的集合
1. WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名。
2. WeakMap的键名所指向的对象,不计入垃圾回收机制。
只要所引用的对象的其他引用都被清除,垃圾回收机制就会释放该对象所占用的内存。也就是说,一旦不再需要,WeakMap 里面的键名对象和所对应的键值对会自动消失,不用手动删除引用。
3. 有时我们想在某个对象上面存放一些数据,但是这会形成对于这个对象的引用
4. WeakMap 弱引用的只是键名,而不是键值。键值依然是正常引用。
2. 区别
1.定义
1.一是没有遍历操作(即没有keys()、values()和entries()方法),也没有size属性。
2. 二是无法清空,即不支持clear方法。
3. const wm = new WeakMap();// size、forEach、clear 方法都不存在wm.size // undefinedwm.forEach // undefinedwm.clear // undefined
1. WeakMap 与 Map 在 API 上的区别主要是两个,
get()、set()、has()、delete()。
2. 方法
2. 属性和方法
在网页的 DOM 元素上添加数据,就可以使用WeakMap结构。WeakMap 里面对element的引用就是弱引用,不会被计入垃圾回收机制也就是说,上面的 DOM 节点对象的引用计数是1,而不是2。当该 DOM 元素被清除,其所对应的WeakMap记录就会自动被移除。
1. 在网页dom上添加数据
2. WeakMap的专用场合就是,它的键所对应的对象,可能会在将来消失。WeakMap结构有助于防止内存泄漏。
如果引用所指向的值占用特别多的内存,就可以通过 Node 的process.memoryUsage方法看出来。node --expose-gc--expose-gc参数表示允许手动执行垃圾回收机制
3. 查看引用内存
4. WeakMap 的另一个用处是部署私有属性。
3. 应用场景
weakMap
1.ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
2.Set函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化。
3. 有点类似队列的意思, Set的遍历顺序就是插入顺序。这个特性有时非常有用,比如使用 Set 保存一个回调函数列表,调用时就能保证按照添加顺序调用。
const set = new Set();document .querySelectorAll('div') .forEach(div => set.add(div));set.size // 5
2. 用法
构造函数,默认就是Set函数。
1.Set.prototype.constructor
返回Set实例的成员总数。
2.Set.prototype.size
1.属性
1.Set.prototype.add(value) 添加某个值,返回Set结构本身
2.Set.prototype.delete(value) 删除某个值,返回一个布尔值,表示删除是否成功。
3.Set.prototype.has(value):返回一个布尔值,表示该值是否为Set的成员。
4.Set.prototype.clear():清除所有成员,没有返回值。
1.操作方法(用于操作数据)
由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys方法和values方法的行为完全一致。
1. Set.prototype.keys():返回键名的遍历器
2.Set.prototype.values():返回键值的遍历器
for (let item of set.entries()) { console.log(item);}// [\"red\
3. Set.prototype.entries():返回键值对的遍历器
3个参数:键值、键名、集合本身
4. Set.prototype.forEach():使用回调函数遍历每个成员
5. 可以用for of 遍历Set
扩展运算符(...)内部使用for...of循环,所以也可以用于 Set 结构
6. 扩展运算符遍历
2.遍历方法(用于遍历成员)
1. 扩展运算符和 Set 结构相结合,就可以去除数组的重复成员。
2.数组的map和filter方法也可以间接用于 Set 了。
3. Set 可以很容易地实现并集(Union)、交集(Intersect)和差集
同步改变原来的 Set 结构,目前没有直接的方法,但有两种变通方法。一种是利用原 Set 结构映射出一个新的结构,然后赋值给原来的 Set 结构;另一种是利用Array.from方法。
4. 同步改变原来Set结构
3. 遍历的应用
1. Set.prototype[Symbol.iterator] === Set.prototype.values// true
4.Set 结构的实例默认可遍历,它的默认遍历器生成函数就是它的values方法。
2.方法
3.Set 实例的属性和方法
let set = new Set();let a = NaN;let b = NaN;set.add(a);set.add(b);set // Set {NaN}
1.向 Set 加入值时认为NaN等于自身,而精确相等运算符认为NaN不等于自身。
let set = new Set();set.add({});set.size // 1set.add({});set.size // 2
2.两个对象总是不相等的。
4.注意点
1. Set 转数组
子主题
5. 数组和set相互转换
set
1.WeakSet 结构与 Set 类似,也是不重复的值的集合。
第一点主要是因为值类型不存在引用,自然不能放在 WeakSet 中。
在 WeakSet 中引用是弱引用
2. 区别:
在该 WeakSet 对象中添加一个新元素value.
1. WeakSet.prototype.add(value)
清空该 WeakSet 对象中的所有元素.
2. WeakSet.prototype.clear()
3. WeakSet.prototype.delete(value)
4.WeakSet.prototype.has(value)
2. 属性方法
WeakSet 的一个用处,是储存 DOM 节点,而不用担心这些节点从文档移除时,会引发内存泄漏。
1.存储dom节点
上面代码保证了Foo的实例方法,只能在Foo的实例上调用。这里使用 WeakSet 的好处是,foos对实例的引用,不会被计入内存回收机制,所以删除实例的时候,不用考虑foos,也不会出现内存泄漏
const foos = new WeakSet()class Foo { constructor() { foos.add(this) } method () { if (!foos.has(this)) { throw new TypeError('Foo.prototype.method 只能在Foo的实例上调用!'); } }}
2.保证实例的方法,只能在实例上调用
其实上文中“需要一个弱引用的集合”和“进行判断时外界对这个对象必然存在引用的”并不完全矛盾。假如在一个模块内部,我既控制了对象的生命周期,而又需要这个集合进行判断,那么的确,这个 WeakSet 确实没有必要。但是假如需要这个集合进行判断的一方并不想控制这个对象的生命周期,那么 WeakSet 倒显得有些必要了
const requests = new WeakSet();class ApiRequest { constructor() { requests.add(this); } makeRequest() { if(!request.has(this)) throw new Error(\"Invalid access\"); // do work }}
3. 假如需要这个集合进行判断的一方并不想控制这个对象的生命周期
3.应用
1. WeakSet 用法解惑 https://zhuanlan.zhihu.com/p/54889129
4.链接
weakSet
map、set、weakMap、weakSet
1. Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。
2. Proxy 实际上重载(overload)了点运算符,即用自己的定义覆盖了语言的原始定义。
1. 创建一个可撤销的Proxy对象。
2. Proxy.revocable()
1. new Proxy()表示生成一个Proxy实例,target参数表示所要拦截的目标对象,
2. handler参数也是一个对象,用来定制拦截行为。
1. handler是一个空对象,没有任何拦截效果,访问proxy就等同于访问target。
2. 一个技巧是将 Proxy 对象,设置到object.proxy属性,从而可以在object对象上调用。
2. handler是一个对象,里面是对应操作的函数
1. proxy对象是obj对象的原型,obj对象本身并没有time属性,所以根据原型链,会在proxy对象上读取该属性,导致被拦截。
3. Proxy 实例也可以作为其他对象的原型对象。
1.拦截对象属性的读取,比如proxy.foo和proxy['foo']。
2. get方法可以继承。
1. proxy.getReceiver 获取第三个参数
2. get方法的第三个参数的例子,它总是指向原始的读操作所在的那个对象,一般情况下就是 Proxy 实例。
3. get 第三个参数的例子
1. 拦截对象属性的设置,比如proxy.foo = v或proxy['foo'] = v,返回一个布尔值。
1. 拦截propKey in proxy的操作,返回一个布尔值。
1. 拦截delete proxy[propKey]的操作,返回一个布尔值。
var arr = [\"a\
1. 方法返回一个由指定对象的所有自身属性的属性名组成的数组。(包括不可枚举属性但不包括Symbol值作为名称的属性)
1. Object.getOwnPropertyNames(proxy)
1. 请注意,Object.getOwnPropertyNames()本身不包含对象的 Symbol 属性,只包含字符串属性。
1. obj:要返回 Symbol 属性的对象。2.在给定对象自身上找到的所有 Symbol 属性的数组。
2. Object.getOwnPropertySymbols(obj)
1. 返回结果仅包括目标对象自身的可遍历属性。
3. Object.keys(proxy)
4. for...in循环
5. ownKeys(target),拦截:1-4返回一个数组。该方法返回目标对象所有自身的属性的属性名,
1. 拦截Object.getOwnPropertyDescriptor(),返回属性的描述对象。
1. target: 目标对象, prop: 属性名2. 方法必须返回一个 object 或 undefined。
2. Object.getOwnPropertyDescriptor()
1. 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
1.obj 要定义属性的对象。prop 要定义或修改的属性的名称或 Symbol 。descriptor 要定义或修改的属性描述符。返回值:被传递给函数的对象。
2. 这个方法允许修改默认的额外选项(或配置) 默认情况下,使用 Object.defineProperty() 添加的属性值是不可修改(immutable)的。
如果 o.a 的 configurable 属性为 true,则不会抛出任何错误,并且,最后,该属性会被删除。
1. configurable 特性表示对象的属性是否可以被删除,以及除 value 和 writable 特性外的其他特性是否可以被修改。
2. 当且仅当该属性的 configurable 键值为 true 时,该属性的描述符才能够被改变,同时该属性也能从对应的对象上被删除。默认为 false。
1.configurable
1. enumerable 定义了对象的属性是否可以在 for...in 循环和 Object.keys() 中被枚举。
1.当且仅当该属性的 enumerable 键值为 true 时,该属性才会出现在对象的枚举属性中。默认为 false。 // enumerable 默认为 false// 如果使用直接赋值的方式创建对象的属性,则 enumerable 为 true// 如果enumerable为false,o.b 为undefined
2. o.propertyIsEnumerable('a') // true
3. o.propertyIsEnumerable(Symbol.for('e')); // true
2. enumerable
该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined。
3. value
1. 如果属性已经存在,Object.defineProperty()将尝试根据描述符中的值以及对象当前的配置来修改这个属性。如果旧描述符将其configurable 属性设置为false,则该属性被认为是“不可配置的”,并且没有属性可以被改变(除了单向改变 writable 为 false)。当属性不可配置时,不能在数据和访问器属性类型之间切换。
2. 当试图改变不可配置属性(除了 value 和 writable 属性之外)的值时,会抛出TypeError,除非当前值和新值相同。
1.当且仅当该属性的 writable 键值为 true 时,属性的值,也就是上面的 value,才能被赋值运算符改变。默认为 false。
2. 当 writable 属性设置为 false 时,该属性被称为“不可写的”。它不能被重新赋值。
4. writable
1. 考虑特性被赋予的默认特性值非常重要,通常,使用点运算符和 Object.defineProperty() 为对象的属性赋值时,数据描述符中的属性默认值是不同的,
5. 添加多个属性和默认值
1. 数据描述符
1. 属性的 getter 函数,如果没有 getter,则为 undefined。当访问该属性时,会调用此函数。执行时不传入任何参数,但是会传入 this 对象(由于继承关系,这里的this并不一定是定义该属性的对象)。该函数的返回值会被用作属性的值。默认为 undefined。
1. get
1. 属性的 setter 函数,如果没有 setter,则为 undefined。当属性值被修改时,会调用此函数。该方法接受一个参数(也就是被赋予的新值),会传入赋值时的 this 对象。默认为 undefined。
2. set
2. 存取描述符
3. 一个描述符只能是这两者其中之一;不能同时是两者。
1. 拥有布尔值的键 configurable、enumerable 和 writable 的默认值都是 false。
2. 属性值和函数的键 value、get 和 set 字段的默认值为 undefined。
3. 如果一个描述符不具有 value、writable、get 和 set 中的任意一个键,那么它将被认为是一个数据描述符。
4.如果一个描述符同时拥有 value 或 writable 和 get 或 set 键,则会产生一个异常。
5. 这些选项不一定是自身属性,也要考虑继承来的属性。为了确认保留这些默认值,在设置之前,可能要冻结 Object.prototype,明确指定所有的选项,或者通过 Object.create(null) 将 __proto__ 属性指向 null。
4. 描述符默认值汇总
1. 实现一个自存档对象。当设置temperature 属性时,archive 数组会收到日志条目。
2. 设置 getter 总是会返回一个相同的值。
5. 自定义 Setters 和 Getters
1.如果访问者的属性是被继承的,它的 get 和 set 方法会在子对象的属性被访问或者修改时被调用。如果这些方法用一个变量存值,该值会被所有对象共享
2. 这可以通过将值存储在另一个属性中解决。在 get 和 set 方法中,this 指向某个被访问和修改属性的对象。
3. 不像访问者属性,值属性始终在对象自身上设置,而不是一个原型。然而,如果一个不可写的属性被继承,它仍然可以防止修改对象的属性。
6. 继承属性
7.链接: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
4. 属性描述符
2. Object.defineProperty()
1. 定义: Object.defineProperties() 方法直接在一个对象上定义新的属性或修改现有属性,并返回该对象。
2. Object.defineProperties本质上定义了obj 对象上props的可枚举属性相对应的所有属性
3. Object.defineProperties()
Object.preventExtensions()方法让一个对象变的不可扩展,也就是永远不能再添加新的属性。
1. 拦截Object.preventExtensions(proxy),返回一个布尔值。
1. Object.isExtensible() 方法判断一个对象是否是可扩展的(是否可以在它上面添加新的属性)。返回Boolean
1. Object.preventExtensions()仅阻止添加自身的属性。但其对象类型的原型依然可以添加新的属性。
2. 如果一个对象可以添加新的属性,则这个对象是可扩展的。Object.preventExtensions()将对象标记为不再可扩展,这样它将永远不会具有它被标记为不可扩展时持有的属性之外的属性。
1. Object.preventExtensions(obj)方法让一个对象变的不可扩展,也就是永远不能再添加新的属性。参数: obj 将要变得不可扩展的对象返回值: 已经不可扩展的对象
2. Object.seal(obj)方法封闭一个对象,阻止添加新属性并将所有现有属性标记为不可配置。当前属性的值只要原来是可写的就可以改变。参数: obj将要被密封的对象。返回值:已经被密封的对象。
1. 这个方法返回传递的对象,而不是创建一个被冻结的副本。
2. 数据属性的值不可更改,访问器属性(有getter和setter)也同样(但由于是函数调用,给人的错觉是还是可以修改这个属性)。
3. 如果一个属性的值是个对象,则这个对象中的属性是可以修改的,除非它也是个冻结对象。
1. 解释
被冻结的对象是不可变的。但也不总是这样。下例展示了冻结对象不是常量对象(浅冻结)obj1 = { internal: {}};Object.freeze(obj1);obj1.internal.a = 'aValue';
2. 对于一个常量对象,整个引用图(直接和间接引用其他对象)只能引用不可变的冻结对象。冻结的对象被认为是不可变的,因为整个对象中的整个对象状态(对其他对象的值和引用)是固定的。注意,字符串,数字和布尔总是不可变的,而函数和数组是对象
1. 浅冻结
1. 要使对象不可变,需要递归冻结每个类型为对象的属性(深冻结)。当你知道对象在引用图中不包含任何 环 (循环引用)时,将根据你的设计逐个使用该模式,否则将触发无限循环。对 deepFreeze() 的增强将是具有接收路径(例如Array)参数的内部函数,以便当对象进入不变时,可以递归地调用 deepFreeze() 。你仍然有冻结不应冻结的对象的风险,例如[window]对象。
2. // 深冻结函数.function deepFreeze(obj) { // 取回定义在obj上的属性名 var propNames = Object.getOwnPropertyNames(obj); // 在冻结自身之前冻结属性 propNames.forEach(function(name) { var prop = obj[name]; // 如果prop是个对象,冻结它 if (typeof prop == 'object' && prop !== null) deepFreeze(prop); }); // 冻结自身(no-op if already frozen) return Object.freeze(obj);}obj2 = { internal: {}};deepFreeze(obj2);obj2.internal.a = 'anotherValue';obj2.internal.a; // undefined
2.深冻结
let a = [0];Object.freeze(a); // 现在数组不能被修改了.a[0]=1; // fails silentlya.push(2); // fails silently// In strict mode such attempts will throw TypeErrorsfunction fail() { \"use strict\" a[0] = 1; a.push(2);}fail();
1. 数组作为一种对象,被冻结,其元素不能被修改。没有数组元素可以被添加或移除。
2. 冻结数组
> Object.freeze(1)TypeError: 1 is not an object // ES5 code> Object.freeze(1)1 // ES2015 code
1. 在ES5中,如果这个方法的参数不是一个对象(一个原始值),那么它会导致 TypeError。在ES2015中,非对象参数将被视为要被冻结的普通对象,并被简单地返回。
3. 冻结参数只能是对象,不能是简单类型的值
用Object.seal()密封的对象可以改变它们现有的属性。使用Object.freeze() 冻结的对象中现有属性是不可变的。
4. 对比Object.seal()
3. Object.freeze() 方法可以冻结一个对象。一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。此外,冻结一个对象后该对象的原型也不能被修改。freeze() 返回和传入的参数相同的对象。
// 新对象默认是可扩展的.var empty = {};Object.isExtensible(empty); // === true// ...可以变的不可扩展.Object.preventExtensions(empty);Object.isExtensible(empty); // === false// 密封对象是不可扩展的.var sealed = Object.seal({});Object.isExtensible(sealed); // === false// 冻结对象也是不可扩展.var frozen = Object.freeze({});Object.isExtensible(frozen); // === false
1. 默认情况下,对象是可扩展的:即可以为他们添加新的属性。以及它们的 __proto__ 属性可以被更改。Object.preventExtensions,Object.seal 或 Object.freeze 方法都可以标记一个对象为不可扩展(non-extensible)。
2. Object.isExtensible(obj)
8. preventExtensions
1. 更改对象的 [[Prototype]]在各个浏览器和 JavaScript 引擎上都是一个很慢的操作
2.应该使用 Object.create()来创建带有你想要的[[Prototype]]的新对象
1.Object.setPrototypeOf()是ECMAScript 6最新草案中的方法,相对于 Object.prototype.__proto__ ,它被认为是修改对象原型更合适的方法
2. 如果prototype参数不是一个对象或者null,则什么都不做(例如,数字,字符串,boolean,或者 undefined)。否则,该方法将obj的[[Prototype]]修改为新的值。
3. 使用较旧的 Object.prototype.__proto__ 属性,我们可以很容易地定义Object.setPrototypeOf 如果它不可用:
4. 通过 Object.getPrototypeOf() 和 Object.prototype.__proto__ 的组合允许将一个原型链完整的附加到一个新的原型对象上:
5. 向一个原型附加一个链
6. 将一个基本类型转化为对应的对象类型并添加到原型链上
7. 给函数类型的对象添加一个链,并添加一个新的方法到那个链上
1.Object.setPrototypeOf 方法的捕捉器。
2. Reflect.setPrototypeOf()
1. 拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)。
4. Proxy 支持的拦截操作
3. handler对象的方法
Proxy
es6
1. step1:首先创建一个空对象,这个对象将会作为执行 new 构造函数() 之后,返回的对象实例step2:将上面创建的空对象的原型(__proto__),指向构造函数的 prototype 属性step3:将这个空对象赋值给构造函数内部的 this,并执行构造函数逻辑step4:根据构造函数执行逻辑,返回第一步创建的对象或者构造函数的显式返回值// 构造函数如果有显式返回值,且返回值为对象类型,那么构造函数返回结果不再是目标实例
2. new 命令的原理 https://javascript.ruanyifeng.com/oop/basic.html
3. 实现继承,劫持另外一个对象方法,继承另一个对象的属性
1.apply方法妙用
2. call
1.bind参数和call一致2. 不同的是bind返回的是一个新的函数,需要加括号“()”调用
3. bind
4.链接: apply方法详解 https://www.cnblogs.com/chenhuichao/p/8493095.html
3. apply、call、bind
1.静态属性也可以说对象的私有属性,只能通过Foo.count(类名.属性)的方式访问,而实例是无法访问的
2. function Foo() {} var f1=new Foo(); Foo.count = 0; //静态属性 Foo.count++; console.log(f1.count);//undefined console.log(Foo.count);//1
1. 静态属性
1.原型属性是构造函数和实例都可以访问的,两种访问方式:Foo.prototype.属性f1.属性
2. function Foo(name) {}; var f1 = new Foo('f1'); Foo.prototype.count = 0;//原型属性 Foo.prototype.count++; console.log(Foo.prototype.count);//1 console.log(f1.count);//1
1. console.log(f1.hasOwnProperty('count'));//falseconsole.log(Foo.hasOwnProperty('count'));//falseconsole.log(Foo.prototype.hasOwnProperty('count'));//trueconsole.log('count' in f1);//trueconsole.log('count' in Foo);//falseconsole.log('count' in Foo.prototype);//true
2. f1本身并没有count属性,当通过f1访问count时,在f1中没有找到count属性,就会按着 原型链 向上查找(f1. __proto__ === Foo.prototype),就会找到Foo.prototype的count属性并返回值。因此,原型属性是共享给实例的,但是它并不属于实例,也不属于构造函数,而是属于原型对象的。
1. hasOwnProperty方法可以判断属性是否属于对象本身。一般用来判断属性是属于实例还是原型对象。2. in操作符判断是否能通过对象访问到属性。
3. 属性判断:
2. 原型属性
1. function Foo(name) {}; var f1 = new Foo('f1'); Foo.prototype.count = 0; //原型属性 Foo.prototype.count++; f1.count++; //实例属性 console.log(Foo.prototype.count); //1 console.log(f1.count); //2
1. console.log(f1.hasOwnProperty('count')); //true console.log(Foo.prototype.hasOwnProperty('count')); //true console.log('count' in f1);//true console.log('count' in Foo.prototype);//true
2. 这个例子只比上一个例子多了一行代码,但是与上面原型属性不同的是,这时会给f1创建一个count属性,然后将f1.count = Foo.prototype.count(深复制),再 f1.count+1,此时,不仅原型对象具有count属性,f1本身也具有了count属性。
2. 属性判断:
实例属性的访问方式也有两种:f1.count(实例名.属性)this.name(this.属性,这里的this指向的就是实例本身)
3. 访问
3.实例属性
1. 实例属性是属于实例自己的,而原型属性由于是共享的,因此引用类型的names属性,可以同时被f1和f2访问/修改,因此在使用过程中需要特别注意这一点。
4. 总结
1. JS在构造函数和实例化时需要注意的3种属性:静态、原型、实例属性 https://blog.csdn.net/jian_zi/article/details/100009788
5. 链接
1.JS在构造函数和实例化时涉及到的3种属性,分别是静态属性、原型属性和实例属性。
4. 静态属性、原型属性、实例属性
1.new 命令
1.预编译阶段进行变量声明;
所以变量的提升,提升的其实是变量的声明,而不是变量的赋值。
变量在声明提升的时候,是全部提升到作用域的最前面,一个接着一个的。但是在变量赋值的时候就不是一个接着一个赋值了,而是赋值的位置在变量原本定义的位置。原本js定义变量的地方,在js运行到这里的时候,才会进行赋值操作,而没有运行到的变量,不会进行赋值操作。
function foo() { var a; var b; a = 1; console.log(a); // 1 console.log(b); // undefined b = 2;}foo();
js 并不是在我们定义一个变量的时候,声明完成之后立即赋值,而是把所有用到的变量全部声明之后,再到变量的定义的地方进行赋值,变量的声明的过程就是变量的提升
2.预编译阶段变量声明进行提升,但是值为 undefined;
1. 在作用域中,不管是变量还是函数,都会提升到作用域最开始的位置,不同的是,函数的提升后的位置是在变量提升后的位置之后的。
2. 函数只有声明式函数才会被提升,字面量函数不会被提升
3. 预编译阶段所有非表达式的函数声明进行提升。
分支主题
b = 'aaa';
4.注意:只有声明的变量和函数才会进行提升,隐式全局变量不会提升
1.js变量提升与函数提升的详细过程 https://www.cnblogs.com/lvonve/p/9871226.html
2. js变量提升与函数提升的机制 https://segmentfault.com/a/1190000008568071
3.js的预编译 https://blog.csdn.net/csdn_zsdf/article/details/99645903
1.允许声明前调用
2.明确自我递归的语意
3.比较适合解释器分析程序
4.集中处理变量声明,并且关联作用域。
5.为什么要进行变量和函数声明提升
1.代码预编译阶段
作用域在预编译阶段确定,但是作用域链是在执行上下文的创建阶段完全生成的。因为函数在调用时,才会开始创建对应的执行上下文。执行上下文包括了:变量对象、作用域链以及 this 的指向
2. 代码执行阶段
1.我们在执行一个函数时,如果这个函数又调用了另外一个函数,而这个“另外一个函数”也调用了“另外一个函数”,便形成了一系列的调用栈
2.正常来讲,在函数执行完毕并出栈时,函数内局部变量在下一个垃圾回收节点会被回收,该函数对应的执行上下文将会被销毁,这也正是我们在外界无法访问函数内定义的变量的原因。也就是说,只有在函数执行时,相关函数可以访问该变量,该变量在预编译阶段进行创建,在执行阶段进行激活,在函数执行完毕后,相关上下文被销毁。
3. 调用栈
2.js代码执行过程
借助闭包来绑定数据变量,可以保护这些数据变量的内存块在闭包存活时,始终不被垃圾回收机制回收
1.定义: 函数嵌套函数时,内层函数引用了外层函数作用域下的变量,并且内层函数在全局环境下可访问,就形成了闭包。
2.对比前述内容,我们知道正常情况下外界是无法访问函数内部变量的,函数执行完之后,上下文即被销毁。但是在(外层)函数中,如果我们返回了另一个函数,且这个返回的函数使用了(外层)函数内的变量,外界因而便能够通过这个返回的函数获取原(外层)函数内部的变量值。这就是闭包的基本原理。
2.在chrome检查工具中,num 值被标记为 Closure,即闭包变量。
1.分配内存空间
2.读写内存
3.释放内存空间
栈空间:由操作系统自动分配释放,存放函数的参数值,局部变量的值等,其操作方式类似于数据结构中的栈。
堆空间:一般由开发者分配释放,这部分空间就要考虑垃圾回收的问题。
2.内存空间
1. var element = document.getElementById("element")element.mark = "marked"// 移除 element 节点function remove() { element.parentNode.removeChild(element)} 我们需要在 remove 方法中添加:element = null,这样更为稳妥。
例子
内存泄漏是指内存空间明明已经不再被使用,但由于某种原因并没有被释放的现象。这是一个非常“玄学”的概念,因为内存空间是否还在使用,某种程度上是不可判定问题,或者判定成本很高。内存泄漏危害却非常直观:它会直接导致程序运行缓慢,甚至崩溃。
3.内存泄漏
1. 如何处理 JavaScript 内存泄露 https://mp.weixin.qq.com/s?__biz=MzA5NzkwNDk3MQ==&mid=2650585408&idx=1&sn=4de7b5bbfa969d9587c163e98bc90684&source=41#wechat_redirect
2. [译] 通过垃圾回收机制理解 JavaScript 内存管理 https://juejin.cn/post/6844903764302774279
3. [JavaScript 随笔] 垃圾回收 https://segmentfault.com/a/1190000003641343
4. [译]编写高性能对垃圾收集友好的代码 https://segmentfault.com/a/1190000007887891
5. JavaScript 中 4 种常见的内存泄露陷阱 https://mp.weixin.qq.com/s?__biz=MzAxODE2MjM1MA==&mid=2651551451&idx=1&sn=b8447a12eceb467992d432b014d9c026&chksm=8025a11ab752280c7915db4ef726611f645d2fee590d6f0f3f9aeedd55c956454f66f786873a&scene=0#wechat_redirect
6. 记一次网页内存溢出分析及解决实践 https://juejin.cn/post/6844903761744265224
4. 链接
3.内存管理
1.标记清除
2.引用计数
4. 浏览器垃圾回收
自由变量是指没有在相关函数作用域中声明,但是使用了的变量
5.自由变量
1. const foo = (function() { var v = 0 return () => { return v++ }}())for (let i = 0; i < 10; i++) { foo()}console.log(foo())在循环执行时,执行 foo(),这样引用自由变量 10 次,v 自增 10 次,最后执行 foo 时,得到 10。
3.var fn = nullconst foo = () => { var a = 2 function innerFoo() { console.log(a) } fn = innerFoo }const bar = () => { fn()}foo()bar()正常来讲,根据调用栈的知识,foo 函数执行完毕之后,其执行环境生命周期会结束,所占内存被垃圾收集器释放,上下文消失。但是通过 innerFoo 函数赋值给 fn,fn 是全局变量,这就导致了 foo 的变量对象 a 也被保留了下来。所以函数 fn 在函数 bar 内部执行时,依然可以访问这个被保留下来的变量对象,输出结果为 2。
4. var fn = nullconst foo = () => { var a = 2 function innerFoo() { console.log(c) console.log(a) } fn = innerFoo}const bar = () => { var c = 100 fn() }foo()bar()在 bar 中执行 fn() 时,fn() 已经被复制为 innerFoo,变量 c 并不在其作用域链上,c 只是 bar 函数的内部变量。因此报错 ReferenceError: c is not defined。
定义:保证一个类只有一个实例,并提供一个访问它的全局访问点。
在redux 中的 createStore 的实现利用了闭包: 我之前很菜的思路认为,createStore是一个constructor构造函数,通过this.state来保存。后来看代码发现不是的,createStore是一个工厂函数,执行的时候,将用户的reducer,state,闭包储存了起来,返回给用户“引用了闭包的函数”。我想知道这个骚操作叫什么名字,想深入了解一下这种实现私有变量维护的手段。
5. 如何利用闭包实现单例模式
6.例题
3.闭包
怎么判断一个数组是数组呢? https://www.cnblogs.com/padding1015/p/9985718.html
JS判断是否是数组的四种做法 https://www.cnblogs.com/echolun/p/10287616.html
判断是否是数组
数组
0. 解析Object.prototype.toString.call()进行数据类型判断 http://www.webzsky.com/?p=9691. JavaScript中Object.prototype.toString方法的原理 https://www.jb51.net/article/79941.htm2. 深入理解Object.prototype.toString方法 https://blog.csdn.net/u014481405/article/details/1079148353. JavaScript中Object.prototype.toString方法的原理 https://www.cnblogs.com/yyy6/p/9447322.html
Object
对象
1. 引用类型
4. 数据类型
js
nodejs 教程 http://nodejs.cn/learn/differences-between-nodejs-and-the-browser
进程与线程的一个简单解释 http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html
进程、线程
nodejs
1. 浏览器渲染原理与过程 https://www.jianshu.com/p/e6252dc9be32
浏览器渲染过程
BFC是一个独立的布局环境,其中的元素布局是不受外界的影响,并且在一个BFC中,块盒与行盒(行盒由一行中所有的内联元素所组成)都会垂直的沿着其父元素的边框排列。
什么是BFC?看这一篇就够了 https://blog.csdn.net/sinat_36422236/article/details/88763187
1.什么是BFC
https://www.cnblogs.com/yyli/p/10847418.html
2.CSS里的BFC和IFC的用法
css
1.暴力遍历
2.缓存
1.两数之和
缓存优化
递归
for循环
递推
2.非波纳契数列
// 71 简化路径// const obj = {// \"\
leetcode :括号20、71目录、判断jsx是否合法、加减乘除表达式、js函数嵌套(函数调用栈)
栈和队列相互转化
1.括号
3.栈
4.队列
1. 链表最后一位next => null
2. 链表第一位叫head
3. 常用递归
2. node包括data和next()
3.js实现链表
1. 删除数据、插入数据比较方便
2. 没有办法做随机访问,只能找到对应节点,然后next、next,判断data得到
4. 优缺点
1. 删除链表元素 203
2. 206 反转链表
// 缓存var hasCycle = function(head) { let map = new Map() while(head){ if(map.has(head)){ return true } map.set(head) head = head.next } return false};
// 快慢指针var hasCycle = function(head) { let fast = head let slow = head while(fast && fast.next){ fast = fast.next.next slow = slow.next if(fast === slow){ return true } } return false};
// flag/** * Definition for singly-linked list. * function ListNode(val) { * this.val = val; * this.next = null; * } *//** * @param {ListNode} head * @return {boolean} */var hasCycle = function(head) { while(head){ if(head.flag) return true head.flag = true head = head.next } return false};
3. 141 环形链表判断
var detectCycle = function(head) { let slow = head let fast = head let start = head while(fast && fast.next){ fast = fast.next.next slow = slow.next if(slow === fast){ while(start&& slow){ if(start === slow){ return start } slow = slow.next start = start.next } } } return null};
4. 142 用O(1)的空间复杂度返回环形入口的节点
5. 练习
1.单向链表
2. 双向链表
1. 约瑟夫环
3. 环形链表
4. 循环链表
5. 双向循环链表
1. 二分查找
6. 跳表
1. 数组和链表配合
7.哈希表
4.链表
练习: 100 相同的树
练习
226 翻转二叉树
1.递归写法
var preorderTraversal = function(root) { let ret = [] let stack = [] let cur = root while(cur || stack.length > 0){ while(cur){ ret.push(cur.val) stack.push(cur) cur = cur.left } cur = stack.pop() cur = cur.right } return ret};
2. 循环迭代写法
144
1. 自 -> left -> right
1.前序遍历
94
1. left ->自己 -> right
1.中序遍历
145
1. left -> right -> 自
1.后序遍历
3. 遍历树
1.定义: 每个节点的值都大于左子树所有的值,并且小于右子树的值
1.迭代
2.递归
3. 可以使用中序遍历
1. 验证二叉搜索树 98
235 二叉搜索树的最近公共祖先
236 二叉树的最近公共祖先
二叉搜索树
// 迭代
1. 二叉树的最大深度 104
二叉树的深度
2. 二叉树
prop、children递归, React15
节点tree多了容易卡顿、不好中止
1.虚拟dom
1. 链表结构,diff过程可中断,继续diff
2. React16
2. filber
3. 架构
1. 递归 + 回溯
广度优先遍历
深度优先遍历
2. leetcode 46
1. 全排列
4. 练习
5. 树
37 数独问题
3. 皇后问题 51
2. 单词搜索 79
快排需要占用额外的存贮空间
912 冒泡排序、快排
26 数组原地去重返回length
26 删除数组中的重复项
11 盛水
js 自带的排序是 插入排序(数据量小)和快排的结合(数据量大)n*logn
15. 179 三数之和
新布置
哈希表
按位异或应用 leetCode 136
位运算
1.数据结构
1.leftpad
374
复杂的 log2n
50
69
1.二分思想
1. 从下至上
2. 子问题叠加
1.思路点
120 三角形最小路径和
递归自上而下
局部当前最优解
贪心算法
动态规划,全局最优解,DP,从下至上,找到最优解
背包问题
动态规划
322 零钱兑换
理解虚拟dom 有帮助
72 编辑距离
10 正则表达式匹配
2. 练习
2.动态规划(动态递推)
3. 哈希表
流式数据,打印前10名
最大堆
4. 堆
慢
数组、链表、跳表时间复杂度分析
logn
146 LRU缓存 https://leetcode-cn.com/problems/lru-cache/
为啥 redis 使用跳表(skiplist)而不是使用 red-black? https://www.zhihu.com/question/20202931
Linked List 的标准实现代码 https://www.geeksforgeeks.org/implementing-a-linked-list-in-java-using-class/
跳跃表 https://redisbook.readthedocs.io/en/latest/internal-datastruct/skiplist.html
Java 源码分析(LinkedList) http://developer.classpath.org/doc/java/util/LinkedList-source.html
Java 源码分析(ArrayList)http://developer.classpath.org/doc/java/util/ArrayList-source.html
跳表
5. 链表
插入操作O(1)
按照元素优先级取出
取出操作 O(logn)
6. 优先队列
有最近相关性可以用栈来解决
20 有效括号
7.练习
2.算法思想
xly
yx
算法小抄 https://labuladong.gitbook.io/algo/guan-yu-zuo-zhe
ds总结
算法
https://zhuanlan.zhihu.com/p/146500964
1. 策略模式
https://zhuanlan.zhihu.com/p/144832214
2. 发布订阅
https://zhuanlan.zhihu.com/p/145271407
3. 单例模式
设计模式
font-main
0 条评论
回复 删除
下一页