先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Web前端全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
如果你需要这些资料,可以添加V获取:vip1024c (备注前端)
正文
var str = ‘123123000’
str.match(/\w{3}/g).join(‘,’) // 123,123,000
*/
if (render > 0) { // 说明不是3的整数倍
return num.slice(0, render) + ‘,’ + num.slice(render, len).match(/\d{3}/g).join(‘,’)
} else {
return num.slice(0, len).match(/\d{3}/g).join(‘,’)
}
}
}
let str = format(298000)
console.log(str)
MDN随机数 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Math/random
// 生成随机数
function random(min, max) {
// Math.floor() 向下进行取证
return Math.floor(Math.random() * (max - min + 1) + min)
}
console.log(random(3, 5))
// 给function的原型上面添加一个 _call 方法
Function.prototype._call = function (context) {
// 判断调用者是否是一个函数 this 就是调用者
if (typeof this != ‘function’) {
return
}
// 如果有 context 传参就是传参者 没有就是window
that = context || window
// 保存当前调用的函数
that.fn = this // 吧 this == fns 存到that.fn来
// 截取传过来的参数
/*
arguments
a: 1
fn: ƒ fns()
*/
// 通过 slice 来截取传过来的参数
const local = […arguments].slice(1)
// 传入参数调用函数
let result = that.fn(…local)
// 删出 fn 属性
delete that.fn
return result
}
let obj = { a: 1 }
function fns(a, b) {
console.log(a, b);
console.log(this)
}
fns._call(obj, 23, 555)
Function.prototype.myApply = function (context) {
if (typeof this !== ‘function’) {
// 判断当前调用者是否为函数
return
}
// 保存传入的this指向
that = context || window
// 保存当前调用的函数
that.fn = this
let result
// 这里开始判断传入的参数是否存在,此时参数是一个数组形式[thisArg,[传参]]
// 那么如果arguments[1]即传参存在的时候,就是需要传参调用保存的函数
// 如果不存在就直接调用函数
if (arguments[1]) {
result = that.fn(…arguments[1])
} else {
result = that.fn()
}
return result
}
let obj = {
a: 1
}
function fn(…val) {
console.log(this)
console.log(…val)
}
fn.apply(obj, [1, 4, 5, 6, 7, 9])
Function.prototype._bind = function (context) {
// 当调用用了_bind 函数 this 指向调用 _bind 的那个函数
// this 不是 function 不让使用 抛出类型错误
if (typeof this != ‘function’) {
throw new TypeError(‘Error’)
}
// 获取参数 除了传过来的第一项我全都要
const args = […arguments].slice(1)
// 吧 function 进行一个缓存
fn = this
// 返回一个函数 因为 bind 不是立即调用的
return function Fn() {
// 判断 this 原型链上是否又该类型的原型 Fn
return fn.apply(this instanceof Fn ? new fn(…arguments) : context, args.concat(…arguments))
}
}
let obj = {
a: 1
}
function fn(…val) {
console.log(this) // obj
console.log(…val) // 231, 31242344, 432
}
fn._bind(obj, 231, 31242344, 432)()
/*
数组的去重
*/
let ylbsz = [1, 35, 6, 78, 66, 6, 35]
function _set(arr) {
// 放一个新数组
let newArr = []
for (let i = 0; i < arr.length; i++) {
if (newArr.indexOf(arr[i]) == -1) {
newArr.push(arr[i])
}
}
return newArr
}
console.log(_set(ylbsz))
console.log([…new Set([11, 11, 222, 222])])
// 字符串去重
let str = ‘123321你好你好’
console.log([…new Set(str.split(‘’))].join(‘’))
function sort(arr) {
// 外层循环控制的是比较的轮数,你要循环几轮
// arr.length (5 ) - 1 = 4
for (let i = 0; i < arr.length - 1; i++) {
// 内层循环控制的每一轮交换的次数
// 第一轮要比较4次 下标为 0 arr.length - 1(4) - i(0) = 4
// 第二轮要比较3次 下标为 1 arr.length - 1(4) - i(1) = 3
// 第三轮要比较2次 下标为 2 arr.length - 1(4) - i(2) = 2
// 第四轮要比较1次 下标为 3 arr.length - 1(4) - i(3) = 1
// 内层循环控制的每一轮交换的次数
for (let j = 0; j < arr.length - 1 - i; j++) {
// arr[j] 第一项 arr[j + 1] 第二项
// arr[j] 第二项 arr[j + 1] 第三项
// …
if (arr[j] > arr[j + 1]) {
// 大于后一项 做一个交换变量 es6 结构赋值 的变量交换
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]
}
}
}
return arr
}
console.log(sort([200, 100, 3, 9, 4]));
let arr = [1, 4, 6, 7, 7, 9]
// 1. 取出我们数组的中间项 用splice 拿到中间项
// 2. 在声明两个空数组,一个空数组用来放置 中间一项 大于 循环的每一项的话 就把他放到左边的数组里,下
// 余的话就放到右边的数组
// 3.接下来就是循环便利了
// 最后再次利用函数自己调用自己一直执行 递归
// 4. 如果传进来的数组 小于等于 1 的话就终止递归循环
// 做一个快排
function fn(arr) {
// 如果传进来的数组 小于等于 1 的话就终止递归循环
if (arr.length <= 1) return arr
// 总长度除以2的话就可以拿到中间一项的下标
let n = Math.floor(arr.length / 2)
// splice 返回被删除的元素 下标为 0 就是只拿第一项 因为它只返回一个数据 就是被删除的一项
let cen = arr.splice(n, 1)[0]
// 2. 在声明两个空数组,一个空数组用来放置 中间一项 大于 循环的每一项的话 就把他放到左边的数组里,下
// 余的话就放到右边的数组
let leftArr = []
let rightArr = []
// 接下来就是循环便利了 判断
for (let j = 0; j < arr.length; j++) {
let item = arr[j]
// > 如果是大于就是 从大到小 如果是小于 < 从小到大
cen < item ? rightArr.push(item) : leftArr.push(item)
}
return fn(leftArr).concat(cen, fn(rightArr))
}
console.log(fn(arr))
function insert(arr) {
// 1、准备一个数组这个数组就是新抓的牌,开始先抓一张牌进来
let handle = []
handle.push(arr[0])
// 2、第二次开始按顺序抓牌一直把牌抓光了
for (let i = 1; i < arr.length; i++) {
// a 是新抓的牌
let a = arr[i]
// 和手里的牌进行比较(从后向前比)
// handle.length - 1 因为下标是从 0 开始数的
for (let j = handle.length - 1; j >= 0; j–) {
// 每一次要比较手里的牌
let b = handle[j]
// 如果当前新牌a比b 大了,可就要放到 b 的后面
// 如果 a > b 就是表示从小到大
// a < b 送大到小
if (a > b) {
// 如果想要放到 j 的后面就要加上一个 1,
/**
-
比如说我要插队一般要插就是插到 a 的前面
-
但是我想要插到 a 的后面怎么办
-
那这个时候就要插到 a 下一个人的人前面就可以了
*/
handle.splice(j + 1, 0, a)
break
}
// 还有一种情况就是如果你比较到第一项的还比较吗,不比较了
// 我们吧新牌放到最前面即可
if (j == 0) {
handle.unshift(a)
}
}
}
return handle
}
console.log(insert([2, 8, 5, 92, 52, 4]))
const array = [5, 4, 7, 8, 9, 2];
let num = array.reduce((a, b) => a > b ? a : b);
console.log(num) // 9
let nums = array.reduce((a, b) => a < b ? a : b);
console.log(nums) // 2
/@ 数据处理对象去重/
let arr = [
{
a: 1,
b: 2,
id: 6666
},
{
a: 1,
b: 2,
id: 6666
},
{
a: 6,
b: 8,
id: 77777
},
{
a: 8,
b: 2,
id: 88888
},
]
/*
@ 输出 :
0: { a: 1, b: 2, id: 6666 }
1: { a: 6, b: 8, id: 77777 }
2: { a: 8, b: 2, id: 88888 }
*/
function ssff(arr4, id) {
var obj = {};
function deWeightFour() {
arr4 = arr4.reduce(function (a, b) {
obj[b.id] ? ‘’ : obj[b.id] = true && a.push(b);
return a;
}, [])
return arr4;
}
var newArr4 = deWeightFour();
return newArr4
}
console.log(ssff(arr, ‘id’))
~ function () {
// 在函数前加上波浪号,其作用是把函数声明转换为表达式,
// 就可以直接将下面的代码放入某个函数里运行。
// 不用indexOf 和 includes
function myIndexOf(a) {
// 1、 这个也可以正则实现 下面代码
// let reg = new RegExp(a)
// res = reg.exec(this)
// return res === nu ll ? -1 : res.index
// 这个也可以正则实现
let lena = a.length
y = this.length
flag = -1
if (lena > y) return -1
// 如果输入的字符串大于要检测的字符串直接 -1
for (var i = 0; i <= y - lena; i++) {
if (this.substr(i, lena) === a) {
// substr() 方法可在字符串中抽取从 start 下标开始的指定数目的字符。
flag = i
break
}
}
return flag
}
String.prototype.myIndexOf = myIndexOf
}()
let demo = ‘dwanlghMappaw’
let str = ‘h’
console.log(demo.myIndexOf(str));
// instanceof
// 1、只要当前类出现在实例的原型上,结果都为 true
// 2、由于我们可以肆意的修改原型的指向,所以检测出来的结果是不准确的
// 3、不能检测基本数据类型
var arr = []
console.log(arr);
function instance_of(example, classFunc) { // 实例.proto === 类.prototype => instanceof
let classFuncPrototype = classFunc.prototype // 这个就代表类的原型
proto = Object.getPrototypeOf(example) // example.proto 实例的.proto
while (true) { // 我也不知道你要找多少次 while循环
if (proto === null) { // 找到最顶层为null就说明没找到 返回 false
return false
}
if (proto === classFuncPrototype) {
return true
}
proto = Object.getPrototypeOf(proto)
}
}
console.log(instance_of(arr, Array));
console.log(instance_of(arr, Object));
console.log(instance_of(arr, RegExp));
/*
① 首先比较两个对象的长度,如果长度不相等使flag为false,为不相等;
② 如果长度相等那就遍历对象1(对象2也可以),利用hasOwnProperty() 方法查看对象1中是否包含对象2中的属性或者方法,如果不包含则使flag为false,为不想等。
hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)。
③ 接下来判断两对象的内存地址是否相同,不同则为true
*/
function compreObj(obj1, obj2) {
var flag = true;
function compre(obj1, obj2) {
if (Object.keys(obj1).length != Object.keys(obj2).length) {
flag = false;
} else {
for (let x in obj1) {
if (obj2.hasOwnProperty(x)) {
if (obj1[x] !== obj2[x]) {
compre(obj1[x], obj2[x]);
}
} else {
flag = false;
}
}
}
if (flag === false) {
return false;
} else {
return true;
}
}
return compre(obj1, obj2)
}
console.log(compreObj(对象1, 对象2));
var str = “10000000000”;
/*
第一种:把数字转换成字符串后,打散为数组,再从末尾开始,逐个把数组中的元素插入到新数组(result)的开头。 每插入一个元素,counter就计一次数(加1),
当counter为3的倍数时,就插入一个逗号,但是要注意开头(i为0时)不需要逗号。最后通过调用新数组的join方法得出结果。
*/
String.prototype.toThousands = function(){
var num = this;
var result = [ ], counter = 0;
num = (num || 0).toString().split(‘’);
for (var i = num.length - 1; i >= 0; i–) {
counter++;
result.unshift(num[i]);
if (!(counter % 3) && i != 0) { result.unshift(‘,’); }
}
return result.join(‘’);
}
console.log(str.toThousands());
/*
第二种:通过正则表达式循环匹配末尾的三个数字,每匹配一次,就把逗号和匹配到的内容插入到结果字符串的开头,
然后把匹配目标(num)赋值为还没匹配的内(RegExp.leftContext)。如果数字的位数是3的倍数时,最后一次匹配到的内容肯定是三个数字,
但是最前面的三个数字前不需要加逗号;如果数字的位数不是3的倍数,那num变量最后肯定会剩下1到2个数字,循环过后,要把剩余的数字插入到结果字符串的开头。
*/
function toThousands(num) {
var num = (num || 0).toString(), re = /\d{3}$/, result = ‘’;
while ( re.test(num) ) {
result = RegExp.lastMatch + result;
if (num !== RegExp.lastMatch) {
result = ‘,’ + result;
num = RegExp.leftContext;
} else {
num = ‘’;
break;
}
}
if (num) { result = num + result; }
return result;
}
console.log(toThousands(str));
第三种:第二种的改良版
function toThousands(num) {
var num = (num || 0).toString(), result = ‘’;
while (num.length > 3) {
result = ‘,’ + num.slice(-3) + result;
num = num.slice(0, num.length - 3);
}
if (num) { result = num + result; }
return result;
}
console.log(toThousands(str));
第四种:懒人版
function toThousands(num) {
return (num || 0).toString().replace(/(\d)(?=(?:\d{3})+$)/g, ‘$1,’);
}
console.log(toThousands(str));
我个人的理解是,如果一个对象的值等于父级(祖父级,曾祖父级…),则说明是循环引用了
定义一个空数组吗,且对于目标对象进行递归,每次都判断递归项是否为对象,是的话就放到数组里,而且每次判断属性值是否存在,在的话说明环引用了
function cycle(obj, parent) {
//表示调用的父级数组
var parentArr = parent || [obj];
for (var i in obj) {
if (typeof obj[i] === “object”) {
//判断是否有循环引用
parentArr.forEach((pObj) => {
if (pObj === obj[i]) {
obj[i] = “[cycle]”
}
});
cycle(obj[i], […parentArr, obj[i]])
}
}
return obj;
}
/**@2方法*/
function hasLoop(obj){
// 判断对象内部是否有和源相同的属性
function findLoop(target, src){
// 源数组,并将自身传入
const source = src.slice().concat([target])
for(const key in target){
// 如果是对象才需要判断
if(typeof target[key] === ‘object’){
// 如果在源数组中找到 || 递归查找内部属性找到相同
if(source.indexOf(target[key]) > -1 || findLoop(target[key], source)){
return true
}
}
}
return false
}
// 如果传入值是对象,则执行判断,否则返回false
return typeof obj === ‘object’ ? findLoop(obj, []) : false
}
简易版Promise.all
function PromiseAll(promiseArray) { //返回一个Promise对象
return new Promise((resolve, reject) => {
if (!Array.isArray(promiseArray)) { //传入的参数是否为数组
return reject(new Error(‘传入的参数不是数组!’))
}
const res = []
let counter = 0 //设置一个计数器
for (let i = 0; i < promiseArray.length; i++) {
Promise.resolve(promiseArray[i]).then(value => {
counter++ //使用计数器返回 必须使用counter
res[i] = value
if (counter === promiseArray.length) {
resolve(res)
}
}).catch(e => reject(e))
}
})
}
const s1 = new Promise((res, rej) => {
setTimeout(() => {
res(‘p1’)
}, 1000)
})
const s2 = new Promise((res, rej) => {
setTimeout(() => {
res(‘p2’)
}, 2000)
})
const s3 = new Promise((res, rej) => {
setTimeout(() => {
res(‘p3’)
}, 3000)
})
const test = PromiseAll([s1,s2, s3])
.then(res => console.log(res))
.catch(e => console.log(e))
console.log(test);
let arr = [
{ id: 1, name: ‘部门1’, pid: 0 },
{ id: 2, name: ‘部门2’, pid: 1 },
{ id: 3, name: ‘部门3’, pid: 1 },
{ id: 4, name: ‘部门4’, pid: 3 },
{ id: 5, name: ‘部门5’, pid: 4 },
]
// // 上面的数据转换为 下面的 tree 数据
// [
// {
// “id”: 1,
// “name”: “部门1”,
// “pid”: 0,
// “children”: [
// {
// “id”: 2,
// “name”: “部门2”,
// “pid”: 1,
// “children”: []
// },
// {
// “id”: 3,
// “name”: “部门3”,
// “pid”: 1,
// “children”: [
// {
// id: 4,
// name: ‘部门4’,
// pid: 3,
// “children”: [
// {
// id: 5,
// name: ‘部门5’,
// pid: 4,
// “children”: []
// },
// ]
// },
// ]
// }
// ]
// }
// ]
function tree(items) {
// 1、声明一个数组和一个对象 用来存储数据
let arr = []
let obj = {}
// 2、for of 便利我么传进来的一个数据,给当前的数据添加children 属性为 array 把他放到我们的obj对象里面
for (let item of items) {
obj[item.id] = { …item, children: [] }
}
// 3、for of 再次便利然后逻辑处理
for (let item of items) {
// 4、把数据里面的id 取出来赋值 方便下一步的操作
let id = item.id
let pid = item.pid
// 5、根据 id 将 obj 里面的每一项数据取出来
let treeitem = obj[id]
// 6、如果是第一项的话 吧treeitem 放到 arr 数组当中
if (pid === 0) {
// 把数据放到 arr 数组里面
arr.push(treeitem)
} else {
// 如果没有 pid 找不到 就开一个 obj { }
if (!obj[pid]) {
obj = {
children: []
}
}
// 否则给它的 obj 根基 pid(自己定义的下标) 进行查找 它里面的children属性 然后push
obj[pid].children.push(treeitem)
}
}
// 返回处理好的数据
return arr
}
console.log(tree(arr))
-
// LazyMan(‘Hank’);
// 输出:
// Hi! This is Hank!
// LazyMan(‘Hank’).sleep(3).eat(‘dinner’)
// 输出:
// Hi! This is Hank!
// //等待3秒…
// Wake up after 3
// Eat dinner~
// LazyMan(‘Hank’).eat(‘dinner’).eat(‘supper’)
// 输出:
// Hi This is Hank!
// Eat dinner~
// Eat supper~
// LazyMan(‘Hank’).sleepFirst(2).eat(‘dinner’).sleep(3).eat(‘supper’)
// 输出:
// 等待2秒…
// Wake up after 2
ES6
-
列举常用的ES6特性:
-
箭头函数需要注意哪些地方?
-
let、const、var
-
拓展:var方式定义的变量有什么样的bug?
-
Set数据结构
-
拓展:数组去重的方法
-
箭头函数this的指向。
-
手写ES6 class继承。
微信小程序
-
简单描述一下微信小程序的相关文件类型?
-
你是怎么封装微信小程序的数据请求?
-
有哪些参数传值的方法?
-
你使用过哪些方法,来提高微信小程序的应用速度?
-
小程序和原生App哪个好?
-
简述微信小程序原理?
-
分析微信小程序的优劣势
-
怎么解决小程序的异步请求问题?
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
rop] = valueobserver()
}
})
function observer() {
li.innerHTML = obj.name
search.value = obj.name
}
setTimeout(() => {
obj.name = ‘孙志豪’
}, 1000)
search.oninput = function () {
obj.name = this.value
}
// LazyMan(‘Hank’);
// 输出:
// Hi! This is Hank!
// LazyMan(‘Hank’).sleep(3).eat(‘dinner’)
// 输出:
// Hi! This is Hank!
// //等待3秒…
// Wake up after 3
// Eat dinner~
// LazyMan(‘Hank’).eat(‘dinner’).eat(‘supper’)
// 输出:
// Hi This is Hank!
// Eat dinner~
// Eat supper~
// LazyMan(‘Hank’).sleepFirst(2).eat(‘dinner’).sleep(3).eat(‘supper’)
// 输出:
// 等待2秒…
// Wake up after 2
ES6
-
列举常用的ES6特性:
-
箭头函数需要注意哪些地方?
-
let、const、var
-
拓展:var方式定义的变量有什么样的bug?
-
Set数据结构
-
拓展:数组去重的方法
-
箭头函数this的指向。
-
手写ES6 class继承。
微信小程序
-
简单描述一下微信小程序的相关文件类型?
-
你是怎么封装微信小程序的数据请求?
-
有哪些参数传值的方法?
-
你使用过哪些方法,来提高微信小程序的应用速度?
-
小程序和原生App哪个好?
-
简述微信小程序原理?
-
分析微信小程序的优劣势
-
怎么解决小程序的异步请求问题?
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)
[外链图片转存中…(img-0eXj4E6J-1713477643961)]一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
-