前端知识体系【上】

1.从几个问题说起

  1. window.onload和DOMContentLoaded区别? script为什么放在头部,为什么放在body(页面加载)
  2. JS创建10个<a>标签,点击弹出对应的序号1~10。(闭包,作用域)
  3. 手写节流throttle和防抖debounce(体验,性能优化)
  4. Promise解决的问题?(JS异步)

2.JS部分

2.1基础语法

题目

  1. typeof能判断的变量类型(基本变量类型)
  2. 什么时候用===和什么时候用==?(强制类型转化)
  3. 值类型和引用类型的区别
  4. 手写深浅拷贝

知识点

原始值类型(互相不干扰),引用类型(类似指针)

值存在栈中,引用类型存在堆中(存放的是地址,修改的是地址中的内容)
在这里插入图片描述
值类型存储需要的空间较少,为了复制较快,所以放在栈中,而且是完全拷贝
常见引用类型 Object(对象),数组(array),null
特殊引用类型function,但不用于存储数据

这篇文章系统有对引用的解释

数据类型

基本数据类型:Undefined、Null、Boolean、Number、String、Symbol (new in ES 6) !
引用类型有:Object 类型、Array 类型、Date 类型、RegExp 类型、Function 类型 等。

变量计算-类型转换

先举几个例子

const a = 100 + 10; //110
const b = 100 + '10'  //10010, String类型
const c = 100 + 'a'  // 100a

100 == '100'  //true
0 == ''         //true
0 == false//true
false == ''//true
null == undefined//true
  1. 字符串拼接
  2. ==运算符和===
//两种等价写法,因为undefine和null不能再转化为其他的
if (obj.a == null) { }
if (obj.a === undefined || obj.a === null) { }

规则:除了判断null之外,其余一律用===

  1. if和逻辑运算

if(真|假),主要是为了逻辑运算服务

  1. 真:!!a === true
  2. 假: !!a === false
    为假的有
    在这里插入图片描述
    在这里插入图片描述
深浅拷贝

这样是浅拷贝

const obj1 = {
    a: 1,
    b: {
        z: "shenzhen"
    },
    arr:['a','b','c']
}

const obj2 = obj1;
obj2.b.z = "guangzhou";
console.log(obj1.b.z)  //guangzhou

手写深拷贝

/**
 * 深拷贝
 * @param {Object} obj 
 */
/**
 * 深拷贝
 * @param {Object} obj 
 */
function deepClone(obj = {}) {
    //非引用类型,或者为空,直接返回(function不能深拷贝)
    if (typeof obj !== 'object' || typeof obj == null) {
        return obj;
    }

    //保存返回的结果, 并根据不同的类型初始化
    let res;
    if (obj instanceof Array) {
        res = [];
    } else {
        res = {};
    }

    for (const key in obj) {
        //拷贝不是原型中的属性,保证不能是原型的属性
        if (obj.hasOwnProperty(key)) {
            res[key] = deepClone(obj[key]);
        }
    }
    return res;
}

const obj2 = deepClone(obj1);
obj2.b.z = "guangzhou";
console.log(obj1.b.z)  //shenzhen

解答

  1. typeof能判断的变量类型(基本变量类型)
    1. 识别所有的值类型
    2. 识别函数
    3. 判断是否为引用类型(不可再细分)
  2. 什么时候用===和什么时候用==?见上文
  3. 值类型和引用类型的区别 见上文
  4. 手写深浅拷贝 见上文

2.2原型和原型链

es6之前,所有的对象都是基于原型的继承。

题目

  1. 如何判断一个变量是不是数组(instanceof发生了什么)
  2. 手写一个简易的jQuery,考虑插件和拓展性
  3. class的原型本质,怎么理解

知识点

class和继承

三部分:constructor,属性,方法
我的理解:class即一个模板,里面有属性和方法,可以通过class创建一个实例,即数据+模板
考虑以下代码

// 父类
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, major) {
        super(name)
        this.major = major
    }
    teach() {
        console.log(`${this.name} 教授 ${this.major}`)
    }
}

// 实例
const xialuo = new Student('夏洛', 100)
console.log(xialuo.name)
console.log(xialuo.number)
xialuo.sayHi()
xialuo.eat()

