一、call apply bind的使用

        apply、bind 和 call 都用于改变函数中的 this 指向。call 通过 func.call(thisArg, arg1, arg2, ...) 的形式调用,参数逐个传递;apply 通过 func.apply(thisArg, [arg1, arg2, ...]) 的形式调用,参数以数组形式传递;bind 使用 func.bind(thisArg, arg1, arg2, ...) 返回一个新的函数,不立即执行,可以稍后调用并传递参数。

        注意事项如下:

  • 无法给箭头函数绑定this
  • apply、call 和 bind 都可以手动设置函数内部的 this 指向,但如果传递 null 或 undefined,this 将默认指向全局对象(非严格模式下是 window,严格模式下是 undefined)
  • bind 返回函数:bind 并不会立即执行函数,而是返回一个新的函数,可以稍后调用;这使其特别适合在事件处理程序等场景中使用。
  • 不可改变的 this:对于使用 bind 绑定了 this 的函数,其 this 指向是不可改变的,后续调用 call 或 apply 也无法重新改变它的 this。
// call apply and bind 给函数绑定this指向的方法
// 其中call apply会立即执行并返回结果, apply传入的是数组参数 
// bind会返回一个新的函数,可以在bind的时候传递部分参数,等调用再传递部分参数

// call

const person = {
    name: "Tom",
}

function func(num1, num2){
    console.log(this);
    return num1 + num2
}

console.log(func.call(person, 1 ,3 )); // Tom 4

// apply
console.log(func.apply(person,[1, 3])) // Tom 4

// bind
const newfunc = func.bind(person, 1, 2)
console.log(newfunc()); // Tom 3

const newfunc2 = func.bind(person)
console.log(newfunc2(1, 2)) // Tom 3

二、call apply bind 的手写代码实现

        在手动实现 call、apply 和 bind 时,关键步骤包括使用 Symbol 生成一个唯一的函数键,将目标函数临时赋值给 thisarg 对象的该键,从而改变 this 的指向。对于 myCall,函数通过展开参数直接调用,并传入单独的参数列表;myApply 则接收一个参数数组并通过展开运算符传递给函数。myBind 返回一个新函数,这个新函数在调用时会结合预设的参数和后续传入的参数,通过 call 方法执行原函数。最后,临时添加的函数属性在调用后被删除,以避免影响原对象。

// 手动实现call apply bind
Function.prototype.myCall = function(thisarg, ...args){
    const fn = Symbol("fn");
    thisarg[fn] = this;
    const res = thisarg[fn](...args)
    delete thisarg[fn]
    return res
}

Function.prototype.myAplly = function(thisarg, args){
    const fn = Symbol("fn")
    thisarg[fn] = this
    const res = thisarg[fn](...args)
    delete thisarg[fn]
    return res
}

Function.prototype.myBind = function(thisarg, ...args){
    return (...args2) => {
        return this.call(thisarg, ...args, ...args2)
    }
}

func.myCall(person, 1, 2) // Tom 3
func.myAplly(person, [1, 2]) // Tom 3
const newfunc3 = func.myBind(person, 1, 2)
console.log(newfunc3()) // Tom 3 

