js 准备
变量类型
1、值类型
undefined String number bool symbol
2、引用类型
object array null function
typeof运算符
识别所有值类型、识别函数。判断是否是引用类型
手写深拷贝
1、注意判断值类型和引用类型
2、注意判断是数组还是对象
3、递归
/** * 深拷贝 */ const obj1 = { age : 20 , name: 'xxx', address: { city:'beijing' }, arr:['a','b','c'] } const obj2 = deepClone(obj1) obj2.address.city = 'shanghai' console.log(obj1.address) function deepClone(obj = {}) { if(typeof obj !== 'object' || obj == null) { return obj } let res if( obj instanceof Array) { res = [] } else { res = {} } //遍历的应该是对象,除了对象都要返回 null array for (let key in obj) { if(obj.hasOwnProperty(key)) { // 保证 key 不是原型的属性 res[key] = deepClone(obj[key]) } } return res }
类型转换
1、字符串拼接 (100 + 10(110) 、100 + ‘10’(10010)、true + ‘10’)
2、== (100 == ‘100’、 0 == ‘’ 、0 == false 、 false == ‘’ 、 null == undefined)true (除了 == null 之外,其他一律用 ===)
3、if语句和逻辑运算 ( && / || )
truly变量 :!!a === ture
falsely变量 : !!a === false ( 0 / NaN / '' / null / undefined / false)
4、减号 ( 结果和运算时会转换为 'number' )
原型和原型链
1、class 和 继承
2、类型判断 instanceof
3、原型和原型链
class
1、constructor
2、属性
3、方法
class Student { constructor(name, number) { //属性 this.name = name this.number = number } sayHi() {//方法 console.log( `姓名 ${this.name} , 学会${ this.number}` ) } } const xialuo =new Student('夏洛',100) console.log(xialuo.name) console.log(xialuo.sayHi())
继承
//父类 class People { constructor(name) { this.name = name } eat () { console.log(`${this.name} eat something`) } } //子类 class Student extends People { constructor(name , number) { super(name); this.number = number } sayHi() { console.log( `姓名 ${this.name} , 学会${ this.number}` ) } } //子类 class Teacher extends People { constructor(name , mojor) { super(name); this.mojor = mojor } teach() { console.log( `姓名 ${this.name} , 教授${ this.mojor}` ) } } const xialuo =new Student('夏洛',100) console.log(xialuo.name) xialuo.sayHi() xialuo.eat() const wanlashi =new Teacher('王老师','语文') console.log(wanlashi.name) wanlashi.teach() wanlashi.eat()
类型判断 instanceof
[] instacneof Array //true
[] instanceof Object //true
原型和原型链
[].__proto__ === Array.prototype // true
原型关系
1、每个class都有显示原型prototype
2、每个实例都有隐式原型__proto__
3、实例的__proto__指向对应的class 的 prototype
原型的执行规则
获取属性xialuo.name或者执行方法xialuo。sayHi()时
1、先在自身属性和方法寻找
2、找不到沿着__proto__向上寻找
原型链
Array.prototype.__proto__ === Object.prototype // true
Array.hasOwnProperty('hasOwnProperty') // false
Object.prototype.hasOwnProperty('hasOwnProperty') // true
作用域和闭包
作用域
1、全局作用域
2、函数作用域
3、块级作用域
自由变量
1、一个变量在当前作用域没有定义,但被使用了
2、向上级作用域,一层一层依次寻找,直到找到为止
3、如果全局作用域没找到,报错 xx is not defined
闭包
//函数作为返回值 function create() { const a = 100 return function () { console.log(a) } } const fn = create() const a = 200 fn() //函数作为参赛被传递 function print(fn) { const b = 200 fn() } const b =100 function fcn() { console.log(b) } print(fcn) //闭包: 自由变量的查找,是在函数定义的地方,向上级作用域找 // 不是在执行的地方!!!
this(this取什么值是在函数执行的时候确定的)
1、作为普通函数
2、使用call apply bind (call可以直接执行,bind是返回一个新的函数)
3、作为对象方法被调用
4、在class 方法中调用
5、箭头函数
function fn1() { console.log(this) } fn1() // window fn1.call({x: 100}) //{x: 100} const fn2 = fn1.bind({x : 200}) fn2() // {x : 200}
手写bind
//模拟bind Function.prototype.bind1 = function (...a) { //将参赛拆解为数组 arguments是对象 const args = Array.prototype.slice.call(arguments) // 获取this (数组的第一项) const t = args.shift() // fn1.bind(...) 中的fn1 const self = this // 返回一个函数 return function () { return self.apply(t,args) } } function fn1(a, b, c) { console.log('this', this) console.log(a, b, c) console.log(arguments) return 'this is fn1' } const fn2 = fn1.bind1({x : 100} , 10, 20, 30) const res = fn2() console.log(res)
实际开发中的闭包应用
1、隐藏数据
2、缓存数据
//闭包隐藏数据, 只提供 API function createCache() { const data = {} return { set: function (key, val) { data[key] = val }, get: function (key) { return data[key] } } } const c = createCache() c.set('a',100) console.log(c.get('a'))//100 const d = createCache() d.set('a',200) console.log(d.get('a'))//200 console.log(c.get('a'))//100
单线程和异步
1、js是单线程语言,只能同时做一件事
2、浏览器和node.js已经支持js启动进程 , web worker
3、js 和 DOM 渲染共用同一个线程,因为JS可修改 DOM 结构
遇到等待(网络请求,定时任务)不能卡主 ----需要异步---回调callback
异步和同步
1、基于JS是单线程语言
2、异步不会阻塞代码执行
3、同步会阻塞代码执行
event loop(事假循环/事件轮询)
1、J是单线程运行
2、异步要基于回调实现
3、event loop就是异步回调的实现原理
event loop 过程1
1、同步代码,一行一行执行放在Call Stack执行
2、遇到异步,会先记录下,等待时机(定时,网络请求)
3、时机到了,就移动到Callback Queue
event loop 过程2
1、如果Call Stack 为空(同步代码执行完 )Event Loop 开始工作
2、轮询查找Callback Queue , 如有则移动到Call Stack执行
3、然后继续轮询查找
DOM事件和event loop
js是单线程
异步使用回调,基于event loop
DOM事件也使用回调,基于event loop
propmise的三种状态
*pending resolved rejected
*pending --> resolved 或 pending -->rejected
*变化不可逆
resolved 触发 then
rejected 触发 catch
async/await
1、异步回调callback hell
2、Promise then catch 链式调用,但也是基于回调函数
3、async/await 是同步语法,彻底消灭回调函数
asycn/await 和Promise 的关系
1、执行async函数,返回的是promise对象
2、await相当于promise的then
3、try...catch 可捕获异常,代替了promis的catch
async function async1() { console.log('async1 start') //2 await async2()//返回值是undefined // await 的后面,都可以看做是callback 里的内容,即异步 console.log('async1 end') //5 } async function async2 () { console.log('async2') //3 重要 } console.log('script start')//1 async1() console.log('script end') //4 //同步代码执行完
for ... of
for ... in( forEach ) 是常规的同步遍历
for ... of 常用于异步的遍历
function muti (num) { return new Promise(resolve => { setTimeout(()=> { resolve(num * num) }, 1000) }) } const nums = [1, 2, 3] nums.forEach( async (i)=> { const res = await muti(i) console.log(res) }) //1s 后同时输出1 4 9 !(async function () { for (let i of nums) { const res = await muti(i) console.log(res) } })() //1s后执行一次
宏任务macroTask 和微任务 microTask
宏任务:setTimeout, setinterval, Ajax ,Dom事件
微任务:Promise async/await
微任务执行时机比宏任务要早
event loop 和 DOM渲染
1、再次回归一遍event loop 的过程
2、JS是单线程的,而且和DOM渲染共用一个线程
3、JS执行的时候,得留一些时机供DOM渲染
event loop轮询时 + DOM渲染
1、每次 Call Stack 清空 同步执行完
2、尝试DOM渲染(都是DOM重新渲染的机会,DOM结构如有改变则重新渲染)
3、然后再去触发下一次Event Loop
微任务和宏任务的区别
宏任务:DOM渲染后触发,setTimeout
微任务:DOM渲染前触发,Promise
const $p1 = `<p> 这是一段文字</p>` document.querySelector('body') .append($p1) //微任务:DOM渲染前触发 Promise.resolve().then(()=> { console.log('length1', document.querySelector('body').children.length) alert('Promise then') //DOM 没有渲染 }) //宏任务:DOM渲染后触发 setTimeout(()=> { console.log('length2', document.querySelector('body').children.length) alert('setTimeout') //DOM 渲染了 })
event loop 解释,为什么微任务比宏任务先
微任务是ES6语法规定的
宏任务是浏览器规定的
所以他们存储的地方不一样
1、Call Stack 清空
2、执行当前的微任务
3、尝试触发DOM渲染
4、触发Event Loop
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人