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

 

 

 

posted @   前端路远且长  阅读(26)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示