三、es6前的类实现与继承 class语法糖的使用

        下述代码展示了ES6中的class语法如何作为ES5函数构造器的语法糖,简化了类的创建和继承。在ES5中,类通过函数构造器定义成员变量,并使用原型链添加成员方法,继承则通过call调用父类构造函数并使用Object.create设置原型。ES6引入了class关键字,使类的定义更直观,包括构造函数、实例方法和继承(使用extends和super)。此外,ES6还支持静态方法(通过static定义,仅可通过类本身调用)和私有变量/方法(使用#前缀,外部无法访问),进一步增强了封装性和代码组织的清晰性。

// class的使用 class是es6中新增的语法糖,本质上还是函数
// 在es6之前我们使用函数构造器来创建类

// es6之前 成员变量
function Person(name){
    this.name = name;
}

// es6之前 成员方法
Person.prototype.sayName = function(){
    console.log(this.name);
}

const tom = new Person("Tom")

// es6之前实现继承
function Student(name, grade){
    // 调用父类构造函数,并且指定this指向未new出来的示例对象
    // 实现将父类的属性绑定到子类的实例对象上
    Person.call(this, name)
    this.grade = grade
}

// 继承父类的方法
Student.prototype = Object.create(Person.prototype,{
    constuctor: Student
})

const student = new Student("Jerry", 1)
student.sayName() // Jerry

// es6之后
class newPerson{
    name
    eps = 0.01
    constructor(name){
        this.name = name;
    }

    sayName(){
        console.log(this.name)
    }
}

const jerry = new newPerson("Jerry")
jerry.sayName() // Jerry

class newStudent extends newPerson{
    constructor(name, grade){
        super(name)
        // 动态创建属性
        this.grade = grade
    }

    getGrade(){
        console.log(this.grade)
    }
}

const newstudent = new newStudent("Jerry", 1)
newstudent.sayName() // Jerry
newstudent.getGrade() // 1

// 静态方法与隐私变量
// 静态方法是类的方法,不是实例对象的方法,可以直接通过类调用
// 隐私变量是在类内部定义的变量,外部无法访问(调试时可以访问)
class newPerson2{
    // 隐私变量
    #name
    // 静态变量
    static secret = "secret"
     
    constructor(name){
        this.#name = name;
    }

    sayName(){
        console.log(this.#name)
    }

    // 静态方法
    static sayHello(){
        console.log("Hello")
    }
    // 隐私方法
    #sayHello(){
        console.log("Hello #Hello")
    }

    getinfo(){
        this.#sayHello()
    }
}

const tom2 = new newPerson2("Tom")
tom2.sayName() // Tom
newPerson2.sayHello() // Hello
tom2.getinfo() // Hello #Hello

四、fetch的基础使用

// 下面将介绍fetch的使用, 常用的还有axios ajax,当然一般项目实践中使用axios
// fetch是es6新增的api,用于替代ajax,fetch是基于promise实现的,相当于axios的简化版本
// 下面将分别演示传递searchparams, json, formdata三种常见的场景

// 1. searchparams
const gerinfo = async () => {
    const params = new URLSearchParams()
    params.append("name", "Tom")
    params.append("age", 18)
    const res = await fetch("https://api.github.com/users/zhengyuanbo?" + params.toString())
    if(res.status >= 200 && res.status < 300){
        const data = await res.json()
        console.log(data)
    }else{
        console.log("error")
    }
    
}

// 2. json
const postinfo = async () => {
    const res = await fetch("https://api.github.com/users/zhengyuanbo", {
        method: "POST",
        headers: {
            "Content-Type": "application/json"
        },
        body: JSON.stringify({
            name: "Tom",
            age: 18
        })
    })
    if(res.status >= 200 && res.status < 300){
        const data = await res.json()
        console.log(data)
    }else{
        console.log("error")
    }
}

// 3. formdata
const postinfo2 = async () => {
    const formData = new FormData()
    formData.append("name", "Tom")
    formData.append("age", 18)
    const res = await fetch("https://api.github.com/users/zhengyuanbo", {
        method: "POST",
        body: formData
    })
    if(res.status >= 200 && res.status < 300){
        const data = await res.json()
        console.log(data)
    }else{
        console.log("error")
    }
}

五、generator的使用

        在同步场景中,生成器通过 yield 关键字暂停函数执行,并通过 next() 方法逐步恢复执行,每次调用 next() 返回包含 value 和 done 属性的对象,便于控制执行流程。在异步场景中,生成器可以通过 yield 暂停等待 Promise 的解决,然后使用链式的 then 方法处理异步结果,实现异步操作的顺序执行。然而,使用生成器处理异步任务较为繁琐,因此在 ES7 中引入了 async 和 await 语法糖,简化了异步代码的编写。

// 在处理异步问题当中,除了使用回调函数以及promise,还可以使用generator

// 首先看一下generator在同步场景中的使用
function* gen(){
    console.log("start")
    yield "1"
    console.log("middle")
    yield "2"
    console.log("end")
}

const g = gen()

let res = g.next() // start
console.log(res) // { value: '1', done: false }
res = g.next() // middle
console.log(res) // { value: '2', done: false }
res = g.next() // end
console.log(res) // { value: undefined, done: true }
// 通过上面的观察得出在generator中使用yield可以暂停函数的执行,通过next方法可以继续执行函数
// 执行next方法将会含有两个属性的对象,value是yield后面的返回值,done表示是否执行完毕,true为执行完毕,且此时value为undefined

// generator在异步场景中的使用
function* gen2(){
    yield fetch("https://api.github.com/users/zhengyuanbo")
    yield fetch("https://api.github.com/users/zhengyuanbo")
}

const g2 = gen2()

g2.next().value
.then(res => res.json())
.then(data => {
    console.log(data)
    return g2.next().value
})
.then(res => res.json())
.then(data => {
    console.log(data)
}).catch(err => {
    console.log(err)
})

// 从上面可以看到generator可以用于处理异步问题,但是generator的使用比较繁琐,所以es7中引入了async await语法糖

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部