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

css

1,盒模型
2,如何实现一个最大的正方形
3,一行水平居中,多行居左
4,水平垂直居中
5,两栏布局,左边固定,右边自适应,左右不重叠
6,如何实现左右等高布局
7,画三角形
8,link @import导入css
9,BFC理解

js

1,判断 js 类型的方式
2,ES5 和 ES6 分别几种方式声明变量
3,闭包的概念?优缺点?
4,浅拷贝和深拷贝
5,数组去重的方法
6,DOM 事件有哪些阶段?谈谈对事件代理的理解
7,js 执行机制、事件循环
8,介绍下 promise.all
9,async 和 await,
10,ES6 的 class 和构造函数的区别
11,transform、translate、transition 分别是什么属性?CSS 中常用的实现动画方式,
12,介绍一下rAF(requestAnimationFrame)
13,javascript 的垃圾回收机制讲一下,
14,对前端性能优化有什么了解?一般都通过那几个方面去优化的?

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

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(第${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;
在这里插入图片描述

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

Vuex核心源码封装
<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>

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

Promise封装

在这里插入图片描述

    <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 = () => {}
                }


### 文末
篇幅有限没有列举更多的前端面试题,小编把整理的前端大厂面试题PDF分享出来,一共有269页

![](https://img-blog.csdnimg.cn/img_convert/04273c2f50e58b6822663c5b2f379206.png)  

![](https://img-blog.csdnimg.cn/img_convert/081b5236ebd9ff24af676bcd4a51c99e.png)

**[开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】](https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0)**

) {
                                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 = () => {}
                }


### 文末
篇幅有限没有列举更多的前端面试题,小编把整理的前端大厂面试题PDF分享出来,一共有269页

![](https://img-blog.csdnimg.cn/img_convert/04273c2f50e58b6822663c5b2f379206.png)  

![](https://img-blog.csdnimg.cn/img_convert/081b5236ebd9ff24af676bcd4a51c99e.png)

**[开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】](https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0)**

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值