赞
踩
选好目标方向,更新美化简历,备好电子版和纸质版.先复习备战,然后边面试边复习.
记录面试情况,这样安排面试时,不易冲突
面试途径来源 | 面试公司名称 | 外包公司名称 | 会议主题 | 面试时间 | 面试方式 | 注意事项 | 问题 | 面试情况 |
---|---|---|---|---|---|---|---|---|
csdn | csdn | 无 | 北京 一面 | 2024.05.01 | 视频会议 会议码: 2024 | 提前5分钟进入 | css js html | 通过 |
面试主要就是: 自我介绍 项目介绍 项目中困难以及收获 开发流程 基础问题(html、css、js、vue、react、webpack、小程序、跨端、node等) 打印题 手写题 项目相关问题 思维逻辑题 实现一个组件思路以及代码
HTTP/1.0:该协议是最早的版本,支持一个TCP连接处理一个请求。每个HTTP请求都要在建立TCP连接后发起,在请求结束后关闭TCP连接,无法复用连接,会导致连接频繁建立和关闭,影响请求速度。
HTTP/1.1:该协议在HTTP/1.0的基础上进行了优化,支持持久连接,即在一个TCP连接上可以传输多个请求和响应。同时引入了管道技术,允许同时发送多个请求,解决了HTTP/1.0中的串行传输问题。另外,HTTP/1.1还增加了对缓存的支持,增强了安全性。
HTTP/2:该协议是HTTP/1.1的升级版本,通过二进制传输替代文本传输,引入了多路复用技术,允许在一个连接上并发处理多个请求和响应。HTTP/2还支持头部压缩、服务器主动推送等功能,进一步提高了性能和效率。
display: flex;
justify-content: center;
align-items: center;
图片在CSS中按照2:1的比例显示,使用padding-top技巧来创建一个具有指定宽高比的容器,然后使图片覆盖在这个容器上。这种方法的好处是不需要知道图片的具体尺寸,而且可以响应式地适应不同大小的屏幕。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> <style> .aspect-ratio-box { position: relative; width: 100%; /* 可以是具体的宽度,也可以是100% */ /* 使用padding-top来创建2:1的宽高比 */ padding-top: 50%; /* 100% / 2 = 50% */ background-color: #000; /* 如果需要,可以设置背景颜色 */ } .aspect-ratio-box img { position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: cover; /* 保持图片的宽高比,覆盖整个容器 */ } </style> </head> <body> <div class="aspect-ratio-box"> <img src="./wallhaven-y8.jpeg" alt="Descriptive Text" /> </div> <script> // .aspect-ratio-box类创建了一个宽高比为2:1的容器。padding-top属性设置为50%,因为容器的宽度是高度的兩倍。然后,图片被设置为绝对定位,宽度和高度都是100%,这样它就会覆盖整个容器。 // object-fit: cover;属性确保图片保持其原始宽高比,同时覆盖整个容器,不会有空白。 // 这种方法适用于响应式设计,因为无论容器的宽度如何变化,图片都会保持2:1的比例显示。 </script> </body> </html>
取递增递减中最大值
this介绍一下
call apply bind
原型链
手写trim
手写promise.all race
typeof {}
Object.prototype.apply([])
事件循环机制+对应题
数组map和forEach的区别
ES6
this指向题
箭头函数
var let const 区别
js 数组方法
js reduce 用途
reduce去重
实现delay
数组拉平 不用 flat
多个数组排序成一个 不用 sort
promise.all promise.race(手写)
两个有序数组合成一个
多维数组拉平用reduce怎么做
es6数组新增的方法有哪些?
promise 和 async await 区别
String([1,2])输出什么
页面防抖和节流策略
可选链式操作符
设计模式
dom事件 冒泡 捕获
js基础类型
js判断数据类型方法
js 数组中改变原数组的方法
闭包
js基础数据类型
boolean、string、number、null、undefined、bigint、Symbol
数据类型判断的方法
typeof instanceof Object.prototype.toString.call
typeof instanceof Object.prototype.toString.call的区别
typeof只能检测基本数据类型 引用数据类型都会返回object null也是返回object
instanceof检测当前实例是否是属于某个类的实例,不能够检测字面量方式创建的基本数据类型,只能检测引用数据类型的值
Object.prototype.toString.call会默认返回当前方法中的this对应的结果,成字符串数组形式返回’[object 对应的类型]’
let 和 var 的区别、
let不能重复声明,var可以重复声明
let有块级作用域,var没有
let没有变量提升,var有
var会给全局作用域定义一个变量,给window增加属性,let声明的变量不会
什么是作用域
当打开页面,浏览器就会形成全局作用域为代码执行提供环境,而这个环境就是全局作用域
而函数执行时,会生成一个私有作用域,为代码执行提供环境
什么是作用域链
在私有作用域中,函数执行,如果要使用一个变量,自己作用域要是有,就使用自己的,要是没有,就向上一级作用域查找,上一级还没有,在向上一级查找,直到找到全局作用域,如果还没有就报错—>这种一级一级向上查找的机制就是【作用域链查找机制】
Object.toString.call的底层运行机制
Object.toString.call是JavaScript中用于获取对象的类型的方法。它的底层运行机制如下:
数组的方法有哪些
pop(删除的一项)、push(新length)、shift(删除的一项)、unshift(新length)、sort、forEach、map、splice、split、reverse、concat、filter
some、every、reduce、from、fill、flat
forEach和map的区别
forEach没有返回值,只是对数组中的每一个元素进行操作
map会返回一个新数组,这个数组的元素是原数组中每个元素执行回调函数后的一个返回值
some、every的区别
some有一项与判定符合就返回true
every每项与判定符合就返回true
reduce的使用
reduce遍历使用
参数1 上一次回调的值或者默认值 参数2 当前项 参数3 索引 参数4 原数组
es6语法有哪些
扩展运算符…
箭头函数
数组新增方法
对象新增方法
new map new set
proxy
promise
async函数
generator函数
箭头函数的区别
箭头函数没有this
不能作为generator函数
不能作为构造函数
遍历对象的方法
for in object.keys object.values
对象常用的方法
object.is() object.assign() object.keys() object.values() Object.entries() [将对象转数组] Object.fromEntries() [将数组转对象]
object.create()
什么是原型链
每个JavaScript对象都有一个原型对象,对象可以从其原型对象继承属性和方法。当访问一个对象的属性或方法时,如果对象本身没有这个属性或方法,JavaScript引擎会沿着原型链向上查找,直到找到对应的属性或方法或者到达原型链的顶端(即Object.prototype)。
在JavaScript中,每个对象都有一个__proto__属性,指向其原型对象。通过原型链,可以实现对象之间的属性和方法的共享,当我们创建一个对象时,JavaScript会自动为该对象分配一个原型对象,这个原型对象又会有自己的原型对象,依次形成原型链。
原型链的查找机制
在对象里查找一个属性,先看自己私有的有没有,如果自己没有,就通过 proto 属性找 到当前所属类的原型上,如果原型上有,就直接用,如果没有,通过原型的 proto 继续往Object类的原型上找,如果还没有,就是undefined, 这种一级一级向上查找就会形成原型链
new的运行原理及手写new方法
什么是闭包,为什么会出现闭包
闭包是指在函数内部创建的函数,它可以访问并持有其外部函数的变量、参数和内部函数,即使外部函数已经执行完毕,闭包仍然可以访问这些变量。
闭包出现的原因是由于JavaScript的词法作用域和函数的特性。当一个函数内部定义了另一个函数,并且内部函数引用了外部函数的变量,这个内部函数就形成了闭包。闭包会捕获并保存外部函数的作用域链,使得外部函数的变量在内部函数执行时仍然可用。
闭包的出现有以下几个常见的原因:
1. 保护变量:闭包可以将变量私有化,避免全局污染,只有通过闭包内部的函数才能访问和修改这些变量。
2. 记忆状态:闭包可以记住函数执行时的上下文环境,使得函数在后续调用时能够保持之前的状态。
3. 实现模块化:通过闭包可以创建私有的命名空间,将相关的函数和变量封装在一起,避免命名冲突。
需要注意的是,闭包会引用外部函数的变量,导致这些变量无法被垃圾回收,可能会造成内存泄漏。因此,在使用闭包时需要注意及时释放不再需要的资源,避免造成不必要的内存占用。
装饰器了解过吗
实现一个promise及原型方法
需要注意的是,useEffect的执行时机和类组件中的生命周期函数略有不同,它是在组件渲染后异步执行的,而不是同步执行的。这意味着在执行副作用操作时,组件可能已经更新了多次,因此需要通过依赖数组来控制更新的条件,避免出现不必要的副作用。
useEffect和useLayoutEffect的区别
useEffect 是异步执行的,useEffect 的执行时机是浏览器完成渲染之后
useLayoutEffect是同步执行的。useLayoutEffect 的执行时机是浏览器把内容真正渲染到界面之前,和 componentDidMount 等价。
为什么useEffect是异步的,在class里对应的生命周期是同步的
执行时机和调用方式不同。
在类组件中,生命周期函数是在组件的不同阶段被调用的,例如componentDidMount在组件挂载完成后被调用,componentDidUpdate在组件更新后被调用,componentWillUnmount在组件卸载前被调用。这些生命周期函数是由React框架在合适的时机直接调用的,是同步执行的。
而useEffect是React函数组件中的一个Hook,它是在组件渲染完成后异步执行的。这是因为React函数组件的渲染过程是异步的,React会将函数组件的执行放入任务队列中,等待当前任务完成后再执行下一个任务,这样可以提高性能和响应速度。因此,useEffect中的副作用操作会在组件渲染完成后被调度执行,而不是立即执行。
由于useEffect是异步执行的,所以在执行副作用操作时,组件可能已经更新了多次。为了控制副作用的执行时机,可以通过依赖数组来指定更新的条件,只有当依赖发生变化时,才会重新执行副作用操作。这样可以避免不必要的副作用和性能问题。
类组件和函数组件的区别
类组件是面向对象编程,可继承,存在生命周期,存在this的指向问题
函数组件是函数式编程,不可继承,没有生命周期
性能渲染
类组件是通过shouldcomponentUpdate来阻断渲染
函数组件通过memo来进行优化
setState是同步还是异步的
在生命周期和合成事件中是异步
在addeventlistener、setTimeout等原生事件是同步
react组件通信
父子通过props传参
子父通过callback函数
redux mobx状态管理
store存储数据,reducer处理逻辑,dispatch派发action
useEffect和useLayoutEffect区别
都是处理副作用,useEffect是异步处理,useLayoutEffect是同步处理
实现一个useState
useState和useRef声明的变量有什么不同
实现useHover hook
function useHover() { const [isHovered, setIsHovered] = useState(false); const ref = useRef(null); const handleMouseOver = () => setIsHovered(true); const handleMouseOut = () => setIsHovered(false); useEffect(() => { const element = ref.current; if (element) { element.addEventListener('mouseover', handleMouseOver); element.addEventListener('mouseout', handleMouseOut); return () => { element.removeEventListener('mouseover', handleMouseOver); element.removeEventListener('mouseout', handleMouseOut); }; } }, [ref]); return [isHovered, ref]; } export default useHover;
interface 和 type 区别
interface只能定义接口类型,type可以定义任何类型
type会合并重复声明的类型,interface不能
高阶工具方法用过哪些?
ts类型
interface和type的区别
ts的好处和坏处
好处:
坏处:
type of 和 key of 的区别
实现题
type info = {age, name, id}
ts实现声明一个类型 只能是info的类型定义 例子 type childrenInfo = { age|name|id}
function useUpdate(){
const [,setUp]=useState(null);
const update = ()=>{
setUp(Date.now())
}
return update;
}
const update = useUpdate();
有一组数据如下 根据相同的group分组 实现一个 tab切换页面 对应的tab展示对应的分组,点击对应的tab展示对应的数据
[{id,name,group},{id,name,group},{id,name,group}…]
tab1 tab2 tab3
group1 group2 group3
增加一个筛选项,根据筛选项展示,如果group没有对应的值,tab隐藏,只展示有数据的tab
input 条件
tab2 tab3
group2 group3
如何处理数据,如何能最简便的方式处理
function arr() {
let arr = new Set();
while (arr.size < 100) {
let number = Math.floor(Math.random() * 100) + 1;
if (!arr.has(number)) {
arr.add(number);
}
}
return [...arr].sort((a, b) => a - b);
}
console.log(arr());
const list = [ { id: 1, name: "a" }, { id: 2, name: "b" }, { id: 2, name: "c" }, { id: 4, name: "d" }, ]; let arr = []; const newList = list.filter((item) => { if (!arr.includes(item.id)) { arr.push(item.id); return true; } else { return false; } }); console.log(newList);
// 双指针 function isPalindrome(str) { let left = 0; let right = str.length - 1; while (left < right) { if (str[left] !== str[right]) { return false; } left++; right--; } return true; } console.log("isPalindrome: ", isPalindrome("abcccba")); // 栈 function isPalindromeStack(str) { const stack = []; for (let i = 0; i < Math.floor(str.length / 2); i++) { stack.push(str[i]); } for (let i = Math.ceil(str.length / 2); i < str.length; i++) { if (stack[stack.length - 1] === str[i]) { stack.pop(); } } return !stack.length; } console.log("isPalindromeStack: ", isPalindromeStack("abcccba"));
/** * 滑动窗口 * @param {string} s * @return {number} */ var lengthOfLongestSubstring = function (s) { let ans = 0; let left = 0; const window = new Set(); for (let right = 0; right < s.length; right++) { const c = s[right]; while (window.has(c)) { window.delete(s[left++]); } window.add(c); ans = Math.max(ans, right - left + 1); } return ans; }; console.log(lengthOfLongestSubstring("qwertyuadsa")); var lengthOfLongestSubstring = function (s) { if (s.length < 1) { return 0; } let arr = new Array(s.length).fill(0); for (let i = 0; i < s.length; i++) { let strList = []; strList.push(s[i]); arr[i] = strList.length; for (let j = i + 1; j < s.length; j++) { let item = s[j]; if (!strList.includes(item)) { strList.push(item); arr[i] = strList.length; } else { break; } } } return arr.sort((a, b) => a - b)[arr.length - 1]; };
const arr = [ { id: 12, parentId: 1, name: "朝阳区" }, { id: 241, parentId: 24, name: "田林街道" }, { id: 31, parentId: 3, name: "广州市" }, { id: 13, parentId: 1, name: "昌平区" }, { id: 2421, parentId: 242, name: "上海科技绿洲" }, { id: 21, parentId: 2, name: "静安区" }, { id: 242, parentId: 24, name: "漕河泾街道" }, { id: 22, parentId: 2, name: "黄浦区" }, { id: 11, parentId: 1, name: "顺义区" }, { id: 2, parentId: 0, name: "上海市" }, { id: 24, parentId: 2, name: "徐汇区" }, { id: 1, parentId: 0, name: "北京市" }, { id: 2422, parentId: 242, name: "漕河泾开发区" }, { id: 32, parentId: 3, name: "深圳市" }, { id: 33, parentId: 3, name: "东莞市" }, { id: 3, parentId: 0, name: "广东省" }, ]; function getTree(arr) { const newArr = arr.sort((a, b) => b.parentId - a.parentId); for (let i = 0; i < newArr.length; i++) { let item = newArr[i]; if (item.parentId) { newArr.forEach((arrItem) => { if (arrItem.id === item.parentId) { if (arrItem.children) { arrItem.children.push(item); } else { arrItem.children = [item]; } } }); } } return newArr.filter((item) => !item.parentId).sort((a, b) => a.id - b.id); } const tree = getTree(arr); console.log("tree: ", JSON.stringify(tree, null, 2));
const obj = { id: 0, value: "test_0", children: [ { id: 1, value: "test_1", }, { id: 2, value: "test_2", }, { id: 3, value: "test_3", children: [ { id: 31, value: "test_31", }, { id: 32, value: "test_32", }, { id: 33, value: "test_33", children: [ { id: 331, value: "test_331", }, { id: 332, value: "test_332", }, ], }, ], }, ], }; const arr = []; function changeObj(obj) { arr.push({ id: obj.id, value: obj.value }); if (obj.children) { for (let i = 0; i < obj.children.length; i++) { changeObj(obj.children[i]); } } } changeObj(obj); console.log(arr); // [ // { id: 0, value: 'test_0' }, // { id: 1, value: 'test_1' }, // { id: 2, value: 'test_2' }, // { id: 3, value: 'test_3' }, // { id: 31, value: 'test_31' }, // { id: 32, value: 'test_32' }, // { id: 33, value: 'test_33' }, // { id: 331, value: 'test_331' }, // { id: 332, value: 'test_332' } // ]
// 输入 const str = ` 1 21 3 4 5 6 7 8 9 `; // 输出 // arr=[ // ['1','21','3'], // ['4','5','6'], // ['7','8','9'] // ] let newStr = str.replace(/\s|\n/g, function (a, b, c, d) { if (a === "\n") {// 换行符 return "n"; } else {// 空白符 return "s"; } }); let arr = newStr .split("n") .map((item) => item.split("s").filter((item) => item)) .filter((item) => item.length); console.log("arr: ", arr);
function flat(array) { return array.reduce((result, it) => { return result.concat(Array.isArray(it) ? flat(it) : it); }, []); } __________________ function flattenArray(arr) { return arr.reduce((acc, val) => { if (Array.isArray(arr)) { return acc.concat(flatArray(val)); } else { return acc.concat(val); } }, []); } const multiDimensionalArray = [1, 2, 3, [4, 5, [6]], 7]; const flatArray = flattenArray(multiDimensionalArray); console.log("flatArray: ", flatArray);
示例: compare(“9.5.1”, “10.3.1”); // -1
compare(“1.0.0”, “1.0.0”); // 0
compare(“1”, “1.0.0”); // 0
compare(“1.0.0”, “1”); // 0
compare(“9.4.1”, “9.9.1”); // -1
/** * 两个版本比较, 大于返回1, 等于返回0, 小于返回-1 * @param {*} versionA * @param {*} versionB * @returns */ function compare(versionA, versionB) { const partsA = versionA.split(".").map(Number); const partsB = versionB.split(".").map(Number); const maxLen = Math.max(partsA.length, partsB.length); for (let i = 0; i < maxLen; i++) { const partA = partsA[i] || 0; // 如果某个版本号部分较短,则默认为0 const partB = partsB[i] || 0; if (partA > partB) return 1; if (partA < partB) return -1; } return 0; // 如果所有部分都相等,则返回0 } console.log(compare("9.5.1", "10.3.1")); // -1 console.log(compare("1.0.0", "1.0.0")); // 0 console.log(compare("1", "1.0.0")); // 0 console.log(compare("1.0.0", "1")); // 0 console.log(compare("9.4.1", "9.9.1")); // -1 console.log(compare("4.0.2", "4.0.2")); // 0
var obj = { a: 1, foo() { console.log(this.a); }, }; var a = 2; var foo = obj.foo; var obj2 = { a: 3, foo: obj.foo }; obj.foo(); foo(); obj2.foo();
1
undefined
3
setTimeout(function () { console.log("setTimeout 1"); new Promise(function (resolve) { console.log("promise 1"); resolve(); }).then(function () { console.log("promise then"); }); }); async function async1() { console.log("async1 start"); await async2(); console.log("async1 end"); await async3(); } async function async2() { console.log("async2"); } async function async3() { console.log("async3"); } console.log("eventLoop"); async1(); //await async1(); add await打印结果会有不同 new Promise(function (resolve) { console.log("promise 2"); resolve(); }).then(function () { console.log("promise2 then"); }); new Promise(function (resolve) { console.log("promise 4"); resolve(); }).then(function () { console.log("promise4 then"); }); console.log("eventLoop end");
eventLoop
async1 start
async2
promise 2
promise 4
eventLoop end
async1 end
async3
promise2 then
promise4 then
setTimeout 1
promise 1
promise then
WebSocket是一种网络通信协议,提供了浏览器(客户端)和服务器间的全双工通信能力。这意味着客户端和服务器可以在同一时间内互相发送数据,而无需像传统的HTTP请求那样,每次通信都需要新的请求和响应。WebSocket使得实时、双向的Web应用程序成为可能,例如在线游戏、实时交易平台、协作工具和聊天应用等。
WebSocket的工作原理:
握手:
首先,客户端通过发送一个HTTP请求来初始化WebSocket连接。这个请求被称为“握手请求”。这个请求包含了一个特殊的头部:Upgrade: websocket,表示客户端希望将连接升级到WebSocket。
升级协议:
如果服务器支持WebSocket,它会响应一个101 Switching Protocols状态码,表示同意更改协议。这个HTTP响应也包含一个Upgrade: websocket头部,表明服务器也同意升级协议。
数据传输:
一旦握手成功,客户端和服务器间的HTTP连接就会升级到WebSocket连接。此时,双方可以开始通过这个持久化的连接发送数据。数据以“帧”的形式传输,可以包含文本或二进制数据。
保持连接:
WebSocket连接会保持活跃,直到客户端或服务器主动关闭连接。在连接期间,双方可以随时相互发送数据。
WebSocket的优点:
低延迟:
WebSocket提供了比HTTP更低的延迟,因为它不需要为每个消息建立新的连接。
全双工通信:
WebSocket允许客户端和服务器双向实时通信,没有“请求-响应”的限制。
减少开销:
相较于HTTP轮询,WebSocket减少了额外的HTTP头部信息,因此能更高效地利用带宽。
持久化连接:
一旦WebSocket连接建立,它会保持打开状态直到被客户端或服务器显式关闭。
WebSocket如何使用:
// 创建一个新的WebSocket连接 var socket = new WebSocket("ws://127.0.0.1:5500/"); // 监听WebSocket连接打开事件 socket.onopen = function (event) { console.log("socket.readyState : ", socket.readyState); if (socket.readyState === WebSocket.OPEN) { /* CONNECTING:值为0,表示正在连接; OPEN:值为1,表示连接成功,可以通信了; CLOSING:值为2,表示连接正在关闭; CLOSED:值为3,表示连接已经关闭,或者打开连接失败。 */ // 状态为OPEN,可以安全地发送数据到服务器 socket.send("Hello, Server!"); } }; // 监听接收到服务器数据事件 socket.onmessage = function (event) { console.log("Message from server: ", event.data); }; // 监听WebSocket错误事件 socket.onerror = function (event) { console.error("WebSocket error: ", event); }; // 监听WebSocket连接关闭事件 socket.onclose = function (event) { console.log("WebSocket connection closed: ", event); }; socket.onopen(); /* WebSocket实时通信,比较常采用的方式是 Ajax 轮询 HTTP 请求一般包含的头部信息比较多,其中有效的数据可能只占很小的一部分,导致带宽浪费; 服务器被动接收浏览器的请求然后响应,数据没有更新时仍然要接收并处理请求,导致服务器 CPU 占用; WebSocket 的出现可以对应解决上述问题: WebSocket 的头部信息少,通常只有 2Bytes 左右,能节省带宽; WebSocket 支持服务端主动推送消息,更好地支持实时通信; WebSocket 的特点 建立在 TCP 协议之上; 与 HTTP 协议有着良好的兼容性:默认端口也是 80(ws) 和 443(wss,运行在 TLS 之上),并且握手阶段采用 HTTP 协议; 较少的控制开销:连接创建后,ws 客户端、服务端进行数据交换时,协议控制的数据包头部较小,而 HTTP 协议每次通信都需要携带完整的头部; 可以发送文本,也可以发送二进制数据; 没有同源限制,客户端可以与任意服务器通信; 协议标识符是 ws(如果加密,则为 wss),服务器网址就是 URL; 支持扩展:ws 协议定义了扩展,用户可以扩展协议,或者实现自定义的子协议(比如支持自定义压缩算法等);
setTimeout(() => {
setCount(c => c + 1);
setFlag(f => !f);
// React 会 render 两次,每次 state 变化更新一次
}, 1000);
//18只会渲染一次,想渲染两次
import { flushSync } from 'react-dom'; 强制同步执行
setTimeout(() => {
// 这里的 setState 会立即触发一个组件的更新,不会被批处理
this.setState({ count: this.state.count + 1 });
// 这里的状态更新会立即反映,因为它不会被批处理
console.log(this.state.count); // 输出: 1
}, 0);
浏览器可以通过多种方式来缓存文件,以提高网页加载速度和减少网络流量消耗。以下是浏览器缓存文件的常见方式:
HTTP:
○ HTTP是一种用于传输超文本的协议,数据传输是明文的,不加密。
○ HTTP数据传输速度快,适用于不涉及敏感信息的网站和应用。
○ HTTP在传输过程中容易被窃听和篡改,存在安全风险。
HTTPS:
○ HTTPS通过在HTTP上加入SSL/TLS加密层来保护数据传输的安全性。
○ HTTPS传输的数据是加密的,可以防止数据被窃听和篡改。
○ HTTPS使用加密证书来验证服务器身份,确保通信双方的身份和数据的完整性。
除了安全性方面的区别,HTTP和HTTPS在使用端口上也有区别:
● HTTP默认使用端口80进行通信。
● HTTPS默认使用端口443进行通信。
● http/1和http/2的区别
性能:
○ HTTP/2相比HTTP/1.1具有更好的性能表现,主要体现在以下几个方面:
■ 多路复用:HTTP/2支持在单个连接上并行交换多个请求和响应,而HTTP/1.1需要多个连接来处理并行请求。
■ 头部压缩:HTTP/2使用HPACK算法对头部信息进行压缩,减少了数据传输量。
■ 服务器推送:HTTP/2支持服务器主动推送资源给客户端,减少了客户端请求的延迟。
安全性:
○ HTTP/2对安全性的要求更高,推荐使用HTTPS协议来保护数据传输的安全性。
协议:
○ HTTP/1.1是基于文本的协议,而HTTP/2是二进制协议,更加高效。
头部压缩:
○ HTTP/2使用HPACK算法对头部信息进行压缩,减少了数据传输量,而HTTP/1.1没有头部压缩功能。
服务器推送:
○ HTTP/2支持服务器推送功能,可以在客户端请求之前将相关资源推送给客户端,提高性能。
CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
CommonJS 模块的require()是同步加载模块,ES6 模块的import命令是异步加载,有一个独立的模块依赖的解析阶段。
同步错误使用try catch 捕获
异步使用promise.catch
window.addEventListener监听某个方法
设计模式:
● 工厂模式(Factory Pattern):用于创建对象的模式,通过工厂函数或类来创建对象实例。
● 单例模式(Singleton Pattern):确保一个类只有一个实例,并提供全局访问点。
● 观察者模式(Observer Pattern):定义对象间的一对多依赖关系,当一个对象状态改变时,所有依赖它的对象都会收到通知。
● 发布-订阅模式(Publish-Subscribe Pattern):类似观察者模式,但使用一个调度中心来管理订阅和发布事件。
● 策略模式(Strategy Pattern):定义一系列算法,将每个算法封装起来,并使它们可以互相替换。
● 装饰者模式(Decorator Pattern):动态地给一个对象添加一些额外的职责,就像给一个人穿不同的外套一样。
准则:
● DRY原则(Don’t Repeat Yourself):避免重复代码,尽量将重复的逻辑抽象成函数或模块。
● 单一职责原则(Single Responsibility Principle):一个类应该只有一个引起变化的原因。
● 开放-封闭原则(Open-Closed Principle):软件实体应该对扩展开放,对修改封闭。
● Liskov替换原则(Liskov Substitution Principle):子类应该能够替换其父类而不影响程序的正确性。
● 接口隔离原则(Interface Segregation Principle):多个特定接口要好于一个通用接口。
● 依赖倒置原则(Dependency Inversion Principle):高层模块不应该依赖于低层模块,二者都应该依赖于抽象。
let xhr = new XMLHttpRequest;
xhr.open("get", url, false);
xhr.onreadystatechange=function(){
if(xhr.readyState===4&&/^2\d{2}/.test(xhr.status)){
// 说明数据已经传输到了客户端
}
}
xhr.send();
// 冒泡 function bubble(arr) { for (let i = 0; i < arr.length - 1; i++) { for (let j = 0; j < arr.length - 1 - i; j++) { // 每次排序好一个最大值,放在末尾 if (arr[j] > arr[j + 1]) { [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]; } } } return arr; } console.log(bubble([12, 23, 11, 35, 9, 14])); // 插入排序 function insert(arr) { const handArr = []; handArr.push(arr[0]); for (let i = 1; i < arr.length; i++) { const item = arr[i]; for (let j = handArr.length - 1; j >= 0; j--) { //每次在新的有序数组里,按照大小顺序插入一个值 const cur = handArr[j]; if (item > cur) { handArr.splice(j + 1, 0, item); break; } if (j === 0) { handArr.unshift(item); } } } return handArr; } console.log(insert([12, 23, 11, 35, 9, 14])); // 快速排序 function quick(arr) { if (arr.length <= 1) { return arr; } const middleIndex = Math.floor(arr.length / 2); // 中间索引 const middleValue = arr.splice(middleIndex, 1)[0]; // 删除中间项 const leftArr = []; const rightArr = []; for (let i = 0; i < arr.length; i++) { const item = arr[i]; if (item < middleValue) { leftArr.push(item); } else { rightArr.push(item); } } return quick(leftArr).concat(middleValue, quick(rightArr)); } console.log(quick([12, 23, 11, 35, 9, 14]));
<body> <input id="box" /> <button id="box1">+++</button> <script> let box = document.querySelector("#box"); function fn() { console.log(this, 2); } // 防抖 function debounce(fn, delay) { delay = delay || 200; let timer; return function (...args) { let context = this; timer && clearTimeout(timer); timer = setTimeout(() => { fn.apply(context, args); }, delay); }; } box.oninput = debounce(fn, 400); // 节流 function throttle(fn, interval) { var last; var timer; interval = interval || 200; return function () { var th = this; var args = arguments; var now = +new Date(); if (last && now - last < interval) { clearTimeout(timer); timer = setTimeout(function () { last = now; fn.apply(th, args); }, interval); } else { last = now; fn.apply(th, args); } }; } function log() { console.log("99"); } let box1 = document.querySelector("#box1"); box1.onclick = throttle(log, 4000); </script> </body>
function PromiseAll(list) { return new Promise((resolve, reject) => { const results = []; let count = 0; for (let i = 0; i < list.length; i++) { Promise.resolve(list[i]) .then((res) => { results[i] = res; count++; if (count === list.length) { resolve(results); } }) .catch((err) => { reject(err); }); } }); } // 使用示例: const promise1 = Promise.resolve(3); const promise2 = new Promise((resolve) => setTimeout(resolve, 100, "foo")); const promise3 = Promise.reject(new Error("失败")); PromiseAll([promise1, promise2, promise3]) .then((values) => { console.log(values); // 不会执行,因为 promise3 被拒绝 }) .catch((error) => { console.error(error); // 执行,输出:Error: 失败 }); function PromiseRace(list) { return new Promise((resolve, reject) => { for (let i = 0; i < list.length; i++) { Promise.resolve(list[i]) .then((res) => { resolve(res); }) .catch((err) => { reject(err); }); } }); } // 使用示例: const promise4 = new Promise((resolve) => setTimeout(resolve, 500, "one")); const promise5 = new Promise((resolve) => setTimeout(resolve, 100, "two")); const promise6 = new Promise((resolve, reject) => setTimeout(reject, 300, "three") ); PromiseRace([promise4, promise5, promise6]) .then((value) => { console.log(value); // 输出: "two",因为 promise2 最先解决 }) .catch((reason) => { console.log(reason); // 如果所有 promise 都拒绝,这里会执行 });
function deepClone(source) {
if (source instanceof Object === false) return source;
let target = Array.isArray(source) ? [] : {};
for (let i in source) {
if (source.hasOwnProperty(i)) {
if (typeof source[i] === 'object') {
target[i] = deepClone(source[i]);
} else {
target[i] = source[i];
}
}
}
return target;
}
function delay(time) {
return new Promise((res) => {
setTimeout(() => {
res()
}, time)
})
}
let strUrl = 'www.baidu.com?a=1&b=2&c=3&d=4';
function getUrlParams(url) {
let obj = {};
let paramsAry = url.substring(url.indexOf('?') + 1).split('&')
paramsAry.forEach(item => {
let itm = item.split('=')
obj[itm[0]] = itm[1]
});
return obj;
}
getUrlParams(strUrl)
let ary = [1, 2, 3, [3, 4, [5, 6]], 5, [3, 4]]; function flattenArray(ary) { // 1 // return ary.join(",").split(",").map(v => parseFloat(v)) // 2 // return ary.flat(3) // 3 // return ary.flat(Infinity) // 4 // return ary.reduce((pre,cur)=>{ // return pre.concat(Array.isArray(cur)?fn(cur):cur) // },[]) // 5 // while (ary.some(Array.isArray)) { // ary = [].concat(...ary); // } // return ary; // 6 // return JSON.parse("["+JSON.stringify(ary).replace(/(\[|\])/g, "")+"]") // 7 // return JSON.stringify(ary).replace(/(\[|\])/g, "").split(",").map(v => parseFloat(v)) //8 let result = []; for (let item of arr) { if (Array.isArray(item)) { result = result.concat(flattenArray(item)); } else { result.push(item); } } return result; } console.log(flattenArray(ary)); // function flattenArray(arr, result = []) { // for (let i = 0; i < arr.length; i++) { // const item = arr[i]; // if (Array.isArray(item)) { // flattenArray(item, result); // 递归处理子数组 // } else { // result[result.length] = item; // 手动添加非数组元素到结果数组 // } // } // return result; // }
function create(Con, ...args) {
let obj = {}
Object.setPrototypeOf(obj, Con.prototype)
let result = Con.apply(obj, args)
return result instanceof Object ? result : obj
}
Array.prototype.mapMap = function (fn, thisArg) {
console.log(fn, thisArg);
if (typeof fn !== 'function') {
throw new Error(${fn} is not a function)
}
return this.reduce((pre, cur, index, ary) => {
return pre.concat(fn.call(thisArg, cur, index, ary))
}, [])
}
Array.prototype.myForEach = function(callback) {
if (typeof callback !== 'function') {
throw new Error(${callback} is not a function)
}
for (let i = 0; i < this.length; i++) {
callback(this[i], i, this);
}
};
// 示例用法
const arr = [1, 2, 3];
arr.myForEach((item, index, array) => {
console.log(第${index}个元素是${item},数组是${array});
});
function uniquedObj(params) { let map = new Map(); let res = [] params.forEach(item => { if (!map.has(item.id)) { res.push(item) map.set(item.id, item.id) } }) return res; } let a1 = [ { id: 1, name: '12' }, { id: 12, name: '122' }, { id: 13, name: '133' }, { id: 1, name: '12' }, { id: 12, name: '122' }, ] console.log(uniquedObj(a1));
/** ● @param {ListNode} l1 ● @param {ListNode} l2 ● @return {ListNode} */ const mergeTwoLists = function(l1, l2) { // 定义头结点,确保链表可以被访问到 let head = new ListNode() // cur 这里就是咱们那根“针” let cur = head // “针”开始在 l1 和 l2 间穿梭了 while(l1 && l2) { // 如果 l1 的结点值较小 if(l1.val<=l2.val) { // 先串起 l1 的结点 cur.next = l1 // l1 指针向前一步 l1 = l1.next } else { // l2 较小时,串起 l2 结点 cur.next = l2 // l2 向前一步 l2 = l2.next } // “针”在串起一个结点后,也会往前一步 cur = cur.next } // 处理链表不等长的情况 cur.next = l1!==null?l1:l2 // 返回起始结点 return head.next };
/** ● @param {ListNode} head ● @return {ListNode} */ const deleteDuplicates = function(head) { // 设定 cur 指针,初始位置为链表第一个结点 let cur = head; // 遍历链表 while(cur != null && cur.next != null) { // 若当前结点和它后面一个结点值相等(重复) if(cur.val === cur.next.val) { // 删除靠后的那个结点(去重) cur.next = cur.next.next; } else { // 若不重复,继续遍历 cur = cur.next; } } return head; };
/** ● @param {ListNode} head ● @param {number} n ● @return {ListNode} */ const removeNthFromEnd = function(head, n) { // 初始化 dummy 结点 const dummy = new ListNode() // dummy指向头结点 dummy.next = head // 初始化快慢指针,均指向dummy let fast = dummy let slow = dummy // 快指针闷头走 n 步 while(n!==0){ fast = fast.next n-- } // 快慢指针一起走 while(fast.next){ fast = fast.next slow = slow.next } // 慢指针删除自己的后继结点 slow.next = slow.next.next // 返回头结点 return dummy.next };
/** ● @param {ListNode} head ● @return {ListNode} */ const reverseList = function(head) { // 初始化前驱结点为 null let pre = null; // 初始化目标结点为头结点 let cur = head; // 只要目标结点不为 null,遍历就得继续 while (cur !== null) { // 记录一下 next 结点 let next = cur.next; // 反转指针 cur.next = pre; // pre 往前走一步 pre = cur; // cur往前走一步 cur = next; } // 反转结束后,pre 就会变成新链表的头结点 return pre };
例: nums1 = [0], m = 0, nums2 = [1], n = 1 输出[1]
var merge = function (nums1, m, nums2, n) {
let len = m + n;
while (n > 0) {
if (m <= 0) {
nums1[--len] = nums2[--n]
continue
}
nums1[--len] = nums1[m - 1] >= nums2[n - 1] ? nums1[--m] : nums2[--n]
}
};
const climbStairs = (n) => {
let prev = 1;
let cur = 1;
for (let i = 2; i < n + 1; i++) {
const temp = cur; // 暂存上一次的cur
cur = prev + cur; // 当前的cur = 上上次cur + 上一次cur
prev = temp; // prev 更新为 上一次的cur
}
return cur;
}
console.log(climbStairs(3));
function ajax(options){ // 准备一个默认的对象 let default_op={ type:"get", async:true, cache:true, success:null, data:null } // 循环options,给default中属性名重新赋值; for(let key in options){ default_op[key]=options[key]; } if(default_op.type.toLowerCase()==="get"){ // 为了解决传参;get请求需要将data的值拼到url的后面; let str=`?`; for(let key in default_op.data){ str+=`${key}=${default_op.data[key]}&` } str=str.slice(0,str.length-1); default_op.url+=str; if(!default_op.cache){ // 如果不走缓存,在后面添加时间戳; default_op.url+= `&time=${Date.now()}`; } } let xhr = new XMLHttpRequest; // 取到default_op中的值;给open方法传入参数; xhr.open(default_op.type,default_op.url,default_op.async); xhr.onreadystatechange=function(){ if(xhr.readyState===4&&/^2\d{2}/.test(xhr.status)){ // 把请求回来的数据转成JSON格式的对象,传给success的回调; let val = JSON.parse(xhr.responseText); default_op.success(val); }else if(xhr.readyState===4){ // 如果请求不成功,执行失败的回调; default_op.error(); } } // 发送请求; if(default_op.type==="get"){ default_op.data=null; } xhr.send(default_op.data); } ajax({ url:"data.txt", type:"get", data:{username:"a",password:"b"}, cache:false, success:function(data){ console.log(data); } })
<script> (function (global, factory) { // global就是 window // factory是 function (window, noGlobal) {} if (typeof module === "object" && typeof module.exports === "object") { // ... // 支持CommonJs模块规范的执行这里(例如node.js) } else { // 代码能走到这里说明是浏览器或者webView环境 // 当外层自执行代码执行的时候,factory执行,function (window, noGlobal) {} // window // 也就是说function的里第一个形参被赋值的实参就是window factory(global); } // typeof windiw => 'object' }(typeof window !== "undefined" ? window : this, function (window, noGlobal) { // 参数信息 // window --> window // noGlobal --> undefined // .... var version = "1.11.3", jQuery = function (selector, context) { return new jQuery.fn.init(selector, context); }; // jQqury还一个自定义类,他们把jQuery的原型重定向了, // 而且还给jQ加了一个属性,属性值也是自己的原型 jQuery.fn === jQuery.prototype jQuery.fn = jQuery.prototype = { // 这里面是jQ的公有属性和方法 jquery: version, // 我们自己重定向后的原型是没有construstor,所以他给手动增加了一个constructor属性指向自己的类 // 为了保证原型的完整性 constructor: jQuery, // 转换为数组的方法 // this:一般是当前类jQuery的实例 toArray: function () { // this:一般是当前类jQuery的实例 return slice.call(this); }, // 把jQ对象转换为原生js对象 get: function (num) { return num != null ? // Return just the one element from the set (num < 0 ? this[num + this.length] : this[num]) : // Return all the elements in a clean array slice.call(this); }, each: function (callback, args) { // this就是当前实例, // each是jQ类的一个私有属性(把jQ当做对象来用) // 一会去jQ里查each方法 return jQuery.each(this, callback, args); }, eq: function (i) { var len = this.length, j = +i + (i < 0 ? len : 0); return this.pushStack(j >= 0 && j < len ? [this[j]] : []); }, } // 把jQuery赋值给window的$和jQuery,这样你就在全局下都可以使用了 if (typeof noGlobal === "undefined") { window.jQuery = window.$ = jQuery; } })); $() //jQ提供的方法放到了两个位置 // 1、原型上jQuery.prototype={toArray:fn} // $().toArray() // 只有jQ的实例才可以调用 // 2、对象上jQuery.ajax = ... // $.ajax() // 直接调取使用 // 检测当前对象是数组还是类数组 // function isArraylike(obj) { // if (type === "function" || jQuery.isWindow(obj)) { // return false; // } // if (obj.nodeType === 1 && length) { // return true; // } // return type === "array" || length === 0 || // typeof length === "number" && length > 0 && (length - 1) in obj; // } </script>
<script> (function (global, factory) { factory(global); // factory 是实参的回调函数 }(typeof window !== "undefined" ? window : this, function (window, noGlobal) { // 在这个作用域准备了数组和对象的常用方法; var deletedIds = []; var slice = deletedIds.slice; var concat = deletedIds.concat; var push = deletedIds.push; var indexOf = deletedIds.indexOf; var class2type = {}; var toString = class2type.toString; var hasOwn = class2type.hasOwnProperty; var support = {}; var jQuery = function (selector, context) { // jQuery执行时,会返回一个init的实例; return new jQuery.fn.init(selector, context); }; jQuery.fn = jQuery.prototype = { // 这是jquery原型上的方法;jQuery实例能用; jquery: version, constructor: jQuery, toArray: function () { return slice.call(this); }, } // 是往原型上扩展方法的; jQuery.extend = jQuery.fn.extend = function () {} // 扩展传一个对象,那么jquery的实例以后就可以调用新扩展的方法了; jQuery.extend({ toArray: function () { }, slice: function () { } }) // 返回的init实例,就是通过传入选择器,获取到的对应的元素 init = jQuery.fn.init = function (selector, context) { } // init.prototype = jQuery.fn; // 把jQuery的prototype给了init的原型 // 把jQuery这个方法给了全局下的$ window.jQuery = window.$ = jQuery; }))(); $("#box") // jQuery 的实例,可以调用jquery原型上的方法; // $.addClass// jQuery 的实例,可以调用jquery原型上的方法; // console.log(module); // $("#box").prev(); // $.ajax // $("#box").ajax() // jquery的私有属性和jquery这个类原型的方法; </script>
Vue的插件必须使用Vue.use;只是vuex会默认检测到是vue的官方插件,看不到vue.use;vue.use执行时,会默认调用里面的install;
<body> <div id="app"> {{$store.state.count}} <child></child> </div> <script src="../node_modules/vue/dist/vue.js"></script> <script src="../node_modules/vuex/dist/vuex.js"></script> <script> let Vuex=(function(){ class Store{ constructor(){ // this==> 返回值store实例 } } function install(_Vue){ // _Vue : 就是Vue这个类函数; // mixin : 将生成store通过mixin方法注入到每一个组件上 _Vue.mixin({ beforeCreate(){// 比组件内部的beforeCreate早执行 console.log(this);// 代表每一个组件实例; } }) } return { Store, install } })(); // Vue的插件必须使用Vue.use;只是vuex会默认检测到是vue的官方插件,看不到vue.use; vue.use执行时,会默认调用里面的install; Vue.use(Vuex); // Vuex.Store // Vuex.mapState // store 是Store的一个实例;并且这个实例最后放到了vue的实例属性上; let store = new Vuex.Store({ state:{ count:100 }, mutations:{ addCount(state){ state.count++; } }, actoions:{ add({commit},payload){ commit("addCount",10) } } }); let child={ template:"<div>{{this.$store.state.count}}</div>" } // $store 会添加到每一个组件的实例上; let vm = new Vue({ el:"#app", store,// 会给当前实例以及当前实例的所有子孙组件都会新增一个$store属性; // 把Store的一个实例给了这个store属性 components:{child} }); </script> </body>
<body> <div id="app"> {{$store.state.msg}} {{$store.getters.str}} <child></child> </div> <script src="../node_modules/vue/dist/vue.js"></script> <script> // vuex:只要改变了state,凡是依赖state的组件都会高效的更新; // new Vue的data属性; let Vuex=(function(){ class Store{ constructor(options){ // this==> 返回值store实例 // 初始化一个state;如果传递进来的state,会给其默认一个{} // 为了能让数据能够监听到,当数据发生改变,依赖的视图也要更新的; let vm = new Vue({ data:{ state:options.state||{} } }); //this.state=options.state||{}; // 将Vm的state的空间地址赋值给了this.state this.state = vm.state; // this==> $store this.mutations={}; let mutations=options.mutations;//这个就是传递进来的那个mutations // Object.keys : 把对象中的属性名挑出来放到一个数组中 // 就是在实例身上准备一个mutations对象,里面包含了options外界传递进来的方法, 那么方法中的this已经是指向了store这个实例; // Object.keys(options.mutations).forEach(key=>{ //this.mutations[key].bind(this,this.state) this.mutations[key]=(payload)=>{ options.mutations[key].call(this,this.state,payload) } }); // 执行私有属性的方法时,调用原型上的方法; let commit = this.commit;// 把原型的commit给了变量commit; // 给当前实例新增一个commit属性,属性值是一个函数 this.commit=(type,option)=>{ commit.call(this,type,option) } // this.commit=function(type,option){ // options.mutations[type].call(this,option) // } // actions this.actions = {}; let actions = options.actions||{}; Object.keys(actions).forEach(key=>{ this.actions[key]=(option)=>{ // 第一个this指向把函数中的this改成当前实例store // 把store传给action的方法; actions[key].call(this,this,option) } }); let dispatch = this.dispatch; this.dispatch=(type,option)=>{ dispatch.call(this,type,option) } // getters this.getters={}; let getters = options.getters||{}; // Object.keys : 将对象的属性名收集起来放到一个数组中 Object.keys(getters).forEach(key=>{ // 给getters中每一个属性新增一个get方法; Object.defineProperty(this.getters,key,{ get:()=>{ // 会进行缓存,只有依赖的属性发生改变会执行; return getters[key].call(this,this.state) } }); }); } // 把commit 放到store的原型上 commit(type,payload){ console.log(this);// Store的实例 this.mutations[type](payload) } dispatch(type,option){ this.actions[type](option) } } //...Vuex.mapState(['a',"b"]); // 将store中的state放到当前的computed属性中 function mapState(ary){ let obj ={}; ary.forEach(key=>{ obj[key]=function(){ console.log(this); // this 执行组件的实例 return this.$store.state[key] } }) return obj; } function mapGetters(ary){ let obj ={}; ary.forEach(key=>{ obj[key]=function(){ return this.$store.getters[key] } }) return obj; } function mapActions(ary){ let obj ={}; ary.forEach(key=>{ obj[key]=function(option){ return this.$store.dispatch(key,option) } }) return obj; } function mapMutations(ary){ let obj ={}; ary.forEach(key=>{ obj[key]=function(option){ return this.$store.commit(key,option) } }) return obj; } // ...Vuex.mapState(["count"]) function install(_Vue){ // _Vue和外面的Vue指向同一个空间地址 // _Vue : 就是Vue这个类函数; // mixin : 将生成store通过mixin方法注入到每一个组件上 _Vue.mixin({ beforeCreate(){// 比组件内部的beforeCreate早执行 // 生成一个组件的实例,就会执行一次;并且在自己的beforecreate之前执行的; //console.log(this);// 代表每一个组件实例; // this --> Vue的实例vm // 第二次执行 组件的实例 //this.$store=this.$options.store //console.log(this); // 这个会进行循环遍历, if(this.$options&&this.$options.store){ // 如果该条件是成立的,说明是vm实例; this.$store=this.$options.store; }else{ // 如果不成立,说明是子孙组件 // 如果父组件存在,那么就把父组件的$store属性赋值给子组件的$store属性; // $parent : 指的是当前组件的父组件 this.$store =this.$parent&&this.$parent.$store } } }) } return { Store, install, mapState, mapActions, mapGetters, mapMutations } })(); // Vue的插件必须使用Vue.use;只是vuex会默认检测到是vue的官方插件,看不到vue.use;vue.use 执行时,会默认调用里面的install; Vue.use(Vuex); // Vuex.Store // Vuex.mapState // store 是Store的一个实例;并且这个实例最后放到了vue的实例属性上; let store = new Vuex.Store({ state:{ count:100, msg:"李明帅" }, mutations:{ add(state,val){ // this==> store console.log(state); state.count+=val; } }, actions:{ addNum({commit},val){ commit("add",val); } }, getters:{ str(state){ return state.count%2===0?"偶数":"奇数"; } } }); let child={ created(){ // 组件在使用时,才会触发其钩子函数 }, methods:{ fn(){ // this.$store===store==Store的实例 this.$store.commit("add",100); // this.$store.dispatch("addNum",1) } }, computed:{ str(){ }, ...Vuex.mapState(['count']) }, template:"<div>{{$store.state.count}}{{count}}<button @click='fn'>增加 </button></div>" } // $store 会添加到每一个组件的实例上; let vm = new Vue({ el:"#app", store,// 会给当前实例以及当前实例的所有子孙组件都会新增一个$store属性; //a:100, beforeCreate(){ //console.log(this); // debugger }, // 把Store的一个实例给了这个store属性 // 组件在注册时,不会调用生命周期钩子函数, components:{child} }); //console.log(vm);// 目前vm身上没有$store // $options:代表 :new的对象,会把对象中的键值对添加到当前实例的$options上 // 1.先准备vuex对象【Store,install】; // 2. Vue.use执行调用了里面install,install执行调用了Vuex.mixin,对Vue这个类进行了修改 // 3.生成了一个store // 4.new Vue;把store放到了实例的$options // 5.随后vm的生命周期,执行了mixin中的beforeCreate=>把options的store直接赋值给了实例的 $store属性; </script> </body>
class VueRouter{ constructor(options){ const {routes}=options; // 监听当前页面的hash值的切换 // 当第一次解析页面时,会有一个默认的hash值 /// 循环遍历routes,把path和component重新放入一个新的对象中 // {"/home/:id":home} this.routeMap = routes.reduce((prev,next)=>{ prev[next.path]=next.component; return prev; },{}); // // this ==> VueRouter的实例,也是每一个组件上的_router Vue.util.defineReactive(this.route={},'path',"/"); window.addEventListener("load",()=>{ // 如果没有hash值,那么给其赋默认值/;如果本来就有hash,什么也不做; location.hash?null:location.hash="/"; }) window.addEventListener("hashchange",()=>{ // 当页面hash值发生改变以后,会触发这个方法;1.a标签 2.手动 // 获取当当前页面的hash值,获取到#后面的字符串; let path = location.hash.slice(1); this.route.path = path; }) } } //在Vuex注入了$store,在路由注入_router VueRouter.install=function(_Vue){ _Vue.mixin({ // 给每一个组件新增一个_router的属性,这个属性的属性值是VueRouter的实例 beforeCreate(){ // this==> 每一个组件实例 if(this.$options&&this.$options.router){ // 给每一个组件实例新增_router属性,属性值就是VueRouter的实例; 这是给Vm这个Vue实例新增 this._router=this.$options.router; }else{ // 给vm的组件的实例新增 this._router=this.$parent && this.$parent._router; } // 给每一个实例添加$route属性, Object.defineProperty(this,"$route",{ value:{ route:this._router.route// ? 这个route } }); // 注册两个内置组件 // router-link router-view // 注册全局组件 <router-link to="/home"></router-link> let child = {} Vue.component("router-link",{ props:{ to:String }, // template:"<div></div>", render(createElement){// h是一个createdElement,这个方法可以直接接受一个组件; createElement 用来创建虚拟的DOM //return createElement("a",{},首页) // render : 渲染函数 // render: 将虚拟DOM可以转成真实的DOM;这个函数返回什么, 那么最终router-link就渲染成什么 // this==> 当前的组件实例 // return + 组件;可以把组件渲染成一个真实的DOM; // return h(child); // return <child></child> // $slots return <a href={`#${this.to}`}>this.$slots.default</a> } }); // router-view : 根据页面的hash值显示对应的组件 Vue.component("router-view",{ render(createdElement){ // 这个传递一个动态的组件名字 return createElement(this._router.routeMap[this._router.route.path]) } }) } }) }; let router=new VueRouter({ routes:[] }) let vm = new Vue({ router, render(){ } }) export default VueRouter;
function createStore(reducer) { let state; let getState = () => JSON.parse(JSON.stringify(state)); // action : type 要改的数据 function dispatch(action) { state = reducer(state, action); listeners.forEach(item => { if (typeof item === "function") { item(); } }) } let listeners = [];// 存储订阅的事件的一个容器;当调用dispatch的时候,让这个事件池中的方法执行; dispatch({});// 为了初始化数据 let subcribe = (fn) => { listeners.push(fn); return () => { listeners = listeners.filter(item => item !== fn); } } return { getState, dispatch, subcribe } } export default createStore; 补充 <script> //vue->vueX //react-> //事件池 所有公共状态 //修改状态 //reducer 管理员 登记在案 (原始状态state={},dispatch对象) return state 把原始容器中的状态修改为啥; //getState()获得信息 //store,dispatch type //store.subscribe 发布订阅 //redux 的漏洞 //setState的同步异步? //index.js 工程化 //action-types.js 派发行为标识ACTION,TYPE的宏管理 变量 //reducers index.js 把各个小reducers版块合一起 // //redux源码 function createStore(reducer) { if (typeof reducer !== "function") { throw new TypeError("reducer must be a function") } let state; let listeners = []; const getState = function getState() { //防止返回状态和原始共用地址 return JSON.parse(JSON.stringify(state)); } //向容器事件池中加方法 const subscribe = function getState(func) { if (typeof func !== "function") { if (listeners.includes(func)) { listeners.push(func); } } return function unsubscribe() { listeners = listeners.filter(item => item !== func); } } //派发任务 const dispatch = function getState(action) { if (action === null || action === undefined) return; if (typeof action !== "object") return; if (!action.hasOwnProperty("type")) return; //执行 state = reducer(state, action); // listeners.forEach(item => { item(); }) } //初始的时候先开发一次dispatch // const randomString=function(){ // return Math.random().toString(36).substring(7).split("").join(".") // } dispatch({ type: "@@redux/INIT$(Math.random())" }) return { getState, subscribe, dispatch } } export { createStore }; </script>
import React from "react"; // import store from "../store/index.js"; import actions from "../store/actions/counter"; // react-redux: 将store的公共数据放到组件的属性上; // 属性和状态的更新都能引发视图的更新; // react-redux要求返回一个连接后的组件; import {connect} from "react-redux"; // 可以将store中的数据和action的动作以属性的方式传递给该组件 class Counter extends React.Component{ // constructor(){ // super(); // this.state={num:store.getState().counter.num} // } // componentDidMount(){ // // 在redux中,A组件通过dispatch更新了store中的数据,同时B组件也使用了store中这个数据, 但是B组件不会自动更新; // store.subscribe(()=>{ // this.setState({num:store.getState().counter.num}) // }) // } add=()=>{ // store.dispatch(actions.add()); //当使用方法时,保持和action中的add一致; this.props.add(); } min=()=>{ // store.dispatch(actions.min()); this.props.min(); } render(){ // 1. 组件事件 ==> 2. action-type==>3.actions==> 4.reducer==>5.组件 //想更新视图,需要调用render方法,那么执行setState就会调用render; // 每当dispatch时,都要更新视图的;那么把setState方法进行订阅; return <div> <button onClick={this.add}>+</button> {this.props.num} <button onClick={this.min}>-</button> </div> } } //源码 // connect : 第一个参数: 函数 第二个参数: 是当前组件 // actions : 是一个返回类型的对象; // mapStateToProps\mapDisPatchToProps都是在connect函数内部调用的 let mapStateToProps=state=>({...state.counter}); // 当执行connect时,会默认调用这个箭头函数, 并且将store中的state数据传给当前的函数state参数;返回当前组件对应的数据,并且放在了当前组件的行间属性上; let mapDisPatchToProps=(dispatch)=>{// dispatch==>store.dispatch return {// 这个对象会放在组件的属性上; add:()=>{dispatch(actions.add())}, min:()=>{dispatch(actions.min())} } } // 都是将这两个函数的返回值放到组件的属性上; export default connect(mapStateToProps,actions)(Counter); //在执行connect时,判断第二个参数是否是一个函数,如果是函数,则将函数的执行结果放在组件的行间属性上, 如果是一个对象,那么会默认调用一个bindActionsCreator这个方法,将该方法的返回值放在组件行间属性上 当前属性传给组件 // action 就是connect传入的action dispatch是store中的dispatch方法 // let bindActionCreator=(action,dispatch)=>{ // let obj ={}; // for(let key in action){ // obj[key]= ()=>{ // dispatch(action[key]()) // } // } // return obj; // }
<script> // 重写Promise;写一个类似于Promise这个类的方法 class MyPromise { constructor(excutor) { // 当new MyPromise,constructor执行的; // this --> Promise的实例; // pending fulfilled rejected this.state = "pending"; // 用来存储成功的回调函数和失败的回调函数的; this.fulfilledEvent = []; this.rejectedEvent = []; // 1.resolve,改变了状态 let resolve = (result) => { // 执行resolve,那么实例的状态变成了成功态;this-->Promise实例 // 如果不是pending状态,就不再向下执行; if (this.state !== "pending") return; this.state = "fulfilled"; clearTimeout(this.timer); this.timer = setTimeout(() => { this.fulfilledEvent.forEach(item => { if (typeof item == "function") { item(result); } }) }) }; let reject = (result) => { if (this.state !== "pending") return; this.state = "rejected"; clearTimeout(this.timer); this.timer = setTimeout(() => { this.rejectedEvent.forEach(item => { if (typeof item == "function") { item(result); } }) }) } try { excutor(resolve, reject); } catch (e) { // e: 错误信息 reject(e); } } // 是订阅;是在往成功的事件池和失败的事件池放入成功的回调函数和失败的回调函数 then(resolveFn, rejectedFn) { // 当then不传回调时,给两个形参赋默认值 if (resolveFn === undefined) { resolveFn = () => {} } if (rejectedFn === undefined) { rejectedFn = () => {} } // this return new MyPromise((resolve, reject) => { // then 返回的这个实例是p2; // 往事件池中放入方法; // resolve :这个是函数,函数中的this--> p2; // this--> then返回的promise实例 // this.fulfilledEvent.push(resolveFn); this.fulfilledEvent.push((result) => { try { let x = resolveFn(result); // resolve : 让成功的事件池中的方法运行,里面执行时, this-->P2的事件池中的方法执行 x instanceof MyPromise ? x.then(resolve, reject) : resolve(); } catch (e) { reject(e); } }); this.rejectedEvent.push((result) => { try { let x = rejectedFn(result); // resolve : 让成功的事件池中的方法运行,里面执行时, this-->P2的事件池中的方法执行 x instanceof MyPromise ? x.then(resolve, reject) : resolve(); } catch (e) { reject(e); } }); }) } } let p1 = new MyPromise(function (resolve, reject) { // resolve(100) // resolve(200); // reject(); // console.log(a); // console.log(resolve); resolve(); }) p1.then(function (a) { // fn1 // 成功的回调函数 console.log(a); return new Promise(function (resolve, reject) { //:x===p3 resolve (); // 这个resolve 执行,能让p3的事件池中的方法执行, p3的事件池中有个resolve;所以就会这个p3事件中的resolve执行,p3中的resolve执行, 这个this指向p2,那么就能让p2事件池中的方法运行; }) }, function () { // fn2 // 失败的回调 console.log(88); }).then(function () { // fn3 // 如果上一个then中的回调返回一个promise实例,那么这个函数会被resolve包裹一下, 再放进这个实例的事件池中 }, function () { }) //console.log(p1); // p1 p2 p3 // 第一个then:把方法放到p1的事件池中; // 第二个then:把方法放到p2的事件池中; // let p = new Promise(function (resolve, reject) { // resolve : 形参 // 如果代码异常,会走失败;不会在控制台抛出异常 //console.log(a); resolve(); }) let c = p.then(function () { // }).then(function () { // 上一个中的回调如果不返回promise实例, 那么这个then受上一个then默认返回的promise的状态影响,如果回调中返回了promise实例, 那么这个then就受返回的promise实例的影响 }, function () { }); console.log(c); </script>
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。