2024年Web前端面试题(最全、最详细、持续更新)_web前端面试问题(1)

ajax

1)ajax请求的原理/ 手写一个ajax请求?
2)readyState?
3)ajax异步与同步的区别?
4)ajax传递中文用什么方法?

ajax.PNG

前12.PNG

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

function log() {
console.log(“99”);
}
let box1 = document.querySelector(“#box1”);
box1.onclick = throttle(log, 4000);


#### 深克隆



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)
})
}


#### url截取参数转换为对象



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;
// }


#### new操作符



function create(Con, …args) {
let obj = {}
Object.setPrototypeOf(obj, Con.prototype)
let result = Con.apply(obj, args)
return result instanceof Object ? result : obj
}


#### map实现



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))
}, [])
}


#### forEach实现



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(第 i n d e x 个元素是 {index}个元素是 index个元素是{item},数组是${array});
});


#### 找出2个数组中不重复出现的数字


* 输入 array1 = [1,2,3,4,5] array2 = [2,3,4,6], 输出 [1,5,6]


#### 数组对象去重



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));


#### 合并2个有序链表



/**

● @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;
};


#### 删除链表的倒数第 N 个结点



/**

● @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
};


#### leetcode 88 合并2个有序数组 将num2合入nums1


例: 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]
}
};


#### leetcode 70 爬楼梯



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));


### 手写题


#### 对象结构转树结构



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);


### 源码


#### 封装jequry中的AJAX的应用



    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);
        }
    })

#### JQ源码简单理解



<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>

#### jquery核心源码



<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>

#### Vuex的源码


Vue的插件必须使用Vue.use;只是vuex会默认检测到是vue的官方插件,看不到vue.use;vue.use执行时,会默认调用里面的install;  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/6ea50ecc967f456ab3ab3c893f62061c.png)



{{$store.state.count}}

#### Vuex核心源码封装



{{$store.state.msg}} {{$store.getters.str}}

// 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.mutationstype
}
dispatch(type,option){
this.actionstype
}
}
//…Vuex.mapState([‘a’,“b”]);
// 将store中的state放到当前的computed属性中
function mapState(ary){
let obj ={};
ary.forEach(key=>{
obj[key]=function(){
console.log(this);
// this 执行组件的实例
return this.KaTeX parse error: Expected 'EOF', got '}' at position 19: …re.state[key] }̲ }) return ob…store.getters[key]
}
})
return obj;
}
function mapActions(ary){
let obj ={};
ary.forEach(key=>{
obj[key]=function(option){
return this.KaTeX parse error: Expected 'EOF', got '}' at position 29: …h(key,option) }̲ }) return ob…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. s t o r e = t h i s . store=this. store=this.options.store
//console.log(this);
// 这个会进行循环遍历,
if(this.KaTeX parse error: Expected 'EOF', got '&' at position 8: options&̲&this.options.store){
// 如果该条件是成立的,说明是vm实例;
this. s t o r e = t h i s . store=this. store=this.options.store;
}else{
// 如果不成立,说明是子孙组件
// 如果父组件存在,那么就把父组件的 s t o r e 属性赋值给子组件的 store属性赋值给子组件的 store属性赋值给子组件的store属性;
// p a r e n t : 指的是当前组件的父组件 t h i s . parent : 指的是当前组件的父组件 this. parent:指的是当前组件的父组件this.store =this.KaTeX parse error: Expected 'EOF', got '&' at position 7: parent&̲&this.parent.KaTeX parse error: Expected 'EOF', got '}' at position 8: store }̲ } }) } ret…store=storeStore的实例
this. s t o r e . c o m m i t ( " a d d " , 100 ) ; / / t h i s . store.commit("add",100); // this. store.commit("add",100);//this.store.dispatch(“addNum”,1)
}
},
computed:{
str(){

},
…Vuex.mapState([‘count’])
},
template:“

{{$store.state.count}}{{count}}<button @click=‘fn’>增加

}
// KaTeX parse error: Expected '}', got '#' at position 47: …new Vue({ el:"#̲app", store,//…store属性;
//a:100,
beforeCreate(){
//console.log(this);
// debugger
},
// 把Store的一个实例给了这个store属性
// 组件在注册时,不会调用生命周期钩子函数,
components:{child}
});
//console.log(vm);// 目前vm身上没有$store
// o p t i o n s : 代表 : n e w 的对象,会把对象中的键值对添加到当前实例的 options:代表 :new的对象,会把对象中的键值对添加到当前实例的 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属性;


#### VueRouter源码



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;


#### redux源码



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>

#### react-redux工程化



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


+
{this.props.num}
-

}
}

//源码
// 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(actionkey)
// }
// }
// return obj;
// }


#### Promise封装


![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/9901380f6d3b4746bcc8dcea57ecd6b6.png)



<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;

Vue

  • 什么是MVVM?

  • mvvm和mvc区别?它和其它框架(jquery)的区别是什么?哪些场景适合?

  • 组件之间的传值?

  • Vue 双向绑定原理

  • 描述下 vue 从初始化页面–修改数据–刷新页面 UI 的过程?

  • 虚拟 DOM 实现原理

  • Vue 中 key 值的作用?

  • Vue 的生命周期

  • Vue 组件间通信有哪些方式?

  • vue 中怎么重置 data?

  • 组件中写 name 选项有什么作用?

  • Vue 的 nextTick 的原理是什么?

  • Vuex 有哪几种属性?

    开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

/ obj[key]= ()=>{
// dispatch(actionkey)
// }
// }
// return obj;
// }


#### Promise封装


![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/9901380f6d3b4746bcc8dcea57ecd6b6.png)



<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;

Vue

  • 什么是MVVM?

  • mvvm和mvc区别?它和其它框架(jquery)的区别是什么?哪些场景适合?

  • 组件之间的传值?

  • Vue 双向绑定原理

  • 描述下 vue 从初始化页面–修改数据–刷新页面 UI 的过程?

  • 虚拟 DOM 实现原理

  • Vue 中 key 值的作用?

  • Vue 的生命周期

  • Vue 组件间通信有哪些方式?

  • vue 中怎么重置 data?

  • 组件中写 name 选项有什么作用?

  • Vue 的 nextTick 的原理是什么?

  • Vuex 有哪几种属性?

    开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值