JS原理-笔记(1/3)

JS原理-笔记(1/3)

知识点自测

今天课程中涉及到的已学习知识点

  1. 函数的call方法-文档链接
// 以指定的this调用函数,并通过 从第二个参数开始依次传递参数
function func(food,drink){console.log(this)console.log(food)console.log(drink)
}
const obj = {name:'小黑'
}
func.call(obj,'西蓝花','咖啡')
  1. 函数的apply方法-文档链接
// 以指定的this调用函数,并通过 数组的形式 传递参数
function func(food,drink){console.log(this)console.log(food)console.log(drink)
}
const obj = {name:'小黑'
}
func.apply(obj,['西蓝花','咖啡'])
  1. 函数的bind方法-文档链接
function func(food, drink) {console.log(this)console.log(food)console.log(drink)
}
const obj = {name: '小黑'
}
const bindFunc = func.bind(obj, '花菜')
bindFunc('可乐')
  1. 剩余参数-文档链接
function func(...args){console.log(args)// 以数组的形式获取传入的所有参数
}
func('西蓝花','西葫芦','西洋参','西芹')
  1. Promise核心用法-文档链接
const p = new Promise((resolve, reject) => {setTimeout(() => {const num = parseInt(Math.random() * 10)if (num > 5) {resolve(`成功啦--${num}`)} else {reject(`失败啦--${num}`)}}, 1000)
})
p.then(res => {console.log(res)
}, err => {console.log(err)
})
  1. URLSearchParams核心用法-文档链接
// 实例化时支持传入JS对象
const params = new URLSearchParams({ name: 'jack', age: 18 })
// toString方法 返回搜索参数组成的字符串,可直接使用在 URL 上。
console.log(params.toString())
  1. Object.create核心用法-文档链接
  const person = {name: 'itheima',foods: ['西蓝花', '西红柿', '西葫芦']}// 将传入的对象作为原型,创建一个新对象(浅拷贝)const clone = Object.create(person)clone.name = 'itheima'clone.foods.push('西北风')console.log(clone.foods === person.foods)// true
  1. Object.assign核心用法-文档链接
  const person = {name: 'itheima',foods: ['西蓝花', '西红柿', '西葫芦']}const son = {name: 'rose',}// 参数1 目标对象// 参数2 源对象// 将源对象的自身属性复制到目标对象,并返回目标对象const returnTarget = Object.assign(son, person)console.log(returnTarget === son)// trueconsole.log(son.name)// itheimaconsole.log(son.foods === person.foods)// true

JS中的this

这一节咱们来学习JS中this相关的知识点

传送门:MDN-this

传送门:MDN-call

传送门:MDN-apply

传送门:MDN-bind

传送门:MDN-箭头函数

传送门:MDN-剩余参数

传送门:MDN-Symbol

这一节咱们会学习的有:

  1. 如何确认this指向
  2. 如何改变this指向
  3. 手写callapplybind

如何确认this的值:

在非严格模式下,总是指向一个对象,在严格模式下可以是任意值,开启严格模式可以使用如下两种方式:

  1. 在整个脚本顶部开启
  2. 在函数顶部开启
// 为整个脚本开启严格模式
'use strict'
function func() {// 为函数开启严格模式'use strict'
}

然后就可以根据不同的模式来确认this指向啦,

  1. 全局执行环境中,指向全局对象(非严格模式、严格模式)

  2. 函数内部,取决于函数被调用的方式

    1. 直接调用的this值:

      1. 非严格模式:全局对象(window)
      2. 严格模式:undefined
    2. 对象方法调用的this值:

      1. 调用者
// 1.全局执行环境
// 非严格模式: 不做任何设置,直接写就是非严格模式
// console.log(this) // window
// 严格模式: 代码顶部加上 'use strict' 即可
// 'use strict' // 为整个脚本开启严格模式
// console.log(this) // window// 2.函数内部
//  2.1 直接调用-非严格模式
// function func() {
//   console.log(this) // 全局对象window
// }
// func()//  2.1 直接调用-严格模式
// function func() {
//   'use strict'
//   console.log(this) // undefined
// }
// func()//  2.2 对象方法调用
// const food = {
//   name: '猪脚饭',
//   eat() {
//     console.log('吧唧吧唧')
//     console.log(this)
//   }
// }
// food.eat()

面试回答:

如何确认this指向:

  1. 全局执行环境中,指向全局对象(非严格模式、严格模式)

  2. 如何开启严格模式:

    // 为整个脚本开启严格模式
    'use strict'
    function func() {// 为函数开启严格模式'use strict'
    }
    
  3. 函数内部,取决于函数被调用的方式

    1. 直接调用的this值:

      1. 非严格模式:全局对象(window)
      2. 严格模式:undefined
    2. 对象方法调用时的this值为调用者

如何改变this指向

