关于前端:社招前端高频面试题

30次阅读

共计 12111 个字符,预计需要花费 31 分钟才能阅读完成。

代码输入后果

Promise.reject('err!!!')
  .then((res) => {console.log('success', res)
  }, (err) => {console.log('error', err)
  }).catch(err => {console.log('catch', err)
  })

输入后果如下:

error err!!!

咱们晓得,.then函数中的两个参数:

  • 第一个参数是用来解决 Promise 胜利的函数
  • 第二个则是解决失败的函数

也就是说 Promise.resolve('1') 的值会进入胜利的函数,Promise.reject('2')的值会进入失败的函数。

在这道题中,谬误间接被 then 的第二个参数捕捉了,所以就不会被 catch 捕捉了,输入后果为:error err!!!'

然而,如果是像上面这样:

Promise.resolve()
  .then(function success (res) {throw new Error('error!!!')
  }, function fail1 (err) {console.log('fail1', err)
  }).catch(function fail2 (err) {console.log('fail2', err)
  })

then 的第一参数中抛出了谬误,那么他就不会被第二个参数不活了,而是被前面的 catch 捕捉到。

—- 问题知识点分割线 —-

列举几个 css 中可继承和不可继承的元素

  • 不可继承的:display、margin、border、padding、background、height、min-height、max-height、width、min-width、max-width、overflow、position、left、right、top、bottom、z-index、float、clear、table-layout、vertical-align
  • 所有元素可继承:visibilitycursor
  • 内联元素可继承:letter-spacing、word-spacing、white-space、line-height、color、font、font-family、font-size、font-style、font-variant、font-weight、text-decoration、text-transform、direction
  • 终端块状元素可继承:text-indent 和 text-align
  • 列表元素可继承:list-style、list-style-type、list-style-position、list-style-image`。

transition 和 animation 的区别

Animationtransition 大部分属性是雷同的,他们都是随工夫扭转元素的属性值,他们的次要区别是 transition 须要触发一个事件能力扭转属性,而 animation 不须要触发任何事件的状况下才会随工夫扭转属性值,并且 transition 为 2 帧,从 from .... to,而animation 能够一帧一帧的

—- 问题知识点分割线 —-

什么状况会阻塞渲染?

首先渲染的前提是生成渲染树,所以 HTML 和 CSS 必定会阻塞渲染。如果你想渲染的越快,你越应该升高一开始须要渲染的文件大小,并且扁平层级,优化选择器。而后当浏览器在解析到 script 标签时,会暂停构建 DOM,实现后才会从暂停的中央从新开始。也就是说,如果你想首屏渲染的越快,就越不应该在首屏就加载 JS 文件,这也是都倡议将 script 标签放在 body 标签底部的起因。

当然在当下,并不是说 script 标签必须放在底部,因为你能够给 script 标签增加 defer 或者 async 属性。当 script 标签加上 defer 属性当前,示意该 JS 文件会并行下载,然而会放到 HTML 解析实现后程序执行,所以对于这种状况你能够把 script 标签放在任意地位。对于没有任何依赖的 JS 文件能够加上 async 属性,示意 JS 文件下载和解析不会阻塞渲染。

—- 问题知识点分割线 —-

代码输入后果

function runAsync (x) {const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000))
  return p
}
function runReject (x) {const p = new Promise((res, rej) => setTimeout(() => rej(`Error: ${x}`, console.log(x)), 1000 * x))
  return p
}
Promise.all([runAsync(1), runReject(4), runAsync(3), runReject(2)])
       .then(res => console.log(res))
       .catch(err => console.log(err))

输入后果如下:

// 1s 后输入
1
3
// 2s 后输入
2
Error: 2
// 4s 后输入
4

能够看到。catch 捕捉到了第一个谬误,在这道题目中最先的谬误就是 runReject(2) 的后果。如果一组异步操作中有一个异样都不会进入 .then() 的第一个回调函数参数中。会被 .then() 的第二个回调函数捕捉。

—- 问题知识点分割线 —-

动静布局求解硬币找零问题

题目形容: 给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算能够凑成总金额所需的起码的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1

示例 1:输出: coins = [1, 2, 5], amount = 11
输入: 3
解释: 11 = 5 + 5 + 1

示例 2:输出: coins = [2], amount = 3
输入: -1

实现代码如下:

const coinChange = function (coins, amount) {
  // 用于保留每个指标总额对应的最小硬币个数
  const f = [];
  // 提前定义已知状况
  f[0] = 0;
  // 遍历 [1, amount] 这个区间的硬币总额
  for (let i = 1; i <= amount; i++) {
    // 求的是最小值,因而咱们预设为无穷大,确保它肯定会被更小的数更新
    f[i] = Infinity;
    // 循环遍历每个可用硬币的面额
    for (let j = 0; j < coins.length; j++) {
      // 若硬币面额小于指标总额,则问题成立
      if (i - coins[j] >= 0) {
        // 状态转移方程
        f[i] = Math.min(f[i], f[i - coins[j]] + 1);
      }
    }
  }
  // 若指标总额对应的解为无穷大,则意味着没有一个符合条件的硬币总数来更新它,本题无解,返回 -1
  if (f[amount] === Infinity) {return -1;}
  // 若有解,间接返回解的内容
  return f[amount];
};

—- 问题知识点分割线 —-

对事件循环的了解

因为 js 是单线程运行的,在代码执行时,通过将不同函数的执行上下文压入执行栈中来保障代码的有序执行。在执行同步代码时,如果遇到异步事件,js 引擎并不会始终期待其返回后果,而是会将这个事件挂起,继续执行执行栈中的其余工作。当异步事件执行结束后,再将异步事件对应的回调退出到一个工作队列中期待执行。工作队列能够分为宏工作队列和微工作队列,当以后执行栈中的事件执行结束后,js 引擎首先会判断微工作队列中是否有工作能够执行,如果有就将微工作队首的事件压入栈中执行。当微工作队列中的工作都执行实现后再去执行宏工作队列中的工作。

Event Loop 执行程序如下所示:

  • 首先执行同步代码,这属于宏工作
  • 当执行完所有同步代码后,执行栈为空,查问是否有异步代码须要执行
  • 执行所有微工作
  • 当执行完所有微工作后,如有必要会渲染页面
  • 而后开始下一轮 Event Loop,执行宏工作中的异步代码

—- 问题知识点分割线 —-

一个 tcp 连贯能发几个 http 申请?

如果是 HTTP 1.0 版本协定,个别状况下,不反对长连贯,因而在每次申请发送结束之后,TCP 连贯即会断开,因而一个 TCP 发送一个 HTTP 申请,然而有一种状况能够将一条 TCP 连贯放弃在沉闷状态,那就是通过 Connection 和 Keep-Alive 首部,在申请头带上 Connection: Keep-Alive,并且能够通过 Keep-Alive 通用首部中指定的,用逗号分隔的选项调节 keep-alive 的行为,如果客户端和服务端都反对,那么其实也能够发送多条,不过此形式也有限度,能够关注《HTTP 权威指南》4.5.5 节对于 Keep-Alive 连贯的限度和规定。

而如果是 HTTP 1.1 版本协定,反对了长连贯,因而只有 TCP 连接不断开,便能够始终发送 HTTP 申请,继续一直,没有下限;同样,如果是 HTTP 2.0 版本协定,反对多用复用,一个 TCP 连贯是能够并发多个 HTTP 申请的,同样也是反对长连贯,因而只有一直开 TCP 的连贯,HTTP 申请数也是能够没有下限地继续发送

—- 问题知识点分割线 —-

代码输入后果

var a, b
(function () {console.log(a);
   console.log(b);
   var a = (b = 3);
   console.log(a);
   console.log(b);   
})()
console.log(a);
console.log(b);

输入后果:

undefined 
undefined 
3 
3 
undefined 
3

这个题目和下面题目考查的知识点相似,b 赋值为 3,b 此时是一个全局变量,而将 3 赋值给 a,a 是一个局部变量,所以最初打印的时候,a 仍旧是 undefined。

—- 问题知识点分割线 —-

CDN 的概念

CDN(Content Delivery Network,内容散发网络)是指一种通过互联网相互连贯的电脑网络零碎,利用最靠近每位用户的服务器,更快、更牢靠地将音乐、图片、视频、应用程序及其他文件发送给用户,来提供高性能、可扩展性及低成本的网络内容传递给用户。

典型的 CDN 零碎由上面三个局部组成:

  • 散发服务零碎: 最根本的工作单元就是 Cache 设施,cache(边缘 cache)负责间接响应最终用户的拜访申请,把缓存在本地的内容疾速地提供给用户。同时 cache 还负责与源站点进行内容同步,把更新的内容以及本地没有的内容从源站点获取并保留在本地。Cache 设施的数量、规模、总服务能力是掂量一个 CDN 零碎服务能力的最根本的指标。
  • 负载平衡零碎: 次要性能是负责对所有发动服务申请的用户进行拜访调度,确定提供给用户的最终理论拜访地址。两级调度体系分为全局负载平衡(GSLB)和本地负载平衡(SLB)。全局负载平衡 次要依据用户就近性准则,通过对每个服务节点进行“最优”判断,确定向用户提供服务的 cache 的物理地位。本地负载平衡 次要负责节点外部的设施负载平衡
  • 经营管理系统: 经营管理系统分为经营治理和网络管理子系统,负责解决业务层面的与外界零碎交互所必须的收集、整顿、交付工作,蕴含客户治理、产品治理、计费治理、统计分析等性能。

—- 问题知识点分割线 —-

写代码:实现函数可能深度克隆根本类型

浅克隆:

function shallowClone(obj) {let cloneObj = {};

  for (let i in obj) {cloneObj[i] = obj[i];
  }

  return cloneObj;
}

深克隆:

  • 思考根底类型
  • 援用类型

    • RegExp、Date、函数 不是 JSON 平安的
    • 会失落 constructor,所有的构造函数都指向 Object
    • 破解循环援用
function deepCopy(obj) {if (typeof obj === 'object') {var result = obj.constructor === Array ? [] : {};

    for (var i in obj) {result[i] = typeof obj[i] === 'object' ? deepCopy(obj[i]) : obj[i];
    }
  } else {var result = obj;}

  return result;
}

—- 问题知识点分割线 —-

首屏和白屏工夫如何计算

首屏工夫的计算,能够由 Native WebView 提供的相似 onload 的办法实现,在 ios 下对应的是 webViewDidFinishLoad,在 android 下对应的是 onPageFinished 事件。

白屏的定义有多种。能够认为“没有任何内容”是白屏,能够认为“网络或服务异样”是白屏,能够认为“数据加载中”是白屏,能够认为“图片加载不进去”是白屏。场景不同,白屏的计算形式就不雷同。

办法 1:当页面的元素数小于 x 时,则认为页面白屏。比方“没有任何内容”,能够获取页面的 DOM 节点数,判断 DOM 节点数少于某个阈值 X,则认为白屏。办法 2:当页面呈现业务定义的错误码时,则认为是白屏。比方“网络或服务异样”。办法 3:当页面呈现业务定义的特征值时,则认为是白屏。比方“数据加载中”。

—- 问题知识点分割线 —-

函数中的 arguments 是数组吗?类数组转数组的办法理解一下?

是类数组,是属于鸭子类型的领域,长得像数组,

  • … 运算符
  • Array.from
  • Array.prototype.slice.apply(arguments)

—- 问题知识点分割线 —-

浏览器的渲染过程

浏览器渲染次要有以下步骤:

  • 首先解析收到的文档,依据文档定义构建一棵 DOM 树,DOM 树是由 DOM 元素及属性节点组成的。
  • 而后对 CSS 进行解析,生成 CSSOM 规定树。
  • 依据 DOM 树和 CSSOM 规定树构建渲染树。渲染树的节点被称为渲染对象,渲染对象是一个蕴含有色彩和大小等属性的矩形,渲染对象和 DOM 元素绝对应,但这种对应关系不是一对一的,不可见的 DOM 元素不会被插入渲染树。还有一些 DOM 元素对应几个可见对象,它们个别是一些具备简单构造的元素,无奈用一个矩形来形容。
  • 当渲染对象被创立并增加到树中,它们并没有地位和大小,所以当浏览器生成渲染树当前,就会依据渲染树来进行布局(也能够叫做回流)。这一阶段浏览器要做的事件是要弄清楚各个节点在页面中的确切地位和大小。通常这一行为也被称为“主动重排”。
  • 布局阶段完结后是绘制阶段,遍历渲染树并调用渲染对象的 paint 办法将它们的内容显示在屏幕上,绘制应用 UI 根底组件。

大抵过程如图所示:

留神: 这个过程是逐渐实现的,为了更好的用户体验,渲染引擎将会尽可能早的将内容出现到屏幕上,并不会等到所有的 html 都解析实现之后再去构建和布局 render 树。它是解析完一部分内容就显示一部分内容,同时,可能还在通过网络下载其余内容。

—- 问题知识点分割线 —-

代码输入后果

var F = function() {};
Object.prototype.a = function() {console.log('a');
};
Function.prototype.b = function() {console.log('b');
}
var f = new F();
f.a();
f.b();
F.a();
F.b()

输入后果:

a
Uncaught TypeError: f.b is not a function
a
b

解析:

  1. f 并不是 Function 的实例,因为它原本就不是构造函数,调用的是 Function 原型链上的相干属性和办法,只能拜访到 Object 原型链。所以 f.a() 输入 a,而 f.b() 就报错了。
  2. F 是个构造函数,而 F 是构造函数 Function 的一个实例。因为 F instanceof  Object === true,F instanceof Function === true,由此能够得出结论:F 是 Object 和 Function 两个的实例,即 F 能拜访到 a,也能拜访到 b。所以 F.a() 输入 a,F.b() 输入 b。

—- 问题知识点分割线 —-

如何对我的项目中的图片进行优化?

  1. 不必图片。很多时候会应用到很多润饰类图片,其实这类润饰图片齐全能够用 CSS 去代替。
  2. 对于挪动端来说,屏幕宽度就那么点,齐全没有必要去加载原图节约带宽。个别图片都用 CDN 加载,能够计算出适配屏幕的宽度,而后去申请相应裁剪好的图片。
  3. 小图应用 base64 格局
  4. 将多个图标文件整合到一张图片中(雪碧图)
  5. 抉择正确的图片格式:

    • 对于可能显示 WebP 格局的浏览器尽量应用 WebP 格局。因为 WebP 格局具备更好的图像数据压缩算法,能带来更小的图片体积,而且领有肉眼辨认无差别的图像品质,毛病就是兼容性并不好
    • 小图应用 PNG,其实对于大部分图标这类图片,齐全能够应用 SVG 代替
    • 照片应用 JPEG

—- 问题知识点分割线 —-

如何⽤ webpack 来优化前端性能?

⽤ webpack 优化前端性能是指优化 webpack 的输入后果,让打包的最终后果在浏览器运⾏疾速⾼效。

  • 压缩代码:删除多余的代码、正文、简化代码的写法等等⽅式。能够利⽤ webpack 的 UglifyJsPlugin 和 ParallelUglifyPlugin 来压缩 JS ⽂件,利⽤ cssnano(css-loader?minimize)来压缩 css
  • 利⽤ CDN 减速: 在构建过程中,将引⽤的动态资源门路批改为 CDN 上对应的门路。能够利⽤ webpack 对于 output 参数和各 loader 的 publicPath 参数来批改资源门路
  • Tree Shaking: 将代码中永远不会⾛到的⽚段删除掉。能够通过在启动 webpack 时追加参数 –optimize-minimize 来实现
  • Code Splitting: 将代码按路由维度或者组件分块(chunk), 这样做到按需加载, 同时能够充沛利⽤浏览器缓存
  • 提取公共第三⽅库: SplitChunksPlugin 插件来进⾏公共模块抽取, 利⽤浏览器缓存能够⻓期缓存这些⽆需频繁变动的公共代码

—- 问题知识点分割线 —-

React 17 带来了哪些扭转

最重要的是以下三点:

  • 新的 JSX 转换逻辑
  • 事件零碎重构
  • Lane 模型 的引入

1. 重构 JSX 转换逻辑

在过来,如果咱们在 React 我的项目中写入上面这样的代码:

function MyComponent() {return <p> 这是我的组件 </p>}

React 是会报错的,起因是 React 中对 JSX 代码的转换依赖的是 React.createElement 这个函数。因而凡是咱们在代码中蕴含了 JSX,那么就必须在文件中引入 React,像上面这样:

import React from 'react';
function MyComponent() {return <p> 这是我的组件 </p>}

React 17 则容许咱们在不引入 React 的状况下间接应用 JSX。这是因为在 React 17 中,编译器会主动帮咱们引入 JSX 的解析器,也就是说像上面这样一段逻辑:

function MyComponent() {return <p> 这是我的组件 </p>}

会被编译器转换成这个样子:

import {jsx as _jsx} from 'react/jsx-runtime';
function MyComponent() {return _jsx('p', { children: '这是我的组件'});
}

react/jsx-runtime 中的 JSX 解析器将取代 React.createElement 实现 JSX 的编译工作,这个过程对开发者而言是自动化、无感知的。因而,新的 JSX 转换逻辑带来的最显著的扭转就是升高了开发者的学习老本。

react/jsx-runtime 中的 JSX 解析器看上去仿佛在调用姿态上和 React.createElement 区别不大,那么它是否只是 React.createElement 换了个马甲呢?当然不是,它在外部实现了 React.createElement 无奈做到的性能优化和简化。在肯定状况下,它可能会稍微改善编译输入内容的大小

2. 事件零碎重构

事件零碎在 React 17 中的重构要从以下两个方面来看:

  • 卸掉历史包袱
  • 拥抱新的潮流

2.1 卸掉历史包袱:放弃利用 document 来做事件的中心化管控

React 16.13.x 版本中的事件零碎会通过将所有事件冒泡到 document 来实现对事件的中心化管控

这样的做法尽管看上去曾经足够奇妙,但依然有它不聪慧的中央——document 是整个文档树的根节点,操作 document 带来的影响范畴切实是太大了,这将会使事件变得更加不可控

在 React 17 中,React 团队终于侧面解决了这个问题:事件的中心化管控不会再全副依赖 document,管控相干的逻辑被转移到了每个 React 组件本人的容器 DOM 节点中。比如说咱们在 ID 为 root 的 DOM 节点下挂载了一个 React 组件,像上面代码这样:

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

那么事件管控相干的逻辑就会被装置到 root 节点 下来。这样一来,React 组件就可能本人玩本人的,再也无奈对全局的事件流构成威胁了

2.2 拥抱新的潮流:放弃事件池

在 React 17 之前,合成事件对象会被放进一个叫作“事件池”的中央对立治理。这样做的目标是可能实现事件对象的复用,进而进步性能:每当事件处理函数执行结束后,其对应的合成事件对象外部的所有属性都会被置空,意在为下一次被复用做筹备。这也就意味着事件逻辑一旦执行结束,咱们就拿不到事件对象了,React 官网给出的这个例子就很能阐明问题,请看上面这个代码

function handleChange(e) {
  // This won't work because the event object gets reused.
  setTimeout(() => {console.log(e.target.value); // Too late!
  }, 100);
}

异步执行的 setTimeout 回调会在 handleChange 这个事件处理函数执行结束后执行,因而它拿不到想要的那个事件对象 e

要想拿到指标事件对象,必须显式地通知 React——我永远须要它,也就是调用 e.persist() 函数,像上面这样:

function handleChange(e) {
  // Prevents React from resetting its properties:
  e.persist();
  setTimeout(() => {console.log(e.target.value); // Works
  }, 100);
}

在 React 17 中,咱们不须要 e.persist(),也能够随时随地拜访咱们想要的事件对象。

3. Lane 模型的引入

初学 React 源码的同学由此可能会很天然地认为:优先级就应该是用 Lane 来解决的。但事实上,React 16 中解决优先级采纳的是 expirationTime 模型

expirationTime 模型应用 expirationTime(一个工夫长度)来形容工作的优先级;而 Lane 模型 则应用 二进制数来示意工作的优先级

lane 模型 通过将不同优先级赋值给一个位,通过 31 位的位运算 来操作优先级。

Lane 模型 提供了一个新的优先级排序的思路,绝对于 expirationTime 来说,它对优先级的解决会更细腻,可能笼罩更多的边界条件。

—- 问题知识点分割线 —-

代码输入后果

function runAsync(x) {
  const p = new Promise(r =>
    setTimeout(() => r(x, console.log(x)), 1000)
  );
  return p;
}
function runReject(x) {const p = new Promise((res, rej) =>
    setTimeout(() => rej(`Error: ${x}`, console.log(x)), 1000 * x)
  );
  return p;
}
Promise.race([runReject(0), runAsync(1), runAsync(2), runAsync(3)])
  .then(res => console.log("result:", res))
  .catch(err => console.log(err));

输入后果如下:

0
Error: 0
1
2
3

能够看到在 catch 捕捉到第一个谬误之后,前面的代码还不执行,不过不会再被捕捉了。

留神:allrace 传入的数组中如果有会抛出异样的异步工作,那么只有最先抛出的谬误会被捕捉,并且是被 then 的第二个参数或者前面的 catch 捕捉;但并不会影响数组中其它的异步工作的执行。

—- 问题知识点分割线 —-

如何判断一个对象是不是空对象?

Object.keys(obj).length === 0

手写题:在线编程,getUrlParams(url,key); 就是很简略的获取 url 的某个参数的问题,但要思考边界状况,多个返回值等等

—- 问题知识点分割线 —-

connect 组件原理剖析

1. connect 用法

作用:连贯 React 组件与 Redux store

connect([mapStateToProps], [mapDispatchToProps], [mergeProps],[options])
// 这个函数容许咱们将 store 中的数据作为 props 绑定到组件上
const mapStateToProps = (state) => {
  return {count: state.count}
}
  • 这个函数的第一个参数就是 Reduxstore,咱们从中摘取了 count 属性。你不用将 state 中的数据一成不变地传入组件,能够依据 state 中的数据,动静地输入组件须要的(最小)属性
  • 函数的第二个参数 ownProps,是组件本人的 props

state 变动,或者 ownProps 变动的时候,mapStateToProps 都会被调用,计算出一个新的 stateProps,(在与 ownProps merge 后)更新给组件

mapDispatchToProps(dispatch, ownProps): dispatchProps

connect 的第二个参数是 mapDispatchToProps,它的性能是,将 action 作为 props绑定到组件上,也会成为 MyComp 的 `props