// 实例
const wanglaoshi = new Teacher('王老师', '语文')
console.log(wanglaoshi.name)
console.log(wanglaoshi.major)
wanglaoshi.teach()
wanglaoshi.eat()

子类继承了父类,所以默认拥有父类的sayHi方法,而且可以通过super调用父类的构造函数,从而给name赋初始值

实际上,ES6中的class是原型的语法糖

console.log(typeof People)  //function
console.log(typeof Student)	//function
原型和原型链

原型和类与继承的关系密不可分,再看以下代码

console.log(Student.prototype === xialuo.__proto__) // true
console.log(Teacher.prototype === wanglaoshi.__proto__)  //true
console.log(People.prototype.__proto__ === Object.prototype) //true
console.log(Object.prototype.__proto__) //null

为什么会这样呢?那就牵扯到原型链了,我们称__proto__为隐式原型,在创建每个实例时,都会有默认有一个__proto__属性。规则如下

  1. 每个class都有一个显示原型prototype
  2. 每个实例都有隐式原型__proto__
  3. 实例中的__proto__指向对应class的prototype


在这里插入图片描述
实际上每个原型都会层层往上,最终的原型链如下
在这里插入图片描述
所以

类型判断instanceof

instanceof 实际上就是顺着原型链逐步查找的过程,顺着原型链往上的,都为true
比如x instanceof y ,y只要是x的class或者x的祖先类,都会返回true(因为能顺着原型链找到

console.log(xialuo instanceof Student) //true
console.log(xialuo instanceof People)	//true
console.log(xialuo instanceof Object)	//true

所以在调用某个方法时候,可能某个实例没有这个方法,比如xialuo没有hasOwnProperty这个方法,但顺着原型链往上查找,可以找到Object的原型上有这个方法,所以xialuo也可以调用hasOwnProperty

console.log(xialuo.hasOwnProperty('number')) //true
//解释,是自己的属性,所以返回true

console.log(xialuo.hasOwnProperty('sayHi')) //false
//虽然本身有这个方法,但是sayHi在原型中,所以返回false

问题解答

上述知识点解答了两个问题,剩下的就是jQuery的设计问题了,代码如下

设计jQuery

    <p>我是p1</p>
    <p>我是p2</p>
    <p>我是p3</p>
class jQuery{
    constructor(selector) {
        const res = document.querySelectorAll(selector);
        for (let i = 0; i < res.length; i++) {
            this[i] = res[i];
        }
        this.selector = selector;
        this.length = res.length;
    }
    //返回第几个元素
    get(index) {
        return this[index];
    }
    //遍历,每个元素执行fn,传入一个函数
    each(fn) {
        for (let i = 0; i < this.length; i++) {
            fn(this[i]);
        }
    }

    //监听某个方法,要加则给全部的元素加上type方法
    on(type, fn) {
        this.each((elem) => {
            elem.addEventListener(type, fn);
        })
    }
    //..其余还可以扩展DOM API
}


const $p = new jQuery('p')
console.log($p.get(1).innerText) //我是p2
$p.each((elem)=>{ console.log(elem.innerText) }) 
/*
我是p1
我是p2
我是p3*/

$p.on('click', () => { console.log('click') })
//点击的时候就会打印click

考虑插件的拓展性

//插件,给显示原型添加
jQuery.prototype.dialog = function(){
    console.log("我是插件")
}

//考虑插件的拓展性,造轮子
class myJQuery extends jQuery {
    constructor(selector) {
        //包含了jQuery的所有功能
        super(selector)
    }

    //在此之上拓展自己的方法
    addClass(className) {

    }
    //...
    style(data) {

    }
}

//使用插件中的方法
$p.dialog()  //我是插件

2.3作用域和闭包

题目

  1. this的不用应用场景,应该如何取值
  2. 手写bind函数
  3. 实际开发中闭包的应用场景,举例说明
  4. 创建10个a标签,点击弹出对应的序号

知识点

作用域

作用域即变量的合法使用范围,即红框部分。ES6是块级作用域
在这里插入图片描述
作用域有三种

  1. 全局作用域
  2. 函数作用域
  3. 块级作用域,即花括号里面就是块
自由变量

定义:如果在某个作用域中使用了变量“a”,而变量“a”并未在该作用域中声明(在其它作用域中声明了),则该变量“a”即为自由变量
在这里插入图片描述

闭包

原理:
在这里插入图片描述
定义
在这里插入图片描述
闭包只是作用域应用的特殊情况,有两种表现

  1. 函数作为参数被传递
  2. 函数作为返回值被返回
// 函数作为返回值
function create() {
    const a = 100
    return function () {
        console.log(a)
    }
}

const fn = create()
const a = 200
fn() // 100
/*
解释:fn是在create内部被定义的,定义的时候会记住a,但在当前作用域无,往上查找,发现a为100
*/

// 函数作为参数被传递
function print(fn) {
    const a = 200
    fn()
}
const a = 100
function fn() {
    console.log(a)
}
print(fn) // 100
/*
解释:同上,fn定义的地方无a,上上一层的作用域有a,所以打印的a=100
*/

总结:所有的自由变量的查找,是在函数定义的地方,向上级作用域查找,不是在执行的地方!!!

this的问题

使用场景分为以下5种,记编号为1,2,3,4,5,this取什么值,由函数执行的时候确认
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
解释:fn1()在执行的时候上下文为window,故打印window(第一种)
fn1.call()改变了this的指向,且马上执行(第二种)
fn1.bind()功能和fn1.call()一样,但区别是不回马上执行,而是返回一个函数,所以需要再执行再打印(第二种)
在这里插入图片描述
sayHi()解释:sayHi执行时,是由对象的方法触发的,所以this指向的是当前对象(第三种)
wait()解释:wait中打印时,是由setTimeout函数触发的(第一种)
waitAgain():箭头函数虽然是setTImeout触发的,但由于箭头函数的特点:即永远取上级作用域中的this,即waitAgain中的this,所以也是当前对象(第五种)

第四种:
在这里插入图片描述
再附上一个题的链接

解答

  1. this的不用应用场景,应该如何取值。(见上文,共有5中情况
  2. 手写bind函数
    bind的位置位于Functioni的原型对象之中
console.log(Function.prototype.hasOwnProperty('call')) //true
console.log(Function.prototype.hasOwnProperty('bind'))  //true

bind使用举例

const fn1 = function (a, b, c) {
    console.log(this);
    console.log(a, b, c);
    return 'this is fn1'
}

const fn2 = fn1.bind({ x: 100 }, 10, 20, 30);
const fn3 = fn1;
fn2();
/*
{x:1 00}
10 20 30
*/
fn3();
/*
window
10 20 30
*/

先补以下apply、call、bind这些基础知识,这三者都是修改this的指向,apply和bind只是传入的参数不同,call会立马执行。
在这里插入图片描述

const fn1 = function (a, b, c) {
    console.log(this)
    console.log(a, b, c);
    return 'this is fn1'
}
Function.prototype._bind = function () {
    //获取参数
    const args = Array.prototype.slice.call(arguments);
    //等价于args = Array.from(arguments); args = [...arguments]
    //第一个参数是t是传入的想要绑定到的数据,即{x: 100}
    const t = args.shift();
    //this指向调用_bind的函数,即fn1
    const self = this
    //返回一个函数,注意apply的参数是数组,而bind后面的参数是,分割的参数
    return function () {
        self.apply(t, args)
    }
}
const fn2 = fn1._bind({ x: 100 }, 10, 20, 30)
fn2()  //{ x: 100 }, 10, 20, 30

类似于上面的思路,可以写出call函数

Function.prototype._call = function () {
    //获取参数,其实可以不用call
    const args = Array.prototype.slice.call(arguments);
    //第一个参数是t是传入的想要绑定到的数据,即{x: 100}
    const thisArg = args.shift();
    //新添加一个fn, 保存this,即fn1
    thisArg.fn = this;
    //传入参数并执行
    const res = thisArg.fn(...args);
    //工具人没用了,丢掉它
    delete thisArg.fn;
    return res;
}
fn1._call({ x: 100 }, 10, 20, 30)
  1. 实际开发中闭包的应用场景,举例说明
//闭包隐藏数据,只提供API
function createCache() {
    //闭包中的数据,只能通过闭包内定义的方法访问,外部无法直接访问
    const data = {
        a : 1
    }

    return {
        set: function (key, val) {
            data[key] = val;
        },
        get: function (key) {
            return data[key];
        }
    }
}

const c = createCache();
console.log(c.get('a')) //1

c.set('c', "new data")
console.log(c.get('c')) //new data

但是如果不合理使用闭包会导致内存泄漏的问题,在闭包内部引用的对象,只要funciton还在作用域内,就不会被垃圾回收

如果看得不够爽,再来一篇闭包的文章
5. 创建10个a标签,点击弹出对应的序号

//注意定义域的问题,所以i要写在这个位置
for (let i = 0; i < 10; i++) {
    let a = document.createElement('a');
    a.innerHTML = i + '<br>';

    a.addEventListener('click', (e) => {
        e.preventDefault();
        alert(i);
    })

    document.body.append(a);
}

2.4异步和单线程

题目

  1. 同步和异步的区别是什么
  2. 手写Promise加载一张图片
  3. 前端使用异步的场景有哪些
  4. 如下:
    在这里插入图片描述

知识点

异步和单线程
  1. 异步基于js是单线程语言,只能同时做一件事。同步会阻塞代码的执行,而异步不会阻塞代码的执行
  2. 所以遇到等待(网络请求,定时任务)不能卡住,需要异步,且基于callback调用,异步基于函数来执行
应用场景
  1. 网络请求
  2. 定时任务,setTimeOut

这里返回数据时执行回调函数
在这里插入图片描述
这里onload是回调函数
在这里插入图片描述
定时任务
在这里插入图片描述

callback hell和Promise

如果要获取数据的顺序是data1=>data2=>data3,可能引起回调地狱的问题,Promise就解决了这个问题
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

解答

  1. 同步和异步的区别是什么 (见上图
  2. 手写Promise加载一张图片
const loadImg = url => {
    //返回一个Promise对象
    return new Promise((resolve, reject) => {
        const img = document.createElement('img');
        img.onload = () => {
            //因为是加载图像,所以需要把img传入resolve,知道在成功时返回的then是什么
            resolve(img);
        }
        img.onerror = () => {
            //自定义加载失败时候then接受的参数
            const err = new Error(`图片加载失败 ${url}`);
            reject(err);
        }
        //这里会触发上面的onload和onerror时间,从而使用promise
        img.src = url;
    })
}
const url1 = "https://i-blog.csdnimg.cn/blog_migrate/84e53527fd054885c728cc034840d4e8.png"

loadImg(url1).then(img => {  //可以通过then来接受resolve状态的元素和reject状态的元素
    console.log(img.width);
    return img;             //返回的是img元素
}).then(img => {
    console.log(img.height);
    return loadImg(img.src);    //返回的是promise
})

如果需要先加载图片1,再加载图片2,可以这样,这样就解决了回调地狱的问题

const url1 = "https://i-blog.csdnimg.cn/blog_migrate/84e53527fd054885c728cc034840d4e8.png"
const url2 = "https://i-blog.csdnimg.cn/blog_migrate/4c46eb231627933d4b9ff04fdc9be31a.png"

loadImg(url1).then(img => {  //可以通过then来接受resolve状态的元素和reject状态的元素
    console.log(img.width);   //705
    return img;             //返回的是img元素
}).then(img => {
    console.log(img.height);  //609
    return loadImg(url2);    //返回的是promise
}).then(img2 => {
    console.log(img2.width);   //895
    return img2;
}).then(img2 => {
    console.log(img2.height);  //714
    return img2;
}).catch(err => {
    console.log(err)
});
  1. 前端使用异步的场景有哪些(加载数据,如加载图片,定时任务,见上
  2. 答案13542

2.5ECMA 262标准

主要是js的各种内置对象和api需要熟练掌握

3.JS-web-api

js Web APi是网页操作api,是W3C定义的标准

3.1DOM(Document Object Model)

vue和React框架应用广泛,封装了DOM操作

题目

  1. DOM是哪种数据结构
  2. DOM操作的常用API
  3. attr和property
  4. 一次性插入多个DOM结点,考虑性能

知识点

DOM本质

DOM本质是一棵树,每个元素都是一个结点

DOM结点操作

获取dom结点,attibut,property

<style>
    .container {
        border: 1px solid #ccc;
    }
    .red {
        color: red;
    }
</style>
<div id="div1" class="container">
    <p id="p1">一段文字 1</p>
    <p>一段文字 2</p>
    <p>一段文字 3</p>
</div>
<div id="div2">
    <img src="https://img3.mukewang.com/5a9fc8070001a82402060220-100-100.jpg"/>
</div>
const div1 = document.getElementById('div1') //元素
console.log('div1', div1);  //打印id = div1的标签

const divList = document.getElementsByTagName('div') // 集合
console.log('divList.length', divList.length) // divList.length 2
console.log('divList[1]', divList[1])  //打印文档树中的第二个div标签

const containerList = document.getElementsByClassName('container') // 集合
console.log('containerList.length', containerList.length)
console.log('containerList[0]', containerList[0])

const pList = document.querySelectorAll('p')   //传入的是CSS选择器
console.log('pList', pList)
const p1 = pList[0]

参考了大佬的总结

区别

attribute 是我们在 html 代码中经常看到的键值对
property 是 attribute 对应的 DOM 节点的 对象属性 (Object field)

  1. attribute 会始终保持 html 代码中的初始值, 而 Property 是有可能变化的.
  2. 两者都可以操作dom,进而导致dom的重新渲染

其实, 我们从这两个单词的名称也能看出些端倪:
attribute 从语义上, 更倾向于不可变更的
而 property 从语义上更倾向于在其生命周期中是可变的

// property 形式
// 可以直接通过id获取某个标签
p1.style.width = '100px'
p1.className = 'red'
console.log( p1.className ) //将p1的类名修改为red
console.log(p1.nodeName) //p
console.log(p1.nodeType) // 1

// attribute
p1.setAttribute('data-name', 'imooc')
console.log(p1.getAttribute('data-name')) //imooc

p1.setAttribute('style', 'font-size: 50px;')
console.log( p1.getAttribute('style') )  //font-size: 50px;
DOM结构操作
  1. 增减、删除结点
  2. 移动结点
  3. 获取子元素、父元素
const div1 = document.getElementById('div1')
const div2 = document.getElementById('div2')

// 新建节点
const newP = document.createElement('p')
newP.innerHTML = 'this is newP'
// 插入节点
div1.appendChild(newP)

// 移动节点,对现有结点使用appendchild则会移动结点
const p1 = document.getElementById('p1')
div2.appendChild(p1)

// 获取父元素
console.log( p1.parentNode )

// 获取子元素列表,现在得到的结点包括了text
const div1ChildNodes = div1.childNodes
console.log( div1.childNodes )
const div1ChildNodesP = Array.prototype.slice.call(div1.childNodes).filter(child => {
    if (child.nodeType === 1) {
        return true
    }
    return false
})
console.log('div1ChildNodesP', div1ChildNodesP)

div1.removeChild( div1ChildNodesP[0] )

DOM性能

有以下几个方案
在这里插入图片描述

  1. 缓存
    在这里插入图片描述
    改为一次性操作
    未优化
const list = document.getElementById('list')
for (let i  = 0; i < 20; i++) {
    const li = document.createElement('li')
    li.innerHTML = `List item ${i}`

 	list.appendChild(frag)
}
console.log(list)

优化后

const list = document.getElementById('list')

// 创建一个文档片段,此时还没有插入到 DOM 结构中,frag是游离dom之外的

const frag = document.createDocumentFragment()

for (let i  = 0; i < 20; i++) {
    const li = document.createElement('li')
    li.innerHTML = `List item ${i}`

    // 先插入文档片段中
    frag.appendChild(li)
}

// 都完成之后,再统一插入到 DOM 结构中
list.appendChild(frag)

console.log(list)

解答

  1. DOM是哪种数据结构 (dom数
  2. DOM操作的常用API(获取结点、增加结点、移动结点,删除结点,获取父元素、子元素
  3. attr和property(见上
  4. 一次性插入多个DOM结点,考虑性能(见上

3.2BOM(Browser Object Model

题目

  1. 如何识别浏览器类型
  2. 分析拆解url的各个部分

知识点

navigator

可以通过浏览器的userAgent来判断浏览器的信息

const ua = navigator.userAgent
const isChrome = ua.indexOf('Chrome') > 0;
console.log(isChrome) //true
console.log(ua ) //Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36

screen

屏幕的高度和宽度,因屏幕大小而异

console.log(screen.height) //1536
console.log(screen.width) //864
location
location.href  //https://admission.pku.edu.cn/zsxx/sszs/index.htm?CSRFT=DD70-CXOT-TEKP-QJS6-52BO-8GWF-LX85-RLWP#zz
location.hostname //    "admission.pku.edu.cn"
location.protocol //    "https:"
location.pathname //    "/zsxx/sszs/index.htm"
location.search //  "?CSRFT=DD70-CXOT-TEKP-QJS6-52BO-8GWF-LX85-RLWP"
location.hash  //   "#zz"
history
history.forward()  //对应网页上的前进
history.back() //对应后退

解答

见上文

3.3事件绑定

题目

  1. 编写一个通用的事件监听函数
  2. 描述事件冒泡的流程
  3. 无限下拉的图片的列表,如何监听每个图片的点击

知识点

事件绑定

最简单的事件绑定

const btn = document.getElementById('eve_bind')
btn.addEventListener('click', e => {
    console.log(e.target)       //触发该事件的元素c
    e.preventDefault();           //如果是链接,默认行为是跳转,阻止默认行为
    console.log("click   lzz");
})

// 通用的事件绑定函数
function bindEvent(elem, type, fn) {
    elem.addEventListener(type, fn)
}
bindEvent(btn, 'click', e => {
    console.log("click btn")
})
事件冒泡

即某个dom元素触发了某个事件,如果dom的上级也有相同的事件,则事件会从最低层次的dom元素一层一层往上冒泡。整个路径上的dom元素都能够触发事件

    <button id="btn1">一个按钮</button>

    <div id="div1">
        <p id="p1">激活</p>
        <p id="p2">取消</p>
        <p id="p3">取消</p>
        <p id="p4">取消</p>
    </div>
    <div id="div2">
        <p id="p5">取消</p>
        <p id="p6">取消</p>
    </div>

绑定了p1和第div1的click事件,点击p1后,两个元素都会打印click,e.target打印的是p1。
通过加入e.stopPropagation();后,事件就不会往上冒泡
冒泡顺序 => 底层到上层

//普通的事件绑定函数
const p1 = document.getElementById('p1');
const div1 = document.getElementById('div1');

bindEvent(p1, 'click', e => {
    console.log("p1 click");
    //e.stopPropagation();
    console.log('click', e.target);
})
bindEvent(div1, 'click', e => {
    console.log("div1 click");
    console.log('click', e.target);
})
事件代理

有时候不想给每个元素都绑定事件,这样代码繁琐,且耗费浏览器内存。于是有了事件代理,基于事件冒泡,给祖先元素绑定事件,由祖先元素“代理”每个事件的响应

function bindEvent(elem, type, selector, fn) {
    //注意是两等, 可以不输入标签,则是普通绑定(selector才是处理函数)
    if (fn == null) {
        [fn, selector] = [selector, null];
    }
    elem.addEventListener(type, function (e) {
        const target = e.target;
        //事件代理
        if (selector) {
            //如果匹配
            if (target.matches(selector)) {
                //执行绑定的函数
                fn.call(target, e);
            }
        }
        else { //普通绑定
            fn.call(target, e);
        }
    });
}

//通用的事件代理函数
const div = document.getElementById('div1');
//祖先元素,事件,绑定事件的标签名, 事件处理函数
//div下的a标签都被绑定了click事件
bindEvent(div, 'click', 'a', function(e) {
    e.preventDefault();
    //this指向触发该事件的dom元素
    alert(this.innerHTML);
})

elem.matches使用

解答

  1. 编写一个通用的事件监听函数(需要考虑事件代理,见上
  2. 描述事件冒泡的流程
    1. 基于DOM树形结构
    2. 事件会顺着触发元素往上冒泡
    3. 应用场景:事件代理
  3. 无限下拉的图片的列表,如何监听每个图片的点击
    1. 使用事件代理
    2. 用e.target获取触发事件的元素
    3. 用matches来判断是否是触发元素

3.4ajax

题目

  1. 手写一个简易的ajax(例如vue的axios,fetch,jquery中的ajax)
  2. 跨域的常用实现方式

知识点

XMLHttpRequest

先写如何用

//创建xhr实例
const xhr = new XMLHttpRequest();

//true表示该请求是异步的请求
xhr.open("GET", "data.json", true);

//状态改变的时候触发函数
xhr.onreadystatechange = function () {
	//readyState === 4代表内容已经解析完成
    if (xhr.readyState === 4) {
    	//xhr即是响应码
        if (xhr.status === 200) {
            console.log(xhr.responseText)
        }
        else {
            console.log("其他情况")
        }
    }
}

//可以发或者不发数据
xhr.send(null);

// post请求
// xhr.open("POST", "data.json", true);

// //状态改变的时候触发函数
// xhr.onreadystatechange = function () {
//     if (xhr.readyState === 4) {
//         if (xhr.status === 200) {
//             console.log(xhr.responseText)
//         }
//         else {
//             console.log("其他情况")
//         }
//     }
// }
// const userData = {
//     userName: "zhangsan",
//     pwd: "xxxx"
// }
// xhr.senc(JSON.stringify(userData));
状态码

关于xhr中的readyState和status的解析:
在这里插入图片描述
在这里插入图片描述

跨域、同源策略、jsonP、CORSE

在这里插入图片描述
第三个例子就不同源,同源是浏览器保证的(必须保证,否则一个页面可以请求其他的页面的数据
在这里插入图片描述
在这里插入图片描述
JSONP实现跨域需要服务端配合
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
jsonP的示范

<script>
  window.abc = function (data) {
      //跨域后得到的信息
      console.log(data)
  }
</script>
<!-- src是前台的访问,传入的参数为username,并希望后台提供的服务为callback , 将返回函数执行的结果,即abc( {a : 1}) -->
<script src="http://localhost:8002/jsonp.js?username=xxx&callback=abc"></script>
<!-- 后台的服务为http://localhost:8002/jsonp.js, 只有一段话abc({a : 1}), 即后台希望把数据传给前端的abc -->

在这里插入图片描述
在这里插入图片描述

解答

  1. 手写一个简易的ajax0

const myAjax = function (url) {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        //默认为true
        xhr.open("GET", url);

        xhr.onreadystatechange = () => {
            if (xhr.readyState === 4) {
                if (xhr.status === 200) {
                    resolve(xhr.responseText);
                }
                else if (xhr.status === 404 || xhr.status === 500) {
                    reject(new Error("404 not found hhh"));
                }
            }
        }

        xhr.send(null);
    });
}

const url = "data.json";
myAjax(url).then(res => console.log(res))
    .catch(err => console.log(err));
  1. 跨域的常用实现方式
    1. JSONP(前端
    2. CORS (后端配置实现
补几个封装了ajax的插件

jQuery.ajax() (不是基于promise,过时)
Fetch API
axios

3.5存储

题目

  1. 描述cookie localStorage sessionStorage 区别

知识点

cookie

通过;分割的key:val形式,每次访问服务器都会用cookie访问
通过document.cookie可以查看cookie

使用背景
在这里插入图片描述

在这里插入图片描述

localStorage 和 sessionStorage

H5的 localStorage 和 sessionStorage解决了上面的问题
在这里插入图片描述
两者的区别
在这里插入图片描述

解答

描述cookie localStorage sessionStorage 区别

  1. 容量,cookie是4KB,后者为5M
  2. cookie的API只能通过document.cookie="xxx"来赋值,使用起来不符合js的风格,后者可以通过setItem和getItem来获取
  3. cookie会随着http请求发送出去,增加了数据负荷。而后者保存在本地
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值