主要有2类改变函数内部this指向的方法:

  1. 调用函数并传入具体的this:

    1. call:

      1. 参数1:this
      2. 参数2-n:传递给函数的参数
    2. apply-数组作为参数

      1. 参数1:this
      2. 参数2:以数组的形式,传递给函数的参数
  2. 创建绑定this的函数:

    1. bind:返回一个绑定了this的新函数
    2. 箭头函数:最近的this是谁,就是谁

调用函数并传入具体的this:

function funcA(p1, p2) {console.log('funcA-调用')console.log(this)console.log('p1:', p1)console.log('p2:', p2)
}
const obj = {name: 'jack'
}
// call参数
// 参数1 this值
// 参数2-参数n 挨个传入函数的参数
funcA.call(obj, 1, 2)
// apply参数
// 参数1 this值
// 参数2 以数组的形式传入函数的参数
funcA.apply(obj, [3, 4])

创建绑定this的函数:

function funcB(p1, p2) {console.log('funcB-调用')console.log(this)console.log('p1:', p1)console.log('p2:', p2)
}
const person = {name: 'itheima'
}
// bind参数
// 参数1 this值
// 参数2-参数n 绑定的参数
const bindFuncB = funcB.bind(person, 123)
bindFuncB(666)const student = {name: 'lilei',sayHi: function () {console.log(this)// 箭头会从自己作用域链的上一层继承thisconst inner = () => {console.log('inner-调用了')console.log(this)}inner()}
}
student.sayHi()

面试回答:

如何改变this指向,有2类改变this指向的方法,分别是:

  1. 调用函数时并传入具体的this

    1. call:从第二个参数开始挨个传递参数

    2. apply:在第二个参数以数组的形式传递参数

  2. 创建函数时绑定this?

    1. bind:返回一个绑定了this以及参数(可选)的新函数

    2. 箭头函数:创建时会绑定上一级作用域中的this

手写call方法

这一节咱们来实现myCall方法,实际用法和call方法一致,核心步骤有4步

const person = {name: 'itheima'
}
function func(numA, numB) {console.log(this)console.log(numA, numB)return numA + numB
}
// 参数1:指定的this值
// 参数2-参数n:原函数参数
const res = func.myCall(person, 2, 8)
console.log('返回值为:', res)
  1. 如何定义myCall?
  2. 如何让函数内部的this为某个对象?
  3. 如何让myCall接收参数2-参数n?
  4. 使用Symbol调优myCall
// 1. 如何定义`myCall`
Function.prototype.myCall = function () {// 逻辑略
}// 2 设置this并调用原函数
Function.prototype.myCall = function (thisArg) {// this 是调用myCall的 函数// thisArg 指定的this// 为他添加一个自定义属性,让函数成为他的该属性thisArg['fn'] = this// 调用并获取结果const res = thisArg['fn']()// 移除添加的自定义属性delete thisArg['fn']}// 3 接收剩余参数并返回结果
Function.prototype.myCall = function (thisArg, ...args) {thisArg['fn'] = this// 调用并获取结果const res = thisArg['fn'](...args)// 移除添加的自定义属性delete thisArg['fn']// 返回调用结果return res
}// 4 使用`Symbol`调优`myCall`
Function.prototype.myCall = function (thisArg, ...args) {// 使用Symbol生成唯一标记,避免和原属性冲突const fn = Symbol()thisArg[fn] = thisconst res = thisArg[fn](...args)// 移除添加的自定义属性delete thisArg[fn]// 返回调用结果return res
}// --------测试代码--------
const person = {name: 'itheima'
}
function func(numA, numB) {console.log(this)console.log(numA, numB)return numA + numB
}
// 参数1:指定的this值
// 参数2-参数n:原函数参数
const res = func.myCall(person, 2, 8)
console.log('返回值为:', res)

面试回答:

手写call方法的步骤为

  1. function的原型上添加myCall方法,保证所有函数都可以调用
  2. 方法内部,通过动态为对象添加方法的形式来指定this指向
  3. 调用完毕之后通过delete关键字删除上一步动态增加的方法
  4. 方法的名字通过Symbol进行设置,避免和默认名重复
  5. 使用剩余参数的形式传递参数2-参数n(函数参数)
Function.prototype.myCall = function (thisArg, ...args) {const fn = Symbol()thisArg[fn] = thisconst res = thisArg[fn](...args)delete thisArg[fn]return res
}

手写apply方法

这一节咱们来实现myApply方法,实际用法和apply方法一致,核心步骤依旧4

const person = {name: 'itheima’
}function func(numA, numB) {console.log(this)console.log(numA, numB)return numA + numB
}const res = func.myApply(person, [2, 8])
console.log('返回值为:', res)
  1. 如何定义myApply?

    1. 定义在原型上
  2. 如何让函数内部的this为某个对象?

    1. 动态给对象添加方法,通过对象.方法()调用即可
    2. 使用Symbol来生成方法名
  3. 如何让myApply接收参数?

    1. 定义参数2即可
    2. 传递给原函数时需要使用...展开
// 1. 如何定义`myApply`
Function.prototype.myApply = function () {// 逻辑略
}// 2 如何让函数内部的`this`为某个对象
Function.prototype.myApply = function (thisArg) {// 为他添加一个自定义属性,让函数成为他的该属性// 使用Symbol生成唯一标记,避免和原属性冲突const fn = Symbol()thisArg[fn] = thisconst res = thisArg[fn](...args)// 移除添加的自定义属性delete thisArg[fn]// 返回调用结果return res
}// 3 如何让`myApply`接收参数
Function.prototype.myApply = function (thisArg, args) {const fn = Symbol()thisArg[fn] = this// 调用并获取结果// 用... 将args展开传入const res = thisArg[fn](...args)delete thisArg['fn']// 返回调用结果return res
}// 测试代码
const person = {name: 'itheima’
}function func(numA, numB) {console.log(this)console.log(numA, numB)return numA + numB
}const res = func.myApply(person, [2, 8])
console.log('返回值为:', res)

面试回答:

手写apply方法

  1. function的原型上添加myApply方法,保证所有函数都可以调用
  2. 方法内部,通过动态为对象添加方法的形式来指定this指向
  3. 调用完毕之后通过delete关键字删除上一步动态增加的方法
  4. 方法的名字通过Symbol进行设置,避免和默认名重复
  5. 直接使用数组传递函数的参数,内部调用时结合...运算符展开数组
Function.prototype.myApply = function (thisArg, args) {const fn = Symbol()thisArg[fn] = thisconst res = thisArg[fn](...args)delete thisArg[fn]return res
}

手写bind方法

这一节咱们来实现myBind方法,实际用法和bind方法一致,核心步骤为2步

const person = {name: 'itheima'
}function func(numA, numB, numC, numD) {console.log(this)console.log(numA, numB, numC, numD)return numA + numB + numC + numD
}const bindFunc = func.myBind(person, 1, 2)const res = bindFunc(3, 4)
console.log('返回值:', res)
  1. 如何返回一个绑定了this的函数?
  2. 如何实现绑定的参数,及传入的参数合并?
// 1 如何返回一个绑定了`this`的函数
Function.prototype.myBind = function (thisArg) {// myBind函数调用时,this就是函数本身 return () => {// 通过call方法将传入的 thisArg 作为this进行调用this.call(thisArg)}
}// 2 如何实现绑定的参数,及传入的参数合并
// ...args 接收绑定参数
Function.prototype.myBind = function (thisArg, ...args) {// ...args2 接收调用时的参数return (...args2) => {// thisArg 需要指定的this// args 调用myBind时传入的参数// args2 调用新函数时传入的参数return this.call(thisArg, ...args, ...args2)}
}// 测试代码
const person = {name: 'itheima'
}function func(numA, numB, numC, numD) {console.log(this)console.log(numA, numB, numC, numD)return numA + numB + numC + numD
}const bindFunc = func.myBind(person, 1, 2)const res = bindFunc(3, 4)
console.log('返回值:', res)

面试回答

手写bind方法

  1. function原型上添加myBind函数,参数1为绑定的this,参数2-参数2为绑定的参数
  2. 内部返回一个新箭头函数,目的是绑定作用域中的this
  3. 返回的函数内部,通过call进行this和参数绑定
  4. 通过call的参数2和参数3指定绑定的参数,和调用时传递的参数
Function.prototype.myBind = function (thisArg, ...args) {return (...args2) => {return this.call(thisArg, ...args, ...args2)}
}

JS继承-ES5

这一节咱们来学习如何在JS中实现继承,首先看看在ES6之前可以如何实现继承

传送门:继承与原型链

传送门:继承(计算机科学)

传送门:JavaScript高级程序设计

传送门:MDN-Object.create

传送门:MDN-Object.assign

**继承:**继承可以使子类具有父类的各种属性和方法,而不需要再次编写相同的代码

这一节咱们会学习ES5中常见的继承写法(命名来源于 《JavaScript高级程序设计》)

  1. 原型链实现继承
  2. 构造函数继承
  3. 组合继承
  4. 原型式继承
  5. 寄生式继承
  6. 寄生组合式继承
// 父类
function Parent(){this.name = namethis.foods = ['西蓝花', '西红柿']this.sayFoods = function () {console.log(this.foods)}
}

ES5-原型链实现继承

**核心步骤:**希望继承谁,就将谁作为原型

**缺点:**父类中的引用数据类型,会被所有子类共享

// 父类
function Parent(name) {this.name = namethis.foods = ['西蓝花', '西红柿']this.sayFoods = function () {console.log(this.foods)}
}
// 子类
function Son() {
}
// 将父类的实例 作为子类的原型
Son.prototype = new Parent('jack')
const s1 = new Son()
s1.sayFoods()// ['西蓝花', '西红柿']const s2 = new Son()
s2.sayFoods() // ['西蓝花', '西红柿']s2.foods.push('西葫芦')s2.sayFoods()// ['西蓝花', '西红柿', '西葫芦']
s1.sayFoods()// ['西蓝花', '西红柿', '西葫芦']

面试回答:

ES5-原型链实现继承

  1. 将父类的实例作为子类的原型实现继承
  2. 这种继承方法的缺点是父类中的引用类型数据会被所有子类共享

ES5-构造函数继承

**核心步骤:**在子类的构造函数中通过callapply父类的构造函数

**缺点:**子类没法使用父类原型上的属性/方法

// 父类
function Parent(name) {this.name = name
}
Parent.prototype.sayHi = function () {console.log('你好,我叫:', this.name)
}// 子类
function Son(name) {Parent.call(this, name)
}const s1 = new Son('lucy')
const s2 = new Son('rose')
s1.sayHi() // 报错

面试回答:

ES5-构造函数继承

  1. 在子类的构造函数中通过call或者apply调用父类的构造函数
  2. 这种继承方法的缺点是:子类没法使用父类原型上的属性/方法

ES5-组合继承

通过组合继承,结合上面2种方法的优点

核心步骤:

  1. 通过原型链继承公共的属性和方法
  2. 通过构造函数继承实例独有的属性和方法

**特点:**调用了2次构造函数

// 父类
function Person(name) {this.name = name
}
// 方法加父类原型上
Person.prototype.sayHi = function () {console.log(`你好,我叫${this.name}`)
}
// 子类构造函数
function Student(name, age) {// 调用父类构造函数传入thisPerson.call(this, name)// 子类独有的属性和方法单独设置this.age = age
}
// 设置子类的原型为 父类实例
Student.prototype = new Person()
// 调用子类的构造函数
const s = new Student('李雷', 18)
// 可以使用原型链上的 属性和方法 也可以使用 通过构造函数获取的父类的属性和方法

面试回答:

ES5-组合继承

  1. 组合继承的核心步骤有2步:

    1. 通过原型链继承公共的属性和方法
    2. 通过构造函数继承实例独有的属性和方法
  2. 组合继承的特点:调用2次父类的构造函数,浪费性能

ES5-原型式继承

直接基于对象实现继承

**核心步骤:**对某个对象进行浅拷贝(工厂函数或Object.create),实现继承

**缺点:**父类中的引用数据类型,会被所有子类共享

// 可以用 Object.create替代
function objectFactory(obj) {function Fun() { }Fun.prototype = objreturn new Fun()
}
const parent = {name: 'parent',age: 25,friend: ['rose', 'ice', 'robot'],sayHi() {console.log(this.name, this.age)}
}
const son1 = objectFactory(parent)
const son2 = objectFactory(parent)
son1.friend.push('lucy')
console.log(son2.friend)

面试回答:

ES5-原型式继承

  1. 原型式继承的核心步骤是:对某个对象进行浅拷贝,可以通过内置apiObject.create实现,不需要调用构造函数即可实现继承,主要针对于继承对象的情况
  2. 原型式继承的缺点是:父类中的引用数据类型,会被所有子类共享

ES5-寄生式继承

核心步骤:

定义工厂函数,并在内部:

  1. 对传入的对象进行浅拷贝(公共属性/方法)
  2. 为浅拷贝对象增加属性/方法(独有属性/方法)
function createAnother(origin) {// Object.create基于原型创建新对象,对属性进行浅拷贝const clone = Object.create(origin)// 为对象增加属性/方法clone.sayHi = function () {console.log('你好')}return clone
}
const parent = {name: 'parent',foods: ['西蓝花', '炒蛋', '花菜']
}
const son1 = createAnother(parent)
const son2 = createAnother(parent)

面试回答:

寄生式继承

  1. 寄生式继承的核心步骤是:基于对象创建新对象(可以使用Object.create),并且为新创建的对象增加新的属性和方法

  2. 寄生式继承和上一节学习的原型式继承的区别是:创建出来的新对象,会额外的增加新的属性/方法

ES5-寄生组合式继承

所谓寄生组合式继承,即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。其背 后的基本思路是:不必为了指定子类型的原型而调用超类型的构造函数,我们所需要的无非就是超类型 原型的一个副本而已。

核心步骤:

  1. 通过构造函数来继承属性
  2. 通过原型链来继承方法
// 继承原型函数
function inheritPrototype(son, parent){const prototype = object.create(parent.prototype)prototype.constructor = sonson.prototype = prototype
}// 父类
function Parent(name) {this.name = namethis.foods = ['西蓝花', '西葫芦', '西红柿']
}Parent.prototype.sayHi = function () {console.log(this.name, `我喜欢吃,${this.foods}`)
}// 子类借用父类的构造函数
function Son(name, age) {Parent.call(this, name)this.age = age
}
// 完成原型继承
inheritPrototype(Son,Parent)
// 可以继续在原型上添加属性/方法
Son.prototype.sayAge = function () {console.log('我的年龄是', this.age)
}const son1 = new Son('jack', 18)
const son2 = new Son('rose', 16)

面试回答:

ES5-寄生组合式继承

  1. 寄生组合式继承的核心步骤是:通过构造函数来继承属性,通过原型链来继承方法
  2. 寄生组合式继承和组合式继承的区别是:原型链的继承并没有调用父类的构造函数,而是直接基于父类的原型创建一个新副本实现继承

JS继承-ES6

这一节咱们来学习在ES6中class关键字的使用,并且使用它来实现继承

传送门:mdn类

传送门:阮一峰ES6-class

传送门:mdn-super

ES6中推出了class类,是用来创建对象的模板.class可以看作是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。

这一节咱们会学习:

  1. class核心语法
  2. class实现继承
  3. class语法补充

class核心语法

核心语法:

  1. 如何定义及使用类:
  2. 如何定义实例属性/方法:
// 定义类
class Person {// 实例属性,方便一眼确认有哪些namefood// 构造方法,类似于构造函数,new的时候会调用,内部的this就是实例化的对象constructor(name, food) {this.name = namethis.food = food}// 实例方法sayHi() {console.log(`你好,我叫${this.name},我喜欢吃${this.food}`)}
}
const p = new Person('小黑', '西蓝花')
p.sayHi()

面试回答:

class核心语法:

  1. 通过class 类名{}的形式来定义类
  2. 内部直接写实例属性,可以设置默认值,
  3. 实例方法的添加方式为方法名(){}
  4. 构造函数通过constructor进行添加
  5. 通过new 类名()创建实例,会调用构造函数constructor
class Person{namefood='西兰花炒蛋'constructor(name){this.name=name}sayHi(){console.log('你好,我叫:',this.name)}
}

class实现继承

关键语法:

  1. 子类通过extends继承父类
  2. 子类构造函数中通过super调用父类构造函数
// 在上一份代码的基础上继续编写下面代码
class Student extends Person {songconstructor(name, food, song) {// 子类构造函数使用this以前必须调用supersuper(name, food)this.song = song}// 添加方法sing() {console.log(`我叫${this.name},我喜欢唱${this.song}`)}
}
const s = new Student('李雷', '花菜', '孤勇者')
s.sayHi()
s.sing()

面试回答:

class实现继承

  1. 子类通过extends继承继承父类
  2. 子类如果需要重新定义构造函数,必须在内部通过super关键字调用父类的构造函数

class私有,静态属性和方法

补充语法:

  1. 私有属性/方法的定义及使用(内部调用)
  2. 静态属性/方法的定义及使用(类直接访问)
class Person {constructor(name) {this.name = name}// 通过#作为前缀添加的属性会变为私有// 私有属性#secret = '我有一个小秘密,就不告诉你'// 私有方法#say() {// 私有属性可以在console.log('私有的say方法')}info() {// 在类的内部可以访问私有属性调用私有方法console.log(this.#secret)this.#say()}// 通过 static定义静态属性/方法static staticMethod() {console.log('这是一个静态方法')console.log(this)}static info = '直立行走,双手双脚'
}const p = new Person('jack')
console.log(p)
// 外部无法访问 点语法访问直接报错,通过[]无法动态获取
console.log(p['#secret'])
p.info()
// 通过类访问静态属性/方法
Person.staticMethod()
console.log(Person.info)

面试回答:

class语法补充

  1. class中私有属性/方法

    1. 定义和使用时需要使用关键字#
    2. 私有属性只能在类的内部使用,外部无法使用(代码中)
    3. Chrome的控制台中为了方便调试,可以直接访问
  2. class中静态属性/方法

    1. 定义和使用时需要使用关键字static
    2. 通过类访问
    3. 静态方法中的this是类

fetch

这一节咱们来学习内置函数fetch

传送门-fetch

传送门-Response

传送门-Headers

ajax&axios&fetch的关系:

ajaxajax 是一种基于原生 JavaScript 的异步请求技术。它使用 XMLHttpRequest 对象来发送请求和接收响应。

axiosaxios 是一个基于 Promise 的 HTTP 客户端,可以在浏览器和 Node.js 中使用。它提供了更高级别的封装,使发送请求和处理响应更加简单和灵活。

fetchfetch 是浏览器内置的 API,用于发送网络请求。它提供了一种现代化、基于 Promise 的方式来进行网络通信。用法和axios类似,但相比于 axios,它的功能和封装级别更为简单。

全局的fetch函数用来发起获取资源请求.他返回一个promise,这个promise会在请求响应后被resolve,并传回Response对象

这一节咱们会学习的有:

  1. fetch核心语法

  2. fetch结合URLSearchParams发送get请求:

    1. const obj = {name:'jack',age:18
      }
      name=jack&age=17
      
  3. fetch发送post请求,提交FormData数据

  4. fetch发送post请求,提交JSON数据

fetch核心语法

核心语法:

  1. 如何发请求:
  2. 如何处理响应:
  3. 注:测试用接口
fetch(资源地址,{...配置项对象})
.then(response=>{// 接收请求
})

面试回答:

fetch核心语法

  1. fetch函数的参数:
    1. 参数1:请求的url地址
    2. 参数2:以对象的形式设置请求相关的内容比如,方法,请求头,提交的数据等.
  2. fetch获取到响应结果,需要如何解析:
fetch(参数1,参数2)
.then(response=>{// 接收请求
})

fetch结合URLSearchParams发送get请求:

需求:

  1. 使用fetch结合URLSearchParams调用地区查询接口
;(async function () {const params = new URLSearchParams({pname: '广东省',cname: '广州市'})const url = `http://hmajax.itheima.net/api/area?${params.toString()}`// fetch函数返回的是 Promise对象,通过await等待获取response对象const res = await fetch(url)// .json方法返回的是Promise对象 继续通过await等待const data = await res.json()
})()

面试回答:

fetch结合URLSearchParams发送get请求:

  1. fetch发送get请求时,不需要设置请求方法,因为默认的就是get
  2. URLSearchParams可以用来创建或者解析查询字符串,这里通过它将对象转为查询字符串

post请求-提交JSON

需求:

  1. fetch发送post请求,提交JSON数据
  2. 测试接口-用户注册

核心步骤:

  1. 根据文档设置请求头
  2. 通过配置项设置,请求方法,请求头,请求体
    ; (async function () {// 通过headers设置请求头const headers = new Headers()// 通过 content-type指定请求体数据格式headers.append('content-type', 'application/json')// 参数1 url// 参数2 请求配置const res = await fetch('http://hmajax.itheima.net/api/register', {method: 'post',// 请求方法headers, // 请求头// 请求体body: JSON.stringify({ username: 'itheima9876', password: '123456' })})const json = await res.json()console.log(json)})()

面试回答:

post请求-提交JSON

  1. fetch函数的第二个参数可以设置请求头,请求方法,请求体,根据接口文档设置对应的内容即可
  2. 可以通过JSON.stringify将对象转为JSON

post请求-提交FormData

需求:

  1. fetch发送post请求,提交FormData数据(上传+回显)
  2. 测试接口-上传图片

核心步骤:

  1. 通过FormData添加文件
  2. 通过配置项设置,请求方法,请求体(FormData不需要设置请求头)
  <input type="file" class="file" accept="image/*"><script>document.querySelector('.file').addEventListener('change', async function (e) {// 生成FormData对象并添加数据const data = new FormData()data.append('img', this.files[0])const res = await fetch('http://hmajax.itheima.net/api/uploadimg', {method: 'post',body: data})const json = await res.json()console.log(json)})</script>

面试回答:

  1. post请求-提交FormData

    1. fetch提交FormData时不需要额外的设置请求头

    2. 实例化FormData之后可以通过append(key,value)方法来动态添加数据

  2. ajax&axios&fetch的关系

    1. ajax是一种基于原生JavaScript的异步请求技术,他使用XMLHttpRequest对象来发送请求和接收响应—学习原理时适用

    2. axios是一个基于Promise的http客户端,可以在浏览器和Node.js中使用.他提供了更高级别的封装,使发送请求和处理响应更加简单和灵活—项目开发时适用

    3. fetch是浏览器内置的API,用于发送网络请求.它基于Promise的方式来进行网络通信.和axios类似.但是功能和封装级别比axios更为简单—项目简单,不想导入axios时适用

Generator

这一节咱们来学习generator

传送门-Generator

Generator对象由生成器函数返回并且它符合可迭代协议和迭代器协议.他可以用来控制流程,语法行为和之前学习的函数不一样

Generator-核心语法

核心语法:

  1. 如何定义生成器函数:
  2. 如何获取generator对象
  3. yield表达式的使用
  4. 通过for of获取每一个yield的值
// 1. 通过function* 创建生成器函数 
function* foo() {// 遇到yield表达式时会暂停后续的操作yield 'a'yield 'b'yield 'c'return 'd'
}
// 2. 调用函数获取生成器
const f = foo()
// 3. 通过next方法获取yield 之后的表达式结果,会被包装到一个对象中
// 执行一次next 即可获取一次 yield之后的表达式结果
const res1 = f.next()
console.log(res1)// {value: 'a', done: false}
const res2 = f.next()
console.log(res2)// {value: 'b', done: false}
const res3 = f.next()
console.log(res3)// {value: 'c', done: false}
// 最后一次可以拿到return的结果
const res4 = f.next()
console.log(res4)// {value: 'd', done: true} 
// done 为true之后,获取到的value为undefined
const res5 = f.next()
console.log(res5)// {value: undefined, done: true} // 4. 通过for of 获取每一个yield之后的值,
const f2 = foo()
for (const iterator of f2) {console.log(iterator)
}

面试回答:

Generator-核心语法

  1. 可以通过生成器函数(function* xxx(){})来生成Generator对象:
  2. 通过Generator对象的next方法可以获取yield表达式之后的结果

Generator-id生成器

**需求:**使用Generator实现一个id生成器id

function* idGenerator() {// 逻辑略
}
const idMaker = idGenerator()// 调用next方法,获取id(每次累加1)
const { value: id1 } = idMaker.next()
console.log(id1)
const { value: id2 } = idMaker.next()
console.log(id2)

核心步骤:

  1. 定义生成器函数
  2. 内部使用循环,通过yield返回id并累加
// 1. 通过function* 创建生成器函数 
function* generator() {let id = 0// 无限循环while (true) {// id累加并返回yield id++}
}
// 2. 调用函数获取生成器
const idMaker = generator()
// 3. 需要id的时候 通过next获取即可
const { value: id1 } = idMaker.next()
console.log(id1)
const { value: id2 } = idMaker.next()
console.log(id2)

面试回答:

Generator-id生成器

  1. 生成器函数内部的代码会在调用next方法时执行,利用这一特点,可以实现任意的生成器,需要时调用next即可获取结果

Generator-流程控制

遇到yield表达式时会暂停后续的操作

**需求:**使用Generator实现流程控制

function* weatherGenerator() {// 逻辑略yield axios()
}
// 获取Generator实例
const weather = weatherGenerator()
// 依次获取 北上广深的天气 (axios)
weather.next()

核心步骤:

  1. yield后面跟上天气查询逻辑
  2. 接口文档-天气预报
  3. 参考code:北京 110100 上海 310100 广州 440100 深圳 440300
  <button class="getWeather">天气查询</button><script src="https://cdn.bootcdn.net/ajax/libs/axios/1.3.6/axios.js"></script><script>/*** 需求:流程控制,依次查询,北上广深的天气预报* 参考code: 北京 110100  上海 310100  广州 440100 深圳 440300* 接口文档: https://apifox.com/apidoc/project-1937884/api-49760220* */function* weatherGenerator() {// 北京yield axios('http://hmajax.itheima.net/api/weather?city=110100')// 上海yield axios('http://hmajax.itheima.net/api/weather?city=310100')// 广州yield axios('http://hmajax.itheima.net/api/weather?city=440100')// 深圳yield axios('http://hmajax.itheima.net/api/weather?city=440300')}const cityWeather = weatherGenerator()document.querySelector('.getWeather').addEventListener('click', async () => {const res = await genCity.next()console.log(res)})</script>

面试回答:

Generator-流程控制

  1. 使用Generator控制流程的本质是利用yield关键字来分隔逻辑比如示例中依次调用了多个接口,通过yield分隔,通过next来触发调用

参考资料

  1. 阮一峰-《ECMAScript 6 教程》
  2. 图灵社区-JavaScript高级程序设计

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/71363.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C语言】数据结构的基本概念与评价算法的指标

1. 数据结构的基本概念 1.1 基本概念和术语 1.1.1 数据 数据是信息的载体,是描述客观事物属性的数、字符及所有能输入到计算机中并被计算机程序识别和处理的符号的集合。数据是计算机程序加工的原料 1.1.2 数据元素 数据元素是数据的基本单位,通常作为一个整体进行考虑和…

vue知识点————插槽 slot

slot 插槽 在父组件中引用的子组件 在父组件中写入百度 可在子组件slot插槽中展示出 父组件 <template><div id"app"><child url"https://www.baidu.com">百度</child></div> </template><script> import chil…

如何评估以及优化谷歌ads

在广告投放一段时间后&#xff0c;应该对广告的效果有所了解。如果您的目标是增加销量和网站流量&#xff0c;米贸搜谷歌推广建议请考虑以下问题&#xff1a; 1.哪些关键字为广告带来的点击最多&#xff1f; 2.客户进行搜索时使用的是何种设备&#xff1f;他们来自何处&#xf…

C语言是否快被时代所淘汰?

今日话题&#xff0c;C语言是否快被时代所淘汰&#xff1f;在移动互联网的冲击下&#xff0c;windows做的人越来越少&#xff0c;WP阵营没人做&#xff0c;后台简单的php&#xff0c;复杂的大数据处理的java&#xff0c;要求性能的c。主流一二线公司基本上没多少用C#的了。其实…

ChatGLM学习

GLM paper&#xff1a;https://arxiv.org/pdf/2103.10360.pdfchatglm 130B&#xff1a;https://arxiv.org/pdf/2210.02414.pdf 前置知识补充 双流自注意力 Two-stream self-attention mechanism&#xff08;双流自注意机制&#xff09;是一种用于自然语言处理任务的注意力机制…

Feign负载均衡写法

Feign主要为了面向接口编程 feign是web service客户端&#xff0c;是接口实现的&#xff0c;而ribbon是通过微服务名字访问通过RestTemplate调用的&#xff0c;如下&#xff1a; 在Feign的实现下&#xff0c;我们只需要创建一个接口并使用注解的方式来配置它&#xff08;类似…

AcWing 4405. 统计子矩阵(每日一题)

如果你觉得这篇题解对你有用&#xff0c;可以点点关注再走呗~ 题目描述 给定一个 NM 的矩阵 A&#xff0c;请你统计有多少个子矩阵 (最小 11&#xff0c;最大 NM) 满足子矩阵中所有数的和不超过给定的整数 K ? 输入格式 第一行包含三个整数 N,M 和 K。 之后 N 行每行包含 …

代码随想录算法训练营day58 | LeetCode 739. 每日温度 496. 下一个更大元素 I

739. 每日温度&#xff08;题目链接&#xff1a;力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台&#xff09; 思路&#xff1a;新学习的单调栈做法&#xff0c;实质就是用一个栈存储中间结果。在针对数组某个元素的结果没找出来之前&#xff0c;将数…

OTFS-ISAC雷达部分最新进展(含matlab仿真+USRP验证)

OTFS基带参数设置 我将使用带宽为80MHz的OTFS波形进行设计&#xff0c;对应参数如下&#xff1a; matlab Tx仿真 Tx导频Tx功率密度谱 帧结构我使用的是经典嵌入导频帧结构&#xff0c;Tx信号波形的带宽从右图可以看出约为80Mhz USRP验证 测试环境 无人机位于1m处 Rx导频Rx…

kubernetes 之 minikube折腾记

参考官网教程&#xff0c;链接&#xff1a; https://minikube.sigs.k8s.io/docs/start/ curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 sudo install minikube-linux-amd64 /usr/local/bin/minikube安装完启动minikube&#xff1a;…

算法通关村第十八关——排列问题

LeetCode46.给定一个没有重复数字的序列&#xff0c;返回其所有可能的全排列。例如&#xff1a; 输入&#xff1a;[1,2,3] 输出&#xff1a;[[1,2,3]&#xff0c;[1,3,2]&#xff0c;[2,1,3]&#xff0c;[2,3,1]&#xff0c;[3,1,2]&#xff0c;[3,2,1]] 元素1在[1,2]中已经使…

监控系统典型架构

监控系统典型架构如下&#xff1a; 从左往右看&#xff1a; 采集器是负责采集监控数据的&#xff0c;采集到数据之后传输给服务端&#xff0c;通常是直接写入时序库。 对时序库的数据进行分析和可视化。 告警引擎产生告警事件之后交给告警发送模块做不同媒介的通知。 可视化比…

【CUDA OUT OF MEMORY】【Pytorch】计算图与CUDA OOM

计算图与CUDA OOM 在实践过程中多次碰到了CUDA OOM的问题&#xff0c;有时候这个问题是很好解决的&#xff0c;有时候DEBUG一整天还是头皮发麻。 最近实践对由于计算图积累导致CUDA OOM有一点新的看法&#xff0c;写下来记录一下。包括对计算图的一些看法和一个由于计算图引发…

Unity 时间定时调度系统

C# Unity 时间定时调度系统 之前的文章也有写过时间调度系统&#xff0c;但是没有支持异步调度只有回调调度&#xff0c;而且效率和代码可读性不是很好&#xff0c;下面介绍一种更优质的时间调度系统 1.TimerAction 首先需要定义一个时间行为&#xff0c;每次延时后需要干什…

机器学习处理问题的基本路线

基本路线&#xff1a; 1.搭建环境/数据读入 2.数据分析 3.特征工程 4.建模调参 5.模型融合 异常处理&#xff1a; 通过箱线图&#xff08;或 3-Sigma&#xff09;分析删除异常值&#xff1b;BOX-COX 转换&#xff08;处理有偏分布&#xff09;&#xff1b;长尾截断&…

Redis基本了解

Redis 基于内存进⾏存储&#xff0c;⽀持 key-value 的存储形式&#xff0c;底层是⽤ C 语⾔编写的。 基于 key-value 形式的数据字典&#xff0c;结构⾮常简单&#xff0c;没有数据表的概念&#xff0c;直接⽤键值对的形式完成数据的管理&#xff0c;Redis ⽀持 5 种数据类型…

[machine Learning]推荐系统

其实严格来说推荐系统也是一种监督学习,我们需要根据已有数据进行预测,但是这种训练数据不是单纯的输入和输出问题,所以被归类为"超越监督学习"的一种? 今天去旁听了隔壁专业的机器学习课程,感觉自己的知识确实不是很系统,所以后面会找个机会把前面的代码给补充上.…

Qt打开及创建项目,运行程序(1)

安装之后&#xff0c; 1.文件->新建文件或项目 2.Application->Qt Widgets Application 3.自己设置名称和路径 4.这一步非常非常重要&#xff0c;要选择编译器&#xff0c;&#xff08;MinGW是可以在Qt里用&#xff0c;如果想与VS交互&#xff0c;要选择MSVC&#xff09…

iOS开发Swift-类型转换

1.Int或Double转字符串 let x 20 let y "\(x)" let z String(x)2.Double转Int(去掉小数点后面的) Int(1.9)3.Int转Double Double(1)4.向上转型 class A{//A父类 }class B: A{//B子类继承A }let a A() let b B()b as A //子类转化成父类5.向下转型 class A{//A…

uniapp 路由不要显示#

在Uniapp中&#xff0c;路由默认使用的是hash模式&#xff0c;即在URL中添加#符号。如果你不想在URL中显示#&#xff0c;可以切换为使用history模式。 要在Uniapp中使用history模式&#xff0c;可以按照以下步骤进行操作&#xff1a; 打开manifest.json文件。在"app&qu…