2. 原理解析

首先 connect 之所以会胜利,是因为 Provider 组件

  • 在原利用组件上包裹一层,使原来整个利用成为 Provider 的子组件
  • 接管 Reduxstore作为 props,通过context 对象传递给子孙组件上的connect

connect 做了些什么

它真正连贯 ReduxReact,它包在咱们的容器组件的外一层,它接管下面 Provider提供的 store 外面的 statedispatch,传给一个构造函数,返回一个对象,以属性模式传给咱们的容器组件

3. 源码

connect是一个高阶函数,首先传入 mapStateToPropsmapDispatchToProps,而后返回一个生产Component 的函数 (wrapWithConnect),而后再将真正的Component 作为参数传入 wrapWithConnect,这样就生产出一个通过包裹的Connect 组件,该组件具备如下特点

  • 通过 props.store 获取先人 Componentstore props包含 statePropsdispatchPropsparentProps, 合并在一起失去nextState,作为props 传给真正的Component
  • componentDidMount时,增加事件this.store.subscribe(this.handleChange),实现页面交互
  • shouldComponentUpdate时判断是否有防止进行渲染,晋升页面性能,并失去nextState
  • componentWillUnmount时移除注册的事件this.handleChange
// 次要逻辑

export default function connect(mapStateToProps, mapDispatchToProps, mergeProps, options = {}) {return function wrapWithConnect(WrappedComponent) {
    class Connect extends Component {constructor(props, context) {
        // 从先人 Component 处取得 store
        this.store = props.store || context.store
        this.stateProps = computeStateProps(this.store, props)
        this.dispatchProps = computeDispatchProps(this.store, props)
        this.state = {storeState: null}
        // 对 stateProps、dispatchProps、parentProps 进行合并
        this.updateState()}
      shouldComponentUpdate(nextProps, nextState) {
        // 进行判断,当数据产生扭转时,Component 从新渲染
        if (propsChanged || mapStateProducedChange || dispatchPropsChanged) {this.updateState(nextProps)
            return true
          }
        }
        componentDidMount() {
          // 扭转 Component 的 state
          this.store.subscribe(() = {
            this.setState({storeState: this.store.getState()
            })
          })
        }
        render() {
          // 生成包裹组件 Connect
          return (<WrappedComponent {...this.nextState} />
          )
        }
      }
      Connect.contextTypes = {store: storeShape}
      return Connect;
    }
}

正文完
 0