Fork me on GitHub

Vue3 组合式 API

# 基本范例

<template>
  <button @click="increment">
    Count is: {{ state.count }}, double is: {{ state.double }}
  </button>
</template>

<script>
  import { reactive, computed } from 'vue'

  export default {
    setup() {
      const state = reactive({
        count: 0,
        double: computed(() => state.count * 2),
      })

      function increment() {
        state.count++
      }

      return {
        state,
        increment,
      }
    },
  }
</script>

# Vue 组合式 API

# setup

vue3 比 vue2.x 版本的优势

—— 先学习 Vue2.x

  • Vue3.0 并不是推倒从来,很多 2.x 内容依然被保存

—— 先学习 Typescript

  • Vue3.0 采用 TS 重写,必须学 TS

# 一、Vue3.0 的六大亮点

  1. Performance:性能比 Vue2.x 快 1.2 ~ 2 倍
  2. Tree shaking support:按需编译,体积比 Vue2.x 更小
  3. Composition API *组合式 API(类似 React Hooks)
  4. Better Typescript support:更好的 TS 支持
  5. Custom Renderer API:暴露了自定义渲染 API
  6. Fragment,Teleport(Protal),Suspense:更先进的组件

# 二、Vue3.0 是如何变快的

Vue3 源码编译工具:Vue3 模版编译工具

Vue2.x 模版编译工具:Vue2.x 模版编译工具

# 1、diff 方法优化

  • Vue2 中的虚拟 DOM 是进行全亮的对比
  • Vue3 新增了静态标记(PatchFlag)
    • 在与上次虚拟节点进行对比的时候,只对比带有 patch flag 的节点
    • 并且可以通过 flag 的信息得知当前节点要对比的具体内容
<div id="app">
    <h1>技术摸鱼</h1>
    <p>今天天气真不错</p>
    <div>{{name}}</div>
</div>

--------------------

import { createVNode as _createVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createBlock as _createBlock } from "vue"

export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createBlock("div", { id: "app" }, [
    _createVNode("h1", null, "技术摸鱼"),
    _createVNode("p", null, "今天天气真不错"),
    _createVNode("div", null, _toDisplayString(_ctx.name), 1 /* TEXT */),
    _createVNode("div", {
      class: {red:_ctx.isRed}
    }, "摸鱼符", 2 /* CLASS */),
    _createVNode("button", { onClick: _ctx.handleClick }, "戳我", 8 /* PROPS */, ["onClick"])
  ]))
}

# 2、hoistStatic 静态提升

  • Vue2 中无论元素是否参与更新,每次都会重新创建
  • Vue3 中对于不参与更新的元素,只会被创建一次,之后会在每次渲染时被不停的复用
<div id="app">
    <h1>技术摸鱼</h1>
    <p>今天天气真不错</p>
    <div>{{name}}</div>
    <div :class="{red:isRed}">摸鱼符</div>
    <button @click="handleClick">戳我</button>
</div>


--------------------------------------


import { createVNode as _createVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createBlock as _createBlock } from "vue"

const _hoisted_1 = { id: "app" }
const _hoisted_2 = /*#__PURE__*/_createVNode("h1", null, "技术摸鱼", -1 /* HOISTED */)
const _hoisted_3 = /*#__PURE__*/_createVNode("p", null, "今天天气真不错", -1 /* HOISTED */)

export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createBlock("div", _hoisted_1, [
    _hoisted_2,
    _hoisted_3,
    _createVNode("div", null, _toDisplayString(_ctx.name), 1 /* TEXT */),
    _createVNode("div", {
      class: {red:_ctx.isRed}
    }, "摸鱼符", 2 /* CLASS */),
    _createVNode("button", { onClick: _ctx.handleClick }, "戳我", 8 /* PROPS */, ["onClick"])
  ]))
}

// Check the console for the AST

# 3、cacheHandlers 事件侦听器缓存

  • 默认情况下 onClick 会被视为动态绑定,所以每次都会去追踪它的变化
  • 但是因为是同一个函数,所以没有追踪变化,直接缓存起来复用即可
<div id="app">
    <button @click="handleClick">戳我</button>
</div>



----------------------------------------


import { createVNode as _createVNode, openBlock as _openBlock, createBlock as _createBlock } from "vue"

export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createBlock("div", { id: "app" }, [
    _createVNode("button", {
      onClick: _cache[1] || (_cache[1] = (...args) => (_ctx.handleClick(...args)))
    }, "戳我")
  ]))
}

// Check the console for the AST

# 4、SSR 渲染

  • 当有大量静态的内容的时候,这些内容会被当作纯字符串推进一个 buffer 里面;即使存在动态绑定,会通过模版值嵌入进去,这样会比通过虚拟 dom 来渲染快上很多很多
  • 当静态内容大到一定的量级时候,会用_createStaticVNode 方法在客户端去生成一个 static node,这些静态 node,会被直接 innerHtml,就不需要创建对象,然后根据对象渲染
import {
  createVNode as _createVNode,
  openBlock as _openBlock,
  createBlock as _createBlock,
} from "vue";

export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (
    _openBlock(),
    _createBlock("div", { id: "app" }, [
      _createVNode("h1", null, "技术摸鱼"),
      _createVNode("p", null, "今天天气真不错"),
    ])
  );
}

SSR------------------------------ - SSR;

import { mergeProps as _mergeProps } from "vue";
import {
  ssrResolveCssVars as _ssrResolveCssVars,
  ssrRenderAttrs as _ssrRenderAttrs,
} from "@vue/server-renderer";

export function ssrRender(
  _ctx,
  _push,
  _parent,
  _attrs,
  $props,
  $setup,
  $data,
  $options
) {
  const _cssVars = _ssrResolveCssVars({ color: _ctx.color });
  _push(
    `<div${_ssrRenderAttrs(
      _mergeProps({ id: "app" }, _attrs, _cssVars)
    )}><h1>技术摸鱼</h1><p>今天天气真不错</p></div>`
  );
}

// Check the console for the AST

Vue3+ Vue-CLI3+ 开发生态圈资讯

🚀 欢迎 Star ,后续会不断更新。
🇨🇳 最后更新日期:2️⃣0️⃣2️⃣0️⃣ / 0️⃣9️⃣ / 1️⃣9️⃣

【2020】 ≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡--------------- 【2021】

Twitter vue3: vue 3 will be available by the end of Q2.

9 月 18 日星期五,Vue 的作者尤雨溪发表主题演讲,正式发布 Vue3.0 并回应一些大家关切的问题。表示不建议大家立刻升级到 Vue3.0 版本,之前项目中某些依赖项可能还不支持新版,等社区完善后再进行迁移也不迟。

Vue3.0 正式发布,尤雨溪在线全球发布会:【戳这里

想了解更多有关 Vue 3.0 的信息,请大胆访问:v3.vuejs.org

除了单独 Vue3 资讯,欢迎查看更多 vue.js 资讯:【【🔥Vue.js 资讯 📚】目前 web 前端开发非常火爆的框架;定时更新,欢迎 Star 一下。

# 目录

🐣 关于旧版本

Vue CLI 的包名称由 vue-cli 改成了 @vue/cli。 如果你已经全局安装了旧版本的 vue-cli (1.x 或 2.x),你需要先通过 npm uninstall vue-cli -g 或 yarn global remove vue-cli 卸载它。

🐥Node 版本要求

Vue CLI 需要 Node.js 8.9 或更高版本 (推荐 8.11.0+)。你可以使用 nvm 或 nvm-windows 在同一台电脑中管理多个 Node 版本。

🐓Vue 3.0 源代码

当大多数国人还在庆祝国庆节的时候,尤雨溪大大在昨天凌晨发布了 Vue 3.0 源代码,源码地址:https://github.com/vuejs/vue-next 。虽然目前还 处于 Pre-Alpha 版本,但是可以预见后面的 Alpha、Beta 等版本应该不会太遥远。
之前,就有预言,除了性能优化、脚手架和新功能外,TypeScript 绝对是一个重点,因此,在 Vue 3.0 源代码版本中,98% 代码由 Typescript 编写,相信后面会是 100%。
通过本次发布的源代码可以了解到针对 Vue 3 计划并已实现的主要架构改进和新功能。
Vue 3 中最主要的新特性:组合式 API,已经可以借助 https://github.com/vuejs/composition-api (在 Vue 2 项目中作为插件使用)体验到。

yyx990803 yyx990803

v3.0.0 One Piece

v3.0.0 One Piece

Today we are proud to announce the official release of Vue.js 3.0 “One Piece”. This new major version of the framework provides improved performance, smaller bundle sizes, better TypeScript integration, new APIs for tackling large scale use cases, and a solid foundation for long-term future iterations of the framework.

One Piece. Vuejs 3.0 正式版发布!代号:海贼王


进入了 rc 版本阶段:

3.0.0-rc.12 (2020-09-16)

3.0.0-rc.1 (2020-07-17)

– Alpha (α):预览版,或者叫内部测试版;一般不向外部发布,会有很多 Bug;一般只有测试人员使用。
– Beta (β):测试版,或者叫公开测试版;这个阶段的版本会一直加入新的功能;在 Alpha 版之后推出。
– RC (Release Candidate):最终测试版本;可能成为最终产品的候选版本,如果未出现问题则可发布成为正式版本。

多数开源软件会推出两个 RC 版本,最后的 RC2 则成为正式版本。
我们的 vue3 就不一样,慢慢来,好东西,就要慢慢品!

3.0.0-beta.20 (2020-07-08)
@ github-actions released this 5 days ago · 37 commits to master since this release Please refer to CHANGELOG.md for details.

v3.0.0-beta.2
released this 6 days ago · 29 commits to master since this release
release: v3.0.0-beta.2

v3.0.0-beta.1
released this 7 days ago · 40 commits to master since this release
release: v3.0.0-beta.1

🔥🐔2020 前端面试秘籍

【吐血整理清单一】前端面试全攻略,为您保驾护航,金三银四
【吐血整理清单二】前端面试全攻略,为您保驾护航,金三银四
【吐血整理清单三】前端面试全攻略,为您保驾护航,金三银四

秘籍在手,天下我有; 只要你想进,那么世界就是你的。

🐔 前端整理之道

【整理】前端优化得有个好手段,比如看这个清单 🍑🍒🍓🍆🌽
【整理】前端学习笔记总结清单,应有尽有 🍇🍈🍉🍊🍋

🦃 关于 TypeScript

【2020-Q1-News】TypeScript 新鲜一波流,自己品尝?
【最新】TypeScript 梳理知识点列表,可否一战?
【必会】都已经 9102 年底了,你必须会 TypeScript。

为什么要学习它?
因为:

  • 按需输出 JavaScript 版本
  • 代码标准化利于团队开发
  • 主流框架及最新特性的支持
  • 便于重构和主流 IDE 支持
  • 更多友好特性和检测

哈哈哈,并不是,是因为都已经 2020 了,大公司和你们都在用。

# Vue3-CheatSheet

由 Vue Mastery 网站归纳的 Vue 3 Cheat Sheet, 直观地概况了其主要特性。

Vue3 Cheat Sheet Vue3 Cheat Sheet
img img

# vueuse

antfu/vueuse

like React hooks.
Collection of essential Vue Composition API utils works for Vue 2.x and 3.x https://vueuse.js.org/

Collection of essential Vue Composition API (inspired by react-use)

🚀 Features

  • ⚡ 0 dependencies: No worry about your bundle size
  • 🌴 Fully tree shakable: Only take what you want
  • 🦋 Type Strong: Written in Typescript
  • 🕶 Seamless migration: Works for both Vue 3 and 2
  • 🌎 Browser compatible: Use it though CDN
  • 🎪 Interactive docs & demos: Check out the Storybook!
  • 🔌 Optional Add-ons: Firebase, vue-i18n, etc

img

# 💃🏻 下半年撸 vue3 的姿势 💃🏻

🐯 🦁 🐮 🐷 🐹 🦊

霸气姿势观望 vue3 核心技术 ing

  • Proxy:不只是解决了 defineProperty 的局限性。
  • Performance:性能更比 Vue 2.0 强。
  • Tree shaking support:可以将无用模块 “剪辑”,仅打包需要的。
  • Composition API:组合 API。
  • Fragment, Teleport, Suspense:“碎片”,Teleport 即 Protal 传送门,“悬念”。
  • Better TypeScript support:更优秀的 Ts 支持。
  • Custom Renderer API:暴露了自定义渲染 API。

TODOLIST:

  • 1 Docs & Migration Guides
  • 2 Router
  • 3 Vuex
  • 4 CLI
  • 5 新工具:vite(法语 “快”)
  • 6 vue-test-utils
  • 7 DevTools
  • 8 IDE Support (Vetur)
  • 9 Nuxt

介绍 Vue.js 以及 Vue-next 源码分析文章,希望通过学习 Vue.js 源码获得更好的知识和收获。
【这是入口】你要找的 vue 源码 全宇宙的都在这!

v3.0.0 One Piece

「我是要成为海贼王的男人」

vue => Q3 2020

3.0: Release Management

  • Regression testing for 3.0

  • Automated nightly release

  • Formalize release lifecycle

  • setup CLA process

    3.0: IE11 compat build
    3.0 Official Release

    2.7

  • Backport compatible 3.x features to 2.x

  • Deprecation warnings for 3.x changes

  • This will be the last minor release for 2.x and be offered as LTS (long-term support) for 18 months. It will continue to receive critical security updates even after the LTS period.

Vue 3 Deep Dive with Evan You 【中英字幕】- Vue Mastery
课程中提到的预备课程是《Vue3 响应式原理》

里面评论区和弹幕有在调侃尤大大的:

=》尤雨溪就是个写前端的,懂什么 vue!
=》你一点都不懂 vue
=》他懂个锤子的 vue
=》他根本不懂 vue

=> 兄弟姐妹们,你们的饿了么升级为 vue3 了,在紧急构建中,很多大佬都在。
🎉 A Vue.js 3.0 UI Library element-plus.org/

从 Vue 2 到 Vue 3 的迁移指南

Vue 组合式 API

🚴🏻 🚴🏻‍♂️ 🚴🏻‍♀️ 🚵🏻 🚵🏻‍♂️ 🚵🏻‍♀️

vue3.0 Composition API 入门教程

vue3.0 Composition API 上手初体验 构建基本项目开发环境
vue3.0 Composition API 上手初体验 构建 vue 基础代码
vue3.0 Composition API 上手初体验 使用 vue-router 构建多页面应用
vue3.0 Composition API 上手初体验 神奇的 setup 函数 (一) 响应数据的绑定
vue3.0 Composition API 上手初体验 神奇的 setup 函数 (二) 响应对象数据的绑定
vue3.0 Composition API 上手初体验 神奇的 setup 函数 (三) 生命周期函数
vue3.0 Composition API 上手初体验 神奇的 setup 函数 (四) 计算属性 computed
vue3.0 Composition API 上手初体验 普通组件的开发与使用
vue3.0 Composition API 上手初体验 vue 组件的具名插槽 slot 的变化
vue3.0 Composition API 上手初体验 函数组件的开发与使用
vue3.0 Composition API 上手初体验 用路由循环,做个导航菜单

李金文 /vue-next 学习

vue-next 贡献指南(谷歌翻译版)
《Vue3.0 抢先学》系列之:网友们都惊呆了!
《Vue3.0 抢先学》系列之:一个简单的例子
《Vue3.0 抢先学》系列之:使用 Composition API
《Vue3.0 抢先学》系列之:响应式之 Ref vs. Reactive
《Vue3.0 抢先学》系列之:使用 render 函数
《Vue3.0 抢先学》vue-next 学习总结
《Vue3.0 抢先学》系列之:组件属性 Props
《Vue3.0 抢先学》系列之:组件生命周期
《Vue3.0 抢先学》系列之:更多响应式 API 示例(watch,computed 的变种)

What you will love in Vue 3
Prepare yourself for what to expect in Vue 3 with Alex Kyriakidis’ presentation from the Vue.js Amsterdam conference.

GitHub - vuejs/vite: Make Web Dev Fast Again
Vite is an opinionated web dev build tool that serves your code via native ES Module imports during dev and bundles it with Rollup for production.

Vue.js 3 Course - Composition API, TypeScript, Testing
Vue.js 3 introduces some changes to the Async Component API - find out the changes, and how you can use Async Components with Webpack 5’s bundle splitting to make your apps load faster than ever.

The case for HOC vs The Composition API
In this article Abdelrahman compares Higher-Order Components (using scoped-slots?content_source_url=https://github.com/vue3/vue3-News) with the upcoming Composition API. I especially enjoyed the Vee-Validate v4 comparison. Check it out!

Vuetensils 0.6: Simpler Forms, Better Accessibility, & Useful Filters!
The latest version of Vuetensils has some really cool features: improvements to form authoring, accessibility updates, and new filters to make life easier.

GitHub - vuejs/vitepress
GitHub - Akryum/vue-mention
GitHub - alvarosaburido/vue-dynamic-forms

# 再谈 vue3

# 尤大推出 vue3 beta 之后

🍁 🍄 🌾 💐 🌷 🌹 🥀 🌺 🌸 🌼 🌻
各位亲 (づ ̄ 3  ̄) づ ╭❤ ~,悠着点、悠着点。
🍁 🍄 🌾 💐 🌷 🌹 🥀 🌺 🌸 🌼 🌻

vue 官方提供的尝鲜库:https://github.com/vuejs/composition-api

# 迎接 Vue3.0 系列

👹 👺 💀 👻 👽 🤖

# 最新资讯 - 继续前进 ╰(°▽°)╯

# 英文资料

# 2019 年中旬

天王盖地虎,宝塔镇河妖

文章列表标题 介绍
I_am_a_placeholder_placeholder_placeholder nothing
为 vue3 学点 typescript, 解读高级类型 第一课,体验 typescript; 第二课,基础类型和入门高级类型;第三课,泛型;第四课,解读高级类型;第五课,什么是命名空间 (namespace);
Vue 3.0:更快、更小、让开发者更轻松 在 11 月 14 日 - 16 日于多伦多举办的 VueConf TO 2018 大会上,尤雨溪发表了名为 “Vue 3.0 Updates” 的主题演讲,对 Vue 3.0 的更新计划、方向进行了详细阐述。
Vue 3.0 前瞻,体验 Vue Function API 最近 Vue 官方公布了 Vue 3.0 最重要的 RFC:Function-based component API,并发布了兼容 Vue 2.0 版本的 plugin:vue-function-api,可用于提前体验 Vue 3.0 版本的 Function-based component API。笔者出于学习的目的,提前在项目中尝试了 vue-function-api。
Vue 3.0 之前你必须知道的 TypeScript 实战技巧 很多人对 TypeScript 的使用还停留在基本操作上,其实 TypeScript 的特性非常强大,我们利用好这些特性可以有效地提高代码质量、加速开发效率,今天就介绍 9 个非常实用的 TypeScript 技巧或者特性.
Vue CLI 3.x 与 2.x 的区别 cli3 新增模式概念,每个模式在项目中都有对应的配置文件,项目启动时,对应的文件就会加载,与环境变量不同,一个模式可以包括多个环境变量。
Vue 3.0 RFC API 的实现 Vue3.0 的 RFC 已经发布了几个月了,Vue 底层几乎没有变动,还是沿用原来响应式的。所以一直在思考能不能使用现在的版本,实现 RFC 中的 API,直到看到了 Vue Function API 这个库,这个库让开发者提前尝鲜到了 RFC 中的 API,当然作为 RFC,所以最终 3.0 的 API 还是未知的,以及底层的实现也还未知。
Vue.js 3:面向未来编程(function-based API) 如果你在使用 Vue.js,那么可能知道这个框架的第 3 版就要出来了(如果你是在本篇文章发布后的一段时间看到这段话的话,我希望我的说法还是中肯的 😉)。新版本目前正在积极开发中,所以可能要加入的特性都可以在官方的 RFC(request for comments)仓库中看到:github.com/vuejs/rfcs 。其中有一个特性 function-api,将会在很大程度上影响我们未来 Vue 项目的编写方式。
来自 Vue 3.0 的 Composition API 尝鲜 前段时间,Vue 官方释出了 Composition API RFC 的文档,我也在收到消息的第一时间上手尝鲜。虽然 Vue 3.0 尚未发布,但是其处于 RFC 阶段的 Composition API 已经可以通过插件 @vue/composition-api 进行体验了。接下来的内容我将以构建一个 TODO LIST 应用来体验 Composition API 的用法。
vue-cli 3.0 脚手架,从入门到放弃(二) 想写好代码,和用好一个工具是离不开的, 一般我们使用的工具有 HuilderX,sublimetext3,vscode,webstorm 等,个人目前使用的是 hb,和 vscode,可以根据个人爱好各自选择,但一定要熟练。
vue-cli 3.0 脚手架,从入门到放弃(三) 来总结下流程, 从我们 npm run serve 开始,打开 localhost:8080 端口,首先会加载 index.html。然后去 app.vue 里查找路由组件,这是会调动 router.js 里的配置,选择首先要打开哪个文件,找到我们的那个 path:’/' 的页面,浏览器显示。完成。

# 2019 年上旬

不要叫我达芬奇

2019.01-2019.07

# 2018 年预告

天寒之时必封初冬

# 1、Vue CLI 3 搭建 vue+vuex 最全分析

介绍 Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统。有三个组件: CLI:@vue/cli 全局安装的 npm 包,提供了终端里的 vue 命令(如:vue create 、vue serve 、vue ui 等命令) CLI 服务:@vue/cli-service 是一个开发环境依赖。

# 2、Vue.js 2 vs Vue.js 3 的实现

vue.js 核心团队已经讨论过将在 Vue3 实现的变化。虽然 API 不会改变,但是数据响应机制(译者注:对数据改变的监听和通知)发生了变化。这意味着什么呢,同时它对你意味着什么呢?

# 3、vue3.0 快速创建项目

vue3.0 使用了 cli3 快捷搭建的技巧和配搭

# 4、Vue.js 3.0 PPT(附部分中文翻译)

Evan You 刚刚发布了最新的 Vue 3 和他在 Vue Toronto 的演讲内容:

# 5、vue3.0 初体验有哪些实用新功能

关于项目创建,除了命令创建 3.x 还增加了图形化界面创建以及管理 vue 项目 在创建新项目时还可以混合选用多种集成

# 6、Vue 3.0 的生命周期

ue 3.0 的生命周期多了哪些东西和其中又改变了什么

# 7、Vue-cli 3.0 初体验

如今上 Vue 官网一看,脚手架都早已出 3.0 版了。唉,果然干这行一天不学习都感觉要落伍……

# 8、重磅!Vue CLI 3.0 正式发布,带来多项重大更新

近日,Vue 的作者尤雨溪在 Medium 上宣布正式发布 Vue CLI 3.0,它也将为很多开发者带来期待已久的新特性。

# 9、译文:Vue.js 3.0 开发计划

在上周的 Vue.js 伦敦会议上我简短地透露了下个版本的 Vue 的新特性。这篇文章讲深入地阐述

# 10、Vue 下个版本的计划。Vue CLI 2&3 下的项目优化实践:CDN + Gzip + Prerender

这些优化方案适用于 Vue CLI 2 和 Vue CLI 3 , 文章主要基于 Vue CLI 2 进行介绍,关于如何在 Vue CLI 3 中进行相关的 webpack 调整,我已经放在了 vue-cli3-optimization 这个仓库下,并配有详细的注释,且额外添加方便 Sass 使用的 loader,使用 Sass 时无需再在每个需要引入变量和 mixin 的地方,每次都很麻烦的 @import。下面将详细介绍这些优化方案的实践方式和效果。

# 11、Vue CLI 3 发布:精简配置,增加图形界面,还有这些新功能

不同的项目往往有不同的需求,所以 webpack 这种高可配的打包工具才会火起来,但同时也被骂太复杂了。所以很多人就想简化或者封装配置,例如 facebook/create-react-app 将所有的配置脚本封装为 NPM 软件包。

# 12、专访 Vue 作者尤雨溪:Vue CLI 3.0 重构的原因

上个月,Vue CLI 3.0 正式发布,为很多开发者带来期待已久的新特性,尤雨溪也说 Vue CLI 3.0 经历了重构,旨在尽可能减少现代前端工具在配置上的烦恼,并尽可能在工具链中加入最佳实践,让其成为 Vue 应用程序的默认实践。为了获取更多细节,InfoQ 采访了尤雨溪,以下是对采访内容的整理。

# 13、重磅!尤雨溪发布 Vue 3.0 开发路线

在上周的 Vue.js 伦敦大会上,尤雨溪简要介绍了 Vue 下一个主要版本要发布的内容,9 月 30 日,尤雨溪在 medium 个人博客上发布了 Vue 3.0 的开发路线,全文如下

# 卖艺不卖身

博学之,审问之,慎思之,明辨之,笃行之。

作者:蓝少 (@bluezhan) 版权声明:自由转载 - 非商用 - 非衍生 - 保持署名(创意共享 3.0 许可证

# License

Released under the MIT License.

# About

🎯Find the latest breaking √vue3 & vue-cli 3+ News.

vue3.github.io/vue3-news/

# Topics

vue3 vue-router vue-components weekly news vue vue-cli typescript vue-composition-api vue-mastery vite vue3-cheatsheet

# Resources

Readme

# License

MIT License

# Releases

No releases published

# Packages

No packages published

# Languages

Octotree

IMPORTANT: OCTOTREE 6.0

Login with GitHub

mac 实用工具

# macOS 图床客户端 uPic

uPic 介绍

uPic 采用 Swift 原生开发,通过调用各个服务商的 API 接口实现。体积小、速度快。支持多种图床:smms, 又拍云 USS, 七牛云 KODO, 阿里云 OSS, 腾讯云 COS, 百度云 BOS, 微博Github, 码云 Gitee, Amazon S3, Imgur, 自定义上传服务

支持多种常用的输出格式,并且提供上传前图片压缩功能 (支持 JPG、PNG)。等等一些小功能…

用法介绍:https://blog.svend.cc/upic/

下载方式:从 Github release 下载。
如果访问 Github 下载困难的,可以从 Gitee release 下载。

img

# parallels desktop 虚拟机破解版

百度网盘:链接: https://pan.baidu.com/s/1fq_q5VZCto92mhzT8qUtrQ 密码: dkia

Parallels Desktop 是一款运行在 Mac 电脑上的极为优秀的虚拟机软件,用户可以在 Mac OS X 下非常方便运行 Windows、Linux 等操作系统及应用,用户不必繁琐重复地重启电脑即可在 Win 与 Mac 之间切换甚至同时使用它们。

# 系统要求

# 硬件

  • 配置 Intel Core 2 Duo / Core i3 / Core i5 / Core i7 / Core i9 / Intel Core M / Xeon 处理器的 Mac 电脑
  • 最低 4GB 内存(16GB 更佳)
  • 引导卷(Macintosh HD)需要配置 500MB 的磁盘空间以用于安装 Parallels Desktop 应用程序
  • 虚拟机的额外磁盘空间(由安装的操作系统以及应用程序决定,例如,Windows 10 至少需要 16GB)
  • 推荐采用 SSD 驱动器以提升系统性能
  • 需要联网以用于产品激活和部分功能的使用 [1]

# 软件

  • macOS Catalina 10.15
  • macOS Mojave 10.14
  • macOS High Sierra 10.13
  • macOS Sierra 10.12
  • DirectX 11 最低满足 macOS Mojave 10.14,若满足 macOS 10.15 Catalina 则更佳 [1]

# SwitchHosts

SwitchHosts 是一个管理、快速切换 Hosts 小工具,开源软件,一键切换 Hosts 配置,非常实用,高效。
开发 Web 过程成,部署有多套环境,网址域名都相同,部署在不同的服务器上,有开发环境、测试环境、预发布环境、生产环境。经常要切换 Hosts 来访问,测试以及验证 bug,如果纯手工修改这会花掉不少时间,而且这个过程毫无乐趣可言。

39bQ8O

HCzyQU

# Mindmaster

破解版:链接: https://pan.baidu.com/s/1JRZrSSWJEUg0Smn2FpLKcA 密码: t239

MindMaster 思维导图 [1] 是深圳市亿图软件有限公司推出的一款跨平台思维导图软件。 [2] 软件提供了丰富的智能布局、多样性的展示模式、结合精美的设计元素和预置的主题样式,努力帮用户打造一款真正的效率工具。 [2]

gQGOkP

# Windows10 IOS

百度网盘下载:链接: https://pan.baidu.com/s/1pGba4uRJAAW0Bnn6foi_8A 密码: r79s

配合 parallels desktop 虚拟机使用

手撕代码

# 面试专题总结:手撕代码

希望读者依此构建自己的知识树(思维导图)

偷懒一下:可参考我自己总结思维导图 : 点这里

手撕代码地址:地址

附带:高频面试题积累文档。 来自于(学长、牛客网等平台)

自己开发的博客地址:zxinc520.com

github 地址: 点击

此篇 js - 【手撕代码】 知识点: 全部弄懂了,面试很容易。

# 1、Promise(A + 规范)、then、all 方法

/*
     Promise:构造 Promise 函数对象
     excutor: 执行构造器 (同步执行)
*/
function Promise(excutor) {
  const _that = this;
  _that.status = "pending"; // 给 promise对象指定 status属性,初始值为 pending
  _that.data = undefined; //给 promise 对象指定一个用于储存结果数据的属性
  _that.callbacks = []; // 每个元素的结构:{ onFulfilled(){}, onRejected(){}}

  function resolve(value) {
    // 如果当前状态不是 pending,直接结束
    if (_that.status !== "pending") return;

    // 将 状态改为 resolved
    _that.status = "resolved";

    // 保存 value 数据
    _that.data = value;

    // 如果有待执行callback 函数,立刻异步执行回调函数
    if (_that.callbacks.length > 0) {
      setTimeout(() => {
        _that.callbacks.forEach((callbacksObj) => {
          callbacksObj.onFulfilled(value);
        });
      });
    }
  }

  function reject(reason) {
    // 如果当前状态不是 pending,直接结束
    if (_that.status !== "pending") return;

    // 将 状态改为 rejected
    _that.status = "rejected";

    // 保存 value 数据
    _that.data = reason;

    // 如果有待执行callback 函数,立刻异步执行回调函数
    if (_that.callbacks.length > 0) {
      setTimeout(() => {
        _that.callbacks.forEach((callbacksObj) => {
          callbacksObj.onRejected(reason);
        });
      });
    }
  }

  //立刻同步执行 excutor
  try {
    excutor(resolve, reject);
  } catch (error) {
    //如果执行器抛出异常,promise对象变为 rejected 状态
    reject(error);
  }
}

/*
          Promise原型对象的 then() --- *思路*

            1、指定成功和失败的回调函数
            2、返回一个新的 promise 对象
            3、返回promise的结果由 onFulfilled/onRejected执行结果决定
            4、指定 onFulfilled/onRejected的默认值
         */
Promise.prototype.then = function (onFulfilled, onRejected) {
  onFulfilled =
    typeof onFulfilled === "function" ? onFulfilled : (reason) => reason; //向后传递成功的value

  //指定默认的失败的回调(实现错误/异常穿透的关键点)
  onRejected =
    typeof onRejected === "function"
      ? onRejected
      : (reason) => {
          //向后传递失败的reason
          throw reason;
        };

  const _that = this;

  //返回一个新的promise 对象
  return new Promise((resolve, reject) => {
    /*
                    调用指定的回调函数处理,根据执行结果,改变return的promise的状态
                 */
    function handle(callback) {
      /*
                           1. 如果抛出异常,return 的promise就会失败,reason 就是 error
                           2. 如果回调函数返回的不是promise,return的promise就会成功,value就是返回的值
                           3.如果回调函数返回的是promise,return的promise的结果就是这个promise的结果
                        */

      try {
        const result = callback(_that.data);

        // 3.如果回调函数返回的是promise,return的promise的结果就是这个promise的结果
        if (result instanceof Promise) {
          // result.then(
          //     value => resolve(value), //当result成功时,让return的promise也成功
          //     reason => reject(reason)  //当result失败时,让return的promise也失败
          // )

          result.then(resolve, reject);
        } else {
          //  2. 如果回调函数返回的不是promise,return的promise就会成功,value就是返回的值
          resolve(result);
        }
      } catch (error) {
        //1. 如果抛出异常,return 的promise就会失败,reason 就是 error

        reject(error);
      }
    }

    if (_that.status === "pending") {
      //假设当前状态还是 pending 状态,将回调函数 保存起来
      _that.callbacks.push({
        onFulfilled(value) {
          handle(onFulfilled); //改promise的状态为 onFulfilled状态
        },
        onRejected(reason) {
          handle(onRejected); //改promise的状态为 onRejected状态
        },
      });
    } else if (_that.status === "resolved") {
      //如果当前是resolved状态,异步执行onresolved并改变return的promise状态
      setTimeout(() => {
        handle(onFulfilled);
      });
    } else {
      //onRejected
      setTimeout(() => {
        //如果当前是rejected状态,异步执行onRejected并改变return的promise状态
        handle(onRejected);
      });
    }
  });
};

/*
            Promise原型对象的 catch()
            指定失败的回调函数
            返回一个新的 promise 对象
         */
Promise.prototype.catch = function (onRejected) {
  return this.then(undefined, onRejected);
};

Promise.prototype.finally = function (callback) {
  return this.then(
    (value) => {
      Promise.resolve(callback(value));
    },
    (reason) => {
      Promise.resolve(callback(reason));
    }
  );
};

/*
            Promise函数对象的 resolve()
            返回 指定结果的 "成功" 的 promise 对象
         */
Promise.resolve = function (value) {
  //返回 一个 成功/失败 的promise
  return new Promise((resolve, reject) => {
    if (value instanceof Promise) {
      //使用value的结果作为 promise 的结果
      value.then(resolve, reject);
    } else {
      //value不是promise => promise变为成功,数据是 value
      resolve(value);
    }
  });
};

/*
            Promise函数对象的 reject()
            返回 指定结果的 "失败" 的 promise 对象
         */
Promise.reject = function (reason) {
  //返回 一个失败的 promise
  return new Promise((resolve, reject) => {
    reject(reason);
  });
};

/*
            Promise函数对象的 all()
            返回 一个promise,只有当所有promise都成功时才成功,否则只要有一个失败就 失败
         */
Promise.all = function (promises) {
  const values = Array.apply(null, { length: promises.length }); //用来保存所有成功 value的数组
  let resolvedCount = 0;

  return new Promise((resolve, reject) => {
    //遍历获取每一个 promise的结果
    promises.forEach((p, index) => {
      Promise.resolve(p).then(
        //p成功,将成功的 value 保存 values
        // values.push(value)  => 不能这样
        (value) => {
          resolvedCount++; //成功的次数

          values[index] = value;

          //如果全部成功了,将return的 promise 改为成功
          if (resolvedCount === promises.length) {
            resolve(values);
          }
        },
        (reason) => {
          //只要一个失败了,return 的promise就失败
          reject(reason);
        }
      );
    });
  });
};

/*
            Promise函数对象的 race()
            返回 一个promise,其结果由第一个完成的promise来决定
         */
Promise.race = function (promises) {
  return new Promise((resolve, reject) => {
    //遍历获取每一个 promise的结果
    promises.forEach((p, index) => {
      Promise.resolve(p).then(
        (value) => {
          // 一旦由成功了,将return 变为失败
          resolve(value);
        },

        (reason) => {
          //只要一个失败了,return 的promise就失败
          reject(reason);
        }
      );
    });
  });
};

# 2、手写 call apply bind

// 自定义apply函数
Function.prototype.apply1 = function (obj, arg) {
  //context为null或者是undefined时,设置默认值
  if (!obj) {
    obj = typeof window === "undefined" ? global : window;
  }
  obj.fn = this;
  let result = null;
  //undefined 或者 是 null 不是 Iterator 对象,不能被 ...
  if (arg === undefined || arg === null) {
    result = obj.fn(arg);
  } else {
    result = obj.fn(...arg);
  }
  delete obj.fn;
  return result;
};

// 自定义 call 函数
Function.prototype.call1 = function (obj, ...arg) {
  if (!obj) {
    obj = typeof window === "undefined" ? global : window;
  }
  obj.fn = this;
  let result = null;
  result = obj.fn(...arg);
  delete obj.fn;
  return result;
};

// 自定义 bind 函数
Function.prototype.bind = function (obj, ...arg) {
  if (!obj) {
    obj = typeof window === "undefined" ? global : window;
  }
  let self = this;
  let args = arg;

  function f() {}

  f.prototype = this.prototype;
  let bound = function () {
    let res = [...args, ...arguments];
    let obj = this instanceof f ? this : obj;
    return self.apply(obj, res);
  };
  bound.prototype = new f();
  return bound;
};

# 3、Promise 封装 Ajax 方法

function ajax(url, methods, data) {
  return new Promise((resolve, reject) => {
    let xhr = new XMLHttpRequest();
    xhr.open(methods, url, true);
    xhr.send(data);
    xhr.onreadystatechange = function () {
      if (xhr.readyState === 4 && xhr.status === 200) {
        resolve(xhr.responseText);
      } else {
        reject(xhr.status);
      }
    };
  });
}

# 4、异步加载图片

function loadImageAsync(url) {
  return new Promise(function (resolve, reject) {
    const image = new Image();
    image.onload = function () {
      resolve(image);
    };
    image.onerror = function () {
      reject(new Error("Could not load image at " + url));
    };
    image.src = url;
  });
}

# 5、防抖,节流

//防抖
function debounce(fn, delay) {
  let timeout = null;
  return function () {
    clearTimeout(timeout);
    timeout = setTimeout(() => {
      fn.apply(this, arguments);
    }, delay);
  };
}

//节流
function throttle(fn, delay) {
  let canRun = true;
  return function () {
    if (!canRun) return;
    canRun = false;
    setTimeout(() => {
      fn.apply(this, arguments);
      canRun = true;
    }, delay);
  };
}

# 6、圣杯、双飞翼

# 圣杯

<style>
        *{
            padding: 0;
            margin: 0;
        }
        .container{
            overflow: hidden;
            padding: 0 100px 0 100px;

        }

        .middle,.left,.right{
            position: relative;
            float: left;
        }

        .left{
            width: 100px;
            height: 100px;
            background: red;
            margin-left: -100%;
            left: -100px;
        }

        .right{
            width: 100px;
            height: 100px;
            background: green;
            margin-left: -100px;
            right: -100px;

        }
        .middle{
            background: blue;
            width: 100%;
            height: 300px;
        }
    </style>
</head>
<body>
<div class="container">
    <div class="middle"></div>
    <div class="left"></div>
    <div class="right"></div>
</div>

# 双飞翼

<style>
        .container {
            overflow: hidden;
        }
        .middle, .left, .right {
            float: left;
            height: 100px;
        }
        .left {
            width: 100px;
            background: red;
            margin-left: -100%;
        }
        .right {
            width: 100px;
            background: blue;
            margin-left: -100px;
        }
        .middle {
            width: 100%;
            background: aqua;
        }
        .inner {
            margin: 0 100px;
        }
    </style>
</head>
<body>
<div class="container">
    <div class="middle">
        <div class="inner">middle</div>
    </div>
    <div class="left">left</div>
    <div class="right">right</div>
</div>

# 7、继承相关

# 7.1、原型链继承

  • 原型链继承的基本思想:是利用原型让一个引用类型继承另一个引用类型的属性和方法。

    如 SubType.prototype = new SuperType ();

    function SuperType() {
      this.name = "Yvette";
    }
    function SubType() {
      this.age = 22;
    }
    SubType.prototype = new SuperType();
  • 缺点

    1. 通过原型来实现继承时,原型会变成另一个类型的实例,原先的实例属性变成了现在的原型属性,该原型的引用类型属性会被所有的实例共享
    2. 在创建子类型的实例时,不能向超类型的构造函数中传递参数

# 7.2、借用构造函数

  • 其基本思想为:在子类型的构造函数中调用超类型构造函数。

    function SuperType(name) {
      this.name = name;
    }
    function SubType(name) {
      SuperType.call(this, name);
    }
  • 优点

    1. 可以向超类传递参数
    2. 解决了原型中包含引用类型值被所有实例共享的问题
  • 缺点

    1. 方法都在构造函数中定义,函数复用无从谈起
    2. 另外超类型原型中定义的方法对于子类型而言都是不可见的

# 7.3、组合继承

  • 组合继承指的是将原型链和借用构造函数技术组合到一块,从而发挥二者之长的一种继承模式。基本思路:使用原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承,既通过在原型上定义方法来实现了函数复用,又保证了每个实例都有自己的属性。

    function SuperType() {
      this.name = "zc";
      this.colors = ["pink", "blue", "green"];
    }
    function SubType() {
      SuperType.call(this);
    }
    SubType.prototype = new SuperType();
    SubType.prototype.constructor = SubType;
    let a = new SubType();
    let b = new SubType();
    
    a.colors.push("red");
    console.log(a.colors); //[ 'pink', 'blue', 'green', 'red' ]
    console.log(b.colors); //[ 'pink', 'blue', 'green' ]
  • 优点

    1. 可以向超类传递参数
    2. 每个实例都有自己的属性
    3. 实现了函数复用
  • 缺点

    1. 无论什么情况下,都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部。

# 7.4、原型式继承

  • 原型式继承继承的基本思想:在 object () 函数内部,先创建一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回了这个临时类型的一个新实例,从本质上讲,object () 对传入的对象执行了一次浅拷贝。

    ECMAScript5 通过新增 Object.create () 方法规范了原型式继承。这个方法接收两个参数:一个用作新对象原型的对象和(可选的)一个为新对象定义额外属性的对象 (可以覆盖原型对象上的同名属性),在传入一个参数的情况下,Object.create () 和 object () 方法的行为相同。

    function object(o) {
      function F() {}
      F.prototype = o;
      return new F();
    }
  • 缺点

    1. 同原型链实现继承一样,包含引用类型值的属性会被所有实例共享

# 7.5、寄生式继承

  • 寄生式继承是与原型式继承紧密相关的一种思路。寄生式继承的思路与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数在内部已某种方式来增强对象,最后再像真地是它做了所有工作一样返回对象。

    function object(o) {
      function F() {}
      F.prototype = o;
      return new F();
    }
    function createAnother(original) {
      var clone = object(original); //通过调用函数创建一个新对象
      clone.sayHi = function () {
        //以某种方式增强这个对象
        console.log("hi");
      };
      return clone; //返回这个对象
    }
  • 优点

    1. 基于 person 返回了一个新对象 -—— person2,新对象不仅具有 person 的所有属性和方法,而且还有自己的 sayHi () 方法。在考虑对象而不是自定义类型和构造函数的情况下,寄生式继承也是一种有用的模式。
  • 缺点

    1. 使用寄生式继承来为对象添加函数,会由于不能做到函数复用而效率低下。
    2. 同原型链实现继承一样,包含引用类型值的属性会被所有实例共享。

# 7.6、寄生组合式继承

  • 所谓寄生组合式继承,即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法,基本思路:

    不必为了指定子类型的原型而调用超类型的构造函数,我们需要的仅是超类型原型的一个副本,本质上就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。

    function inheritPrototype(subType, superType) {
      var prototype = object(superType.prototype); //创建对象
      prototype.constructor = subType; //增强对象
      subType.prototype = prototype; //指定对象
    }
    function SuperType(name) {
      this.name = name;
      this.colors = ["pink", "blue", "green"];
    }
    function SuberType(name, age) {
      SuperType.call(this, name);
      this.age = age;
    }
    inheritPrototype(SuberType, SuperType);
  • 步骤

    第一步:创建超类型原型的一个副本

    第二步:为创建的副本添加 constructor 属性

    第三步:将新创建的对象赋值给子类型的原型

  • 优点

    1. 只调用了一次超类构造函数,效率更高。避免在 SuberType.prototype 上面创建不必要的、多余的属性,与其同时,原型链还能保持不变。因此寄生组合继承是引用类型最理性的继承范式。

# 7.7、ES6 继承

  • Class 可以通过 extends 关键字实现继承

    class SuperType {
      constructor(age) {
        this.age = age;
      }
      getAge() {
        console.log(this.age);
      }
    }
    class SubType extends SuperType {
      constructor(age, name) {
        super(age); // 调用父类的constructor(x, y)
        this.name = name;
      }
      getName() {
        console.log(this.name);
      }
    }
  • 对于 ES6 的 class 需要做以下几点说明

    1. class 声明会提升,但不会初始化赋值。Foo 进入暂时性死区,类似于 let、const 声明变量。
    2. class 声明内部会启用严格模式。
    3. class 的所有方法(包括静态方法和实例方法)都是不可枚举的。
    4. class 的所有方法(包括静态方法和实例方法)都没有原型对象 prototype,所以也没有 [[construct]],不能使用 new 来调用。
    5. 必须使用 new 调用 class
    6. class 内部无法重写类名

# 使用 extends 关键字实现继承,有几点需要特别说明

  • 子类必须在 constructor 中调用 super 方法,否则新建实例时会报错。如果没有子类没有定义 constructor 方法,那么这个方法会被默认添加。在子类的构造函数中,只有调用 super 之后,才能使用 this 关键字,否则报错。这是因为子类实例的构建,基于父类实例,只有 super 方法才能调用父类实例。
  • ES5 的继承,实质是先创造子类的实例对象 this,然后再将父类的方法添加到 this 上面(Parent.apply (this))。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到 this 上面(所以必须先调用 super 方法),然后再用子类的构造函数修改 this

# 8、自定义 new 过程

function _new(fn, ...arg) {
  let obj = {};
  obj.__proto__ = fn.prototype;
  let ret = fn.apply(obj, arg);
  return ret instanceof Object ? ret : obj;
}

# 9、手写递归方法实现深拷贝

// 手写实现深拷贝
function checkedType(target) {
  return Object.prototype.toString.call(target).slice(8, -1);
}

function clone(target) {
  let targrtType = checkedType(target);
  let result = null;
  if (targrtType === "Object") {
    result = {};
  } else if (targrtType === "Array") {
    result = [];
  } else {
    return target;
  }
  for (let item in target) {
    let value = target[item];
    if (checkedType(value) === "Object" || checkedType(value) === "Array") {
      result[item] = clone(value);
    } else {
      result[item] = value;
    }
  }
  return result;
}

# 10、实现一个柯里化函数

//ES5写法
const currying = function (fn, ...args) {
  if (args.length < fn.length) {
    return function () {
      return currying(fn, ...args, ...arguments);
    };
  } else {
    return fn(...args);
  }
};

//ES6写法
const currying = (fn, ...args) =>
  args.length < fn.length
    ? (...argments) => currying(fn, ...args, ...argments)
    : fn(...args);

# 11、双向绑定(手写)

// Object.defineProperty 写法
let vm = {};
let obj = {
  name: "zc",
  age: "123",
};

for (let key in obj) {
  if (obj.hasOwnProperty(key)) {
    Object.defineProperty(vm, key, {
      get: function () {
        return obj[key];
      },
      set: function (newvalue) {
        obj[key] = newvalue;
      },
    });
  }
}

obj.age = "111";
vm.age = "112221";

console.log(vm.age);
console.log(obj.age);
// Proxy 写法
let vm = new Proxy(obj, {
  get: function (target, propKey, receiver) {
    console.log(`getting ${propKey}!`);
    return Reflect.get(target, propKey, receiver);
  },
  set: function (target, propKey, value, receiver) {
    console.log(`setting ${propKey}!`);
    return Reflect.set(target, propKey, value, receiver);
  },
});

# 10、JS 发布订阅模式

let pubSub = {
  list: {},
  subscribe: function (key, fn) {
    //订阅
    if (!this.list[key]) {
      this.list[key] = [];
    }
    this.list[key].push(fn);
  },
  publish: function (key, ...args) {
    //发布
    for (let fn of this.list[key]) {
      fn.apply(this, args);
    }
  },
  unSubscribe: function (key, fn) {
    //取消订阅
    let fnlist = this.list[key];
    if (!fnlist) return;
    if (!fn) {
      fnlist.length = 0;
    } else {
      fnlist.forEach((item, index) => {
        if (item === index) {
          fnlist.splice(index, 1);
        }
      });
    }
  },
};

pubSub.subscribe("onwork", (time) => {
  console.log(`上班了:${time}`);
});
pubSub.subscribe("offwork", (time) => {
  console.log(`下班了:${time}`);
});
pubSub.subscribe("launch", (time) => {
  console.log(`吃饭了:${time}`);
});

// // 发布
pubSub.publish("offwork", "18:00:00");
pubSub.publish("launch", "12:00:00");

pubSub.unSubscribe("onwork");
pubSub.publish("onwork", "1222:00:00");

# 11、JS 获取 url 参数

let test =
  "?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=21331&rsv_pq=b8627e62001efbb9&rsv_t=eef5sqIQ98s66yOwueYH5BWlFUARj0PkHBdCA4ahbSVYQA5qO9MBoZPC0mU&rqlang=cn&rsv_enter=1&rsv_dl=tb&rsv_sug3=5&rsv_sug1=1&rsv_sug7=100&rsv_sug2=0&inputT=509&rsv_sug4=509";

function f(str) {
  let str1 = str.slice(1);
  let arr = str1.split("&");
  console.log(arr);
  let map = new Map();

  arr.map((item) => {
    const [key, value] = item.split("=");
    map.set(key, decodeURIComponent(value));
  });

  return map;
}

for (let item of f(test)) {
  console.log(item);
}

# 12、二叉树

//1、求二叉树中的节点个数
function getNodenum(root) {
  if (root == null) {
    return;
  }
  return getNodenum(root.left) + getNodenum(root.right) + 1;
}

//2、求二叉树的最大深度
function maxDepth(root) {
  if (root == null) {
    return;
  }
  return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
}

//3.二叉树的最小深度
function minDepth(root) {
  if (root == null) return;
  let left = minDepth(root.left);
  let right = minDepth(root.right);
  return left == 0 || right == 0 ? left + right + 1 : Math.min(left, right) + 1;
}

//4.先序遍历(递归)
function preroot(root, callback) {
  if (root != null) {
    callback(root.key);
    preroot(root.left, callback);
    preroot(root.right, callback);
  }
}

//先序遍历(非递归)
function preroot(root) {
  let stack = [],
    result = [];
  if (root != null) {
    stack.push(root);
  }
  while (stack.length != 0) {
    let temp = stack.pop();
    result.push(temp.key);

    if (temp.left != null) {
      stack.push(temp.left);
    }
    if (temp.right != null) {
      stack.push(temp.right);
    }
  }
  return result;
}

//5 中序遍历(递归)
function middleroot(root, callback) {
  if (root != null) {
    preroot(root.left, callback);
    callback(root.key);
    preroot(root.right, callback);
  }
}

//5.1 中序遍历(非递归)
function middleroot(root) {
  let stack = [],
    result = [];
  while (true) {
    while (root != null) {
      stack.push(root);
      root = root.left;
    }
    if (stack.length == 0) {
      break;
    }
    let temp = stack.pop();
    result.push(temp.key);
    stack.push(temp.right);
  }
  return result;
}

//分层遍历(递归)
function bfs(root) {
  let queue = [],
    result = [];
  let pointer = 0;
  if (root != null) {
    queue.push(root);
  }
  let bfsFun = function () {
    let temp = queue[pointer];
    if (temp) {
      result.push(temp.key);
      if (temp.left) {
        queue.push(temp.left);
      }
      if (temp.right) {
        queue.push(temp.right);
      }
      pointer++;
      bfsFun();
    }
  };
  bfsFun();
  return result;
}

//分层遍历(非递归)
function bfs(root) {
  let queue = [],
    result = [];
  if (root !== null) {
    queue.push(root);
  }
  let pointer = 0;
  while (pointer < queue.length) {
    let temp = queue[pointer++];
    result.push(temp.key);
    temp.left && queue.push(temp.left);
    temp.right && queue.push(temp.right);
  }
  return result;
}

// 按之字形顺序打印二叉树
function zhiRoot(root) {
  let stack1 = [],
    stack2 = [],
    result = [];

  if (root != null) {
    stack1.push(root);
  }
  let flag = 1;
  while (stack1.length != 0 || stack2.length != 0) {
    const list = [];
    if (flag % 2) {
      while (stack1.length != 0) {
        let temp = stack2.pop();
        list.push(temp.key);
        temp.left && stack2.push(temp.left);
        temp.right && stack2.push(temp.right);
      }
    } else {
      while (stack2.length != 0) {
        let temp = stack1.pop();
        list.push(temp.key);
        temp.left && stack1.push(temp.left);
        temp.right && stack1.push(temp.right);
      }
    }
    i++;
    result.push(list);
  }
  return result;
}

function Print(root) {
  const result = [];

  if (root === null) {
    return result;
  }

  const stack1 = [];
  const stack2 = [];

  stack1.push(root);
  let flag = 1;
  while (stack1.length !== 0 || stack2.length !== 0) {
    const list = [];
    if (flag % 2) {
      while (stack1.length !== 0) {
        const temp = stack2.pop();
        list.push(temp.val);
        temp.left && stack2.push(temp.left);
        temp.right && stack2.push(temp.right);
      }
    } else {
      while (stack2.length !== 0) {
        const temp = stack1.pop();
        list.push(tmp.val);
        temp.left && stack1.push(temp.left);
        temp.right && stack1.push(temp.right);
      }
    }
    i++;
    result.push(list);
  }
  return result;
}

//7、求二叉树第K层的节点个数
function getknum(root, k) {
  if (root == null || k < 0) {
    return;
  }
  if (root !== null && k == 1) {
    return 1;
  }
  return getknum(root.left, k - 1) + getknum(root.right, k - 1);
}

//8.求二叉树第K层的叶子节点个数
function getksonnum(root, k) {
  if (root == null || k < 0) {
    return;
  }
  if (root != null && k == 1) {
    if (root.left == null && root.right == null) {
      return 1;
    } else {
      return 0;
    }
  }
  return getksonnum(root, k - 1) + getksonnum(root, k - 1);
}

//反转二叉树
function reverseRoot(root) {
  if (root == null) {
    return;
  }
  let temp = root.left;
  root.left = reverseRoot(root.right);
  root.right = reverseRoot(temp);
  return root;
}

// 求二叉树的直径
function longerlength(root) {
  let path = 0;
  getlongerlength(root);
  return path;

  function getlongerlength(root) {
    if (root == null) {
      return;
    }
    let left = longerlength(root.left);
    let right = longerlength(root.right);
    path = Math.max(path, left + right);
    return Math.max(left, right) + 1;
  }
}

// 二叉树中和为某一值的路径
function getPath(root, target) {
  let result = [];
  if (root) {
    findPath(root, target, [], 0, result);
  }
  return result;

  function findPath(root, target, stack, sum, result) {
    stack.push(root.key);
    sum += root.key;
    if (!root.left && !root.right && sum === target) {
      result.push(stack.slice(0));
    }
    if (root.left) {
      findPath(root.left, target, stack, sum, result);
    }
    if (root.right) {
      findPath(root.right, target, stack, sum, result);
    }
    stack.pop();
  }
}

//给定一棵二叉搜索树,请找出其中的第k小的结点。(中序遍历+ k小)

# 13、实现一个链表

function linkedList() {
  function node(data) {
    this.data = data;
    this.next = null;
  }

  this.head = null;
  this.length = 0;

  linkedList.prototype.append = function (data) {
    let newnode = new node(data);
    if (this.head == null) {
      this.head = newnode;
    } else {
      let current = this.head;
      while (current.next) {
        current = current.next;
      }
      current.next = newnode;
      this.length++;
    }
  };
  linkedList.prototype.find = function (data) {
    let current = this.head;
    while (current.next) {
      if (current.data === data) {
        break;
      }
      current = current.next;
    }
    return current;
  };

  linkedList.prototype.fixed = function (data, newdata) {
    let current = this.find(data);
    current.data = newdata;
  };

  linkedList.prototype.prefind = function (data) {
    let current = this.head;
    while (current.next) {
      if (current.next.data === data) {
        break;
      }
      current = current.next;
    }
    return current;
  };

  linkedList.prototype.delete = function (data) {
    if (this.head.data === data) {
      this.head = this.head.next;
      return;
    }
    let prenode = this.prefind(data);
    let current = this.find(data);
    prenode = current.next;
  };

  linkedList.prototype.toString = function () {
    let result = "";
    let current = this.head;
    while (current) {
      result += current.data + "->";
      current = current.next;
    }
    return result;
  };
}
let a = new linkedList();
a.append("abc");
a.append("abcd");
a.append("abcde");
console.log(a.toString());

a.fixed("abc", 11111);
console.log(a.toString());

# 14、哈希表

//链地址法
//装载因子(0.25,0.75)
function HashTable() {
  //属性
  this.storage = []; //存储的位置
  this.count = 0; // 数目
  this.limit = 7; //最终限制数组的大小

  //方法
  // 哈希函数
  HashTable.prototype.hashFunc = function (str, size) {
    //1、定义 hashCode变量
    let hashCode = 0;
    for (let i = 0; i < str.length; i++) {
      //2、霍纳算法,来计算hashCode的值
      hashCode = 37 * hashCode + str.charCodeAt(i);
    }
    //3、取余操作
    let index = hashCode % size;
    return index;
  };

  //插入&修改操作
  HashTable.prototype.put = function (key, value) {
    //1.根据key获取对应的 index
    let index = this.hashFunc(key, this.limit);
    // 2、根据 index 取出对应的 bucket
    let bucket = this.storage[index];

    //3、判断 bucket是否为空
    if (bucket == null) {
      bucket = [];
      this.storage[index] = bucket;
    }
    //4、判断是否是修改数据
    for (let i = 0; i < bucket.length; i++) {
      let tuple = bucket[i];
      if (tuple[0] == key) {
        tuple[1] = value;
        return;
      }
    }
    //5.添加操作
    bucket.push([key, value]);
    this.count++;

    //判断是否需要扩容
    if (this.count > this.limit * 0.75) {
      this.resize(this.limit * 2);
    }
  };

  //获取操作
  HashTable.prototype.get = function (key) {
    let index = this.hashFunc(key, this.limit);
    let bucket = this.storage[index];
    if (bucket == null) {
      return false;
    }
    for (let i = 0; i < bucket.length; i++) {
      let tuple = bucket[i];
      if (tuple[0] === key) {
        return tuple[1];
      }
    }
  };
  HashTable.prototype.remove = function (key) {
    let index = this.hashFunc(key, this.limit);
    let bucket = this.storage[index];
    if (bucket == null) {
      return null;
    }
    for (let i = 0; i < bucket.length; i++) {
      let tuple = bucket[i];
      if (tuple[0] == key) {
        bucket.splice(i, 1);
        this.count--;

        //缩小容量
        if (this.limit > 7 && this.count < this.limit * 0.75) {
          this.resize(Math.floor(this.limit / 2));
        }

        return tuple[1];
      }
    }
    return null;
  };

  //哈希表的扩容、
  HashTable.prototype.resize = function (newLimit) {
    //1.保存旧的数据内容
    let oldStorage = this.storage;
    //2. 重置所有的属性

    this.storage = [];
    this.count = 0;
    this.limit = newLimit;

    //3.遍历 oldStorage 所有的 bucket
    for (let i = 0; i < oldStorage.length; i++) {
      let bucket = oldStorage[i];
      if (bucket == null) {
        continue;
      }
      for (let j = 0; j < bucket.length; j++) {
        let tuple = bucket[i];
        this.put(tuple[0], tuple[1]);
      }
    }
  };
}
let a = new HashTable();
a.put("zc", "15");
a.put("zc1", "115");
a.put("z1", "115");
a.put("asd", "115");
a.put("wew", "115");
a.remove("wew");
console.log(a.get("wew"));

# 15、图

function Queue() {
  //栈中的属性
  this.items = [];

  //1.压入栈push()
  Queue.prototype.enqueue = function (...element) {
    this.items.push(...element);
  };

  //2.从队列中删除前端元素
  Queue.prototype.dequeue = function () {
    return this.items.shift();
  };

  //3.查看一下前端元素
  Queue.prototype.front = function () {
    return this.items[0];
  };

  //4.判断栈是否为空
  Queue.prototype.isEmpty = function () {
    return this.items.length === 0;
  };
  //5.获取栈中元素的个数
  Queue.prototype.size = function () {
    return this.items.length;
  };
  //6.toString方法
  Queue.prototype.toString = function () {
    return this.items.toString().split(",").join(" ");
  };
}

function Graph() {
  //属性: 顶点(数组)/边(字典)
  this.vertexes = []; //顶点
  this.edges = new Map(); //边

  //方法
  //增加对应顶点的方法
  Graph.prototype.addVertex = function (v) {
    this.vertexes.push(v);
    this.edges.set(v, []);
  };

  Graph.prototype.addEdge = function (v1, v2) {
    this.edges.get(v1).push(v2);
    this.edges.get(v2).push(v1);
  };

  //实现toString 方法
  Graph.prototype.toString = function () {
    //定义字符转,保存最终的结构
    let resultString = "";
    for (let i = 0; i < this.vertexes.length; i++) {
      resultString += this.vertexes[i] + "->";
      let vEdges = this.edges.get(this.vertexes[i]);
      for (let j = 0; j < vEdges.length; j++) {
        resultString += vEdges[j] + " ";
      }
      resultString += "\n";
    }
    return resultString;
  };

  //图的遍历

  //初始化状态颜色
  Graph.prototype.initializeColor = function () {
    let colors = [];
    for (let i = 0; i < this.vertexes.length; i++) {
      colors[this.vertexes[i]] = "white";
    }
    return colors;
  };

  //广度优先搜索算法(BFS)  基于队列完成
  Graph.prototype.bfs = function (initV, handler) {
    //1.初始化颜色
    let colors = this.initializeColor();

    //2.创建队列
    let queue = new Queue();

    //3.将顶点加入队列中
    queue.enqueue(initV);

    //4.循环从队列中取出元素
    while (!queue.isEmpty()) {
      // 4.1从队列取出一个顶点
      let v = queue.dequeue();

      //4.2 获取和顶点相连的另外顶点
      let vList = this.edges.get(v);

      //4.3 将v的颜色设置为灰色
      colors[v] = "gray";

      //4.4  遍历所有的顶点,并且加入到队列中
      for (let i = 0; i < vList.length; i++) {
        let e = vList[i];
        if (colors[e] == "white") {
          colors[e] = "gray";
          queue.enqueue(e);
        }
      }

      //4.5 访问顶点
      handler(v);

      //4.6 将顶点设置为黑色
      colors[v] = "black";
    }
  };

  //广度优先搜索算法(DFS)
  Graph.prototype.dfs = function (initV, handler) {
    let colors = this.initializeColor();

    //递归访问
    this.dfsVisit(initV, colors, handler);
  };

  Graph.prototype.dfsVisit = function (v, colors, handler) {
    //1.将颜色设置为灰色
    colors[v] = "gray";
    //2.处理V节点
    handler(v);

    //3.访问v相连的顶点
    let vList = this.edges.get(v);
    for (let i = 0; i < vList.length; i++) {
      let e = vList[i];
      if (colors[e] === "white") {
        this.dfsVisit(e, colors, handler);
      }
    }

    //4.将v设置为黑色
    colors[v] = "black";
  };
}

# 16、几种排序算法的实现

function ArrayList() {
  this.array = [];

  ArrayList.prototype.insert = function (item) {
    this.array.push(item);
  };

  ArrayList.prototype.toString = function () {
    return this.array.join("-");
  };

  ArrayList.prototype.swap = function (m, n) {
    let temp = this.array[m];
    this.array[m] = this.array[n];
    this.array[n] = temp;
  };
  //实现排序算法
  //冒泡排序
  ArrayList.prototype.bubbles = function () {
    if (this.array === null || this.array.length < 2) return this.array;
    let length = this.array.length;
    for (let i = length - 1; i >= 0; i--) {
      for (let j = 0; j < i; j++) {
        if (this.array[j] > this.array[j + 1]) {
          this.swap(j, j + 1);
        }
      }
    }
  };

  //选择排序
  ArrayList.prototype.selectSort = function () {
    if (this.array === null || this.array.length < 2) return this.array;
    let length = this.array.length;
    for (let i = 0; i < length - 1; i++) {
      let min = i;
      for (let j = i + 1; j < length; j++) {
        if (this.array[min] > this.array[j]) {
          min = j;
        }
      }
      this.swap(min, i);
    }
  };

  //插入排序
  ArrayList.prototype.insertSort = function () {
    if (this.array === null || this.array.length < 2) return this.array;
    let length = this.array.length;

    for (let i = 1; i < length; i++) {
      var temp = this.array[i];
      let j = i;
      while (this.array[j - 1] > temp && j > 0) {
        this.array[j] = this.array[j - 1];
        j--;
      }
      this.array[j] = temp;
    }
  };

  //高级排序
  //希尔排序 (对插入排序的升级)
  ArrayList.prototype.shellSort = function () {
    if (this.array === null || this.array.length < 2) return this.array;
    let length = this.array.length;
    //初始化增量
    var gap = Math.floor(length / 2);
    // whlie循环
    while (gap > 1) {
      for (let i = gap; i < length; i++) {
        let temp = this.array[i];
        let j = i;
        while (this.array[j - gap] > temp && j > gap - 1) {
          this.array[j] = this.array[j - gap];
          j -= gap;
        }
        this.array[j] = temp;
      }
      gap = Math.floor(gap / 2);
    }
  };
}
// 快排
function median(arr, left, right) {
  let center = Math.floor(left + (right - left) / 2);
  if (arr[left] > arr[center]) {
    swap(arr, left, right);
  }
  if (arr[center] > arr[right]) {
    swap(arr, center, right);
  }
  if (arr[left] > arr[right]) {
    swap(arr, left, right);
  }
  swap(center, right - 1);
  return arr[right - 1];
}
function swap(arr, m, n) {
  let temp = arr[m];
  arr[m] = arr[n];
  arr[n] = temp;
}
function quickSort(arr) {
  if (arr.length <= 1) {
    return arr;
  }
  return quickSortFun(arr, 0, arr.length - 1);
}
function quickSortFun(arr, left, right) {
  if (left < right) {
    let pivot = median(arr, left, right);
    let i = 0;
    let j = right - 1;
    while (true) {
      while (arr[++i] < pivot) {}
      while (arr[--j] > pivot && j > left) {}
      if (i < j) {
        swap(arr, i, j);
      } else {
        break;
      }
    }
    if (i < right) {
      swap(arr, i, right - 1);
    }
    quickSortFun(arr, left, i - 1);
    quickSortFun(arr, i + 1, right);
  }
  return arr;
}
console.log(quickSort([1, 4, 2, 3, 1]));

# 17、手写迭代器

var it = makeIterator(["a", "b"]);
console.log(it.next());
console.log(it.next());
console.log(it.next());

function makeIterator(array) {
  let nextindex = 0;
  return {
    next: function () {
      if (nextindex < array.length) {
        return { value: array[nextindex++], done: false };
      } else {
        return { value: undefined, done: true };
      }
    },
  };
}

# 18、最大连续子序列

let arr = [1, -5, 8, 3, -4, 15, -8];

// function getNum(arr) {
//     let length = arr.length
//     let maxmun=0
//     for (let i = 0; i <length ; i++) {
//         let sum=arr[i]
//         for (let j = i+1; j < length; j++) {
//             sum+=arr[j]
//             if(sum>maxmun){
//                 maxmun = sum
//             }
//
//         }
//     }
//     return maxmun
// }

function getNum(arr) {
  let max = 0;
  let sum = 0;
  for (let num of arr) {
    if (sum < 0) {
      sum = 0;
    }
    sum += num;
    max = Math.max(max, sum);
  }
  return max;
}

console.log(getNum(arr));

# 19、实现一个 EventListener 类,包含 on,off,emit 方法

//实现一个EventListener类,包含on,off,emit方法
class EventListener {
  constructor() {
    this.list = {};
  }

  on(key, fn) {
    if (!this.list[key]) {
      this.list[key] = [];
    }
    this.list[key].push(fn);
  }

  emit(key, ...args) {
    for (let fn of this.list[key]) {
      fn.apply(this, args);
    }
  }

  off(key, fn) {
    let fnlist = this.list[key];
    if (!fnlist) return;
    if (!fn) {
      fnlist.length = 0;
    } else {
      fnlist.forEach((item, index) => {
        if (item === fn) {
          fnlist.splice(index, 1);
        }
      });
    }
  }
}

let obj1 = new EventListener();

obj1.on("work", (value) => {
  console.log(`我是${value}`);
});

obj1.on("eat", (value) => {
  console.log(`我在${value}`);
});

obj1.emit("work", "zc");

obj1.off("eat");

obj1.emit("eat", "吃西瓜");

# 20、sleep 函数

用 promise 写一个 delay 函数

function sleep(time) {
  return new Promise((resolve, reject) => {
    setTimeout(resolve, time);
  });
}

sleep(1000).then((value) => {
  console.log("11111");
});

# 21、手写斐波那契

// 递归
function getnum(num) {
  if (num <= 1) return 1;
  return getnum(num - 1) + getnum(num - 2);
}
console.log(getnum(2));
// ----------------------------------

//动态规划
function getnum(n) {
  let temp = [];
  if (n == 1 || n == 2) {
    return 1;
  } else {
    temp[1] = 1;
    temp[2] = 2;
    for (let i = 3; i < n; i++) {
      temp[i] = temp[i - 1] + temp[i - 2];
    }
    return temp[i - 1];
  }
}

# 22、只包含’(’, ‘)’, ‘[’, ‘]’, ‘{’, ‘}’ 的字符串,判断是否有效。

var isValid = function (s) {
  var rightSymbols = [];
  for (var i = 0; i < s.length; i++) {
    if (s[i] == "(") {
      rightSymbols.push(")");
    } else if (s[i] == "{") {
      rightSymbols.push("}");
    } else if (s[i] == "[") {
      rightSymbols.push("]");
    } else if (rightSymbols.pop() != s[i]) {
      return false;
    }
  }
  return !rightSymbols.length;
};

# 23、数组中只出现一次的数字

let arr = [1, 2, 3, 4, 3, 2, 1];
const p = arr.reduce((a, b) => {
  return a ^ b;
});
console.log(p);

# 24、数组最大深度

let arr = [1, [1, [3], 2], 2, [1], 3, 4];
let count = 0;

function getDep(arr) {
  let p = false;
  p = arr.some((item) => {
    return item.length > 0;
  });

  if (p) {
    count++;
    getDep(arr.flat());
  } else {
    return count;
  }
}
console.log(getDep(arr));

# 25、递归数组扁平化

let arr = [1, 2, 3, 2, [2, 3], [2, [1], 2]];
function wrap() {
  let ret = [];
  return function flatten(arr) {
    for (let item of arr) {
      if (item.constructor === Array) {
        ret.concat(flatten(item));
      } else {
        ret.push(item);
      }
    }
    return ret;
  };
}
console.log(wrap()(arr));

# 26、模拟 js 精度丢失问题

IEEE 754 标准

function add(num1, num2) {
  const num1Digits = (num1.toString().split(".")[1] || "").length;
  const num2Digits = (num2.toString().split(".")[1] || "").length;
  const baseNum = Math.pow(10, Math.max(num1Digits, num2Digits));
  return (num1 * baseNum + num2 * baseNum) / baseNum;
}
console.log(add(0.1, 0.2));

# 27、单例模式

// 单例模式不透明
function singleTon(name) {
  this.name = name;
  this.instance = null;
}

singleTon.prototype.getName = function () {
  console.log(this.name);
};

singleTon.getInstance = function (name) {
  if (!this.instance) {
    this.instance = new singleTon(name);
  }
  return this.instance;
};

var b = singleTon.getInstance("bbbbb");
var a = singleTon.getInstance("a");

console.log(a);
console.log(b);

// ----------------------------------

// 单例模式不透明(闭包)
function singleTon(name) {
  this.name = name;
}

singleTon.prototype.getName = function () {
  console.log(this.name);
};

singleTon.getInstance = (function () {
  let instance = null;
  return function (name) {
    return instance || (instance = new singleTon(name));
  };
})();

var a = singleTon.getInstance("a");
var b = singleTon.getInstance("bbbbb");
var c = singleTon.getInstance("cccccc");

console.log(a);
console.log(b);
console.log(c);
//	单例模式透明
let getInstance = (function () {
  let instance = null;
  return function (name) {
    if (!instance) {
      this.name = name;
      return (instance = this);
    }
    return instance;
  };
})();

let a = new getInstance("aa");
let b = new getInstance("bbbb");
console.log(a);
console.log(b);
// let getSingle = function (fn) {
//     let result= null
//     return function () {
//        return result || (result = fn.call(this,...arguments))
//     }
// }

// 通用的单例验证方法
const getSingle = function (fn) {
  let result;
  return function () {
    return result || (result = fn.apply(this, arguments));
  };
};

function a(name) {
  this.name = name;
}

var b = getSingle(a);

var d = b("bbb", "xxx", "yyyyy");
var c = b("aaa");

console.log(d);
console.log(c);

# 28、策略模式

// 策略类(开发人员)
var Strategies = {
  backend: function (task) {
    console.log("进行后端任务:", task);
  },
  frontend: function (task) {
    console.log("进行前端任务:", task);
  },
  testend: function (task) {
    console.log("进行测试任务:", task);
  },
};
//  环境类(开发组长)
var Context = function (type, task) {
  typeof Strategies[type] === "function" && Strategies[type](task);
};

# 29、代理模式

//【图片预加载 -- 代理模式】

//定义本体
let myImg = (function () {
  var img = new Image();
  document.body.append(img);
  return {
    setsrc(src) {
      this.src = src;
    },
  };
})();

//代理函数
let Proxysetimg = (function () {
  var img = new Image();
  img.onload = function () {
    myImg.setsrc(this.src);
  };
  return {
    setsrc(src) {
      myImg.setsrc("./loading.gif");
      img.src = src;
    },
  };
})();
Proxysetimg("./111.png");

# 30、观察者模式

// 目标者类
class Subject {
  constructor() {
    this.observers = []; // 观察者列表
  }
  // 添加
  add(observer) {
    this.observers.push(observer);
  }

  // 删除
  remove(observer) {
    let idx = this.observers.findIndex((item) => item === observer);
    idx > -1 && this.observers.splice(idx, 1);
  }

  // 通知
  notify() {
    for (let observer of this.observers) {
      observer.update();
    }
  }
}

// 观察者类
class Observer {
  constructor(name) {
    this.name = name;
  }

  // 目标对象更新时触发的回调
  update() {
    console.log(`目标者通知我更新了,我是:${this.name}`);
  }
}

// 实例化目标者
let subject = new Subject();

// 实例化两个观察者
let obs1 = new Observer("前端开发者");
let obs2 = new Observer("后端开发者");

// 向目标者添加观察者
subject.add(obs1);
subject.add(obs2);

// 目标者通知更新
subject.notify();
// 输出:
// 目标者通知我更新了,我是前端开发者
// 目标者通知我更新了,我是后端开发者

# 31、命令模式

class Receiver {
  // 接收者类
  execute() {
    console.log("接收者执行请求");
  }
}

class Command {
  // 命令对象类
  constructor(receiver) {
    this.receiver = receiver;
  }
  execute() {
    // 调用接收者对应接口执行
    console.log("命令对象->接收者->对应接口执行");
    this.receiver.execute();
  }
}

class Invoker {
  // 发布者类
  constructor(command) {
    this.command = command;
  }
  invoke() {
    // 发布请求,调用命令对象
    console.log("发布者发布请求");
    this.command.execute();
  }
}

const warehouse = new Receiver(); // 仓库
const order = new Command(warehouse); // 订单
const client = new Invoker(order); // 客户
client.invoke();

# 32、Promise 处理文件读取

const fs = require("fs");
const path = require("path");

const readfile = function (filename) {
  return new Promise((resolve, reject) => {
    fs.readFile(path.join(__dirname, filename), "utf-8", function (
      error,
      data
    ) {
      if (error) return reject(error);
      resolve(data);
    });
  });
};

readfile("./01.txt")
  .then((value) => {
    console.log(value);
    return readfile("./02.txt");
  })
  .then((value) => {
    console.log(value);
    return readfile("./03.txt");
  })
  .then((value) => {
    console.log(value);
  })
  .catch((reason) => {
    console.log(reason);
  });

# 33、 Generator 函数文件读取

const fs = require("fs");
const path = require("path");

const readfile = function (filename) {
  return new Promise((resolve, reject) => {
    fs.readFile(path.join(__dirname, filename), "utf8", function (error, data) {
      if (error) return reject(error);
      resolve(data);
    });
  });
};
function* gen() {
  yield readfile("./01.txt");
  yield readfile("./02.txt");
  yield readfile("./03.txt");
}
const result = gen();

result
  .next()
  .value.then((value) => {
    console.log(value);
    return result.next().value;
  })
  .then((value) => {
    console.log(value);
    return result.next().value;
  })
  .then((value) => {
    console.log(value);
  })
  .catch((reason) => {
    console.log(reason);
  });

# 34、async 函数文件读取

const fs = require("fs");
const path = require("path");

const readfile = function (filename) {
  return new Promise((resolve, reject) => {
    fs.readFile(path.join(__dirname, filename), "utf8", function (error, data) {
      if (error) return reject(error);
      resolve(data);
    });
  });
};
async function gen() {
  try {
    const f1 = await readfile("./01.txt");
    const f2 = await readfile("./02.txt");
    const f3 = await readfile("./03.txt");
    console.log(f1);
    console.log(f2);
    console.log(f3);
  } catch (e) {
    console.log(e);
  }
}
gen();

API、数组、跨域、动画、事件

# 面试专题总结: API、数组、跨域、动画、事件

希望读者依此构建自己的知识树(思维导图)

偷懒一下:可参考我自己总结思维导图 : 点这里

附带:高频面试题积累文档。 来自于(学长、牛客网等平台)

自己开发的博客地址:zxinc520.com

github 地址: 点击

此篇 js - 【API、数组、跨域、动画、事件】 知识点: 全部弄懂了,面试很容易。

# 1、数组

  • 改变原数组的 API
    1. push()
    2. unshift()
    3. pop()
    4. shift()
    5. reverse()
    6. splice(index, count, value1, value2…)
      • 从索引位 index 处删除 count 个元素,插入 value1, value2 等元素,返回被删除的元素组成的新数组 (改变原数组)
    7. sort()
  • 不改变原数组的 API
    1. join(value)
      • 将数组用 value 连接为字符串,返回被连接后的字符串 (不改变原数组)
      • 将数组用 value 连接为字符串,返回被连接后的字符串 (不改变原数组)
    2. 获取子数组,包含原数组索引 start 的值到索引 end 的值,不包含 end,返回获取的子数组 (不改变原数组)
    3. toString()
      • 将数组中的元素用逗号拼接成字符串,返回拼接后的字符串 (不改变原数组)
    4. indexOf(value)
      • 从索引为 0 开始,检查数组中是否包含有 value,有则返回匹配到的第一个索引,没有则返回 - 1 (不改变原数组)
    5. lastIndexOf(value)
      • 从最后的索引开始,检查数组找那个是否包含 value,有则返回匹配到的第一个索引,没有返回 - 1 (不改变原数组)
    6. concat(value)
      • 将数组和 (或) 值连接成新数组,返回新数组 (不改变原数组)
    7. forEach()
      • 对数组进行遍历循环,对数组中每一项运行给定函数,参数都是 function 类型,默认有传参,参数分别为:遍历数组内容、对应的数组索引、数组本身。没有返回值
    8. map()
      • 指 “映射”,对数组中的每一项运行给定函数,返回每次函数调用的结果组成的新数组
    9. filter()
      • “过滤” 功能,数组中的每一项运行给定函数,返回满足过滤条件组成的数组
    10. every()
      • 判断数组中每一项都是否满足条件,只有所有项都满足条件,才会返回 true
    11. some()
      • 判断数组中是否存在满足条件的项,只要有一项满足条件,就会返回 true
    12. reduce()
      • 接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值

# 2、类数组转变为数组的方法

  • 类数组的定义
    • 可以通过索引访问元素,并且拥有 length 属性
    • 没有数组的其他方法,例如 push , forEach , indexOf 等。
  • ES5
    • Array.prototype.slice.call () 等同于 [].slice.call (arguments)
  • ES6
    • Array.from()
    • … 扩展运算符
    • for of 直接遍历类数组(iterator 接口)

# 3、稀疏数组和密集数组

  • 稀疏数组
    • 是什么
      • 具有不连续索引的数组,其 length 属性值大于元素的个数。
    • 造成稀疏数组的操作
      1. delete 操作符
      2. 构造函数
      3. 在数组字面量中省略值
      4. 指定数组索引大于数组长度
      5. 指定数组长度大于当前数组长度
    • 缺点
      • 操作的不统一
  • 密集数组
    • 是什么
      • 具有连续索引的数组,其 length 属性值等于元素的个数。
    • 创建方式
      1. Array.apply(null, Array(3)) || Array.apply(null, {length: 3})
      2. Array.from({length: 3})
      3. […Array(4)]

# 4、柯里化函数

  • 定义

    • 柯里化,即 Currying 的音译。 Currying 是编译原理层面实现多参函数的一个技术。
  • 手写柯里化函数

    • ES5 写法

      const currying = function (fn, ...args) {
        if (args.length < fn.length) {
          return function () {
            return currying(fn, ...args, ...arguments);
          };
        } else {
          return fn(...args);
        }
      };
    • ES6 写法(箭头函数)

      const currying = (fn, ...args) =>
        args.length < fn.length
          ? (...argments) => currying(fn, ...args, ...argments)
          : fn(...args);

# 5、window 全局对象(BOM)

  1. navigator 导航器对象
    • Navigator 对象包含有关浏览器的信息
    • appCodeName 返回浏览器的代码名
    • appName 返回浏览器的名称
    • appVersion 返回浏览器的平台和版本信息
    • cookieEnabled 返回指明浏览器中是否启用 cookie 的布尔值
    • platform 返回运行浏览器的操作系统平台
    • userAgent 返回由客户机发送服务器的 user-agent 头部的值
  2. screen 显示器对象
  3. history 历史对象
    • back () 返回前一个 URL
    • forward () 返回下一个 URL
    • go () 返回某个具体页面
  4. location 位置对象
    • 属性
      • hash 设置或返回从井号 (#) 开始的 URL(锚)。
      • host 设置或返回主机名和当前 URL 的端口号。
      • hostname 设置或返回当前 URL 的主机名
      • href 设置或返回完整的 URL
      • pathname 设置或返回当前 URL 的路径部分。
      • port 设置或返回当前 URL 的端口号。
      • protocol 设置或返回当前 URL 的协议。
      • search 设置或返回从问号 (?) 开始的 URL(查询部分)。
    • 方法
      • assign (URL) 加载新的文档
      • reload () 重新加载当前页面
      • replace (newURL) 用新的文档替换当前文档
  5. document 文档对象【DOM】

# 6、ajax 和 fetch

# 6.1、Ajax

  • 本质

    • 是在 HTTP 协议的基础上以异步的方式与服务器进行通信.
  • 封装原生 Ajax 请求

    function ajaxGet(url, callback) {
      var xhr = new XMLHttpRequest();
      xhr.open("GET", url, true);
      xhr.send();
    
      xhr.onreadystatechange = function () {
        if (xhr.readyState == 4 && xhr.status == 200) {
          callback(xhr.responseText);
        }
      };
    }

# 6.2、fetch

  • fetch 是什么

    • Fetch 是浏览器提供的原生 AJAX 接口。
  • Fetch 为何出现?

    • 由于原来的 XMLHttpRequest 不符合关注分离原则,且基于事件的模型在处理异步上已经没有现代的 Promise 等那么有优势。因此 Fetch 出现来解决这种问题。
  • Fetch API

    • Fetch API 提供了能够用于操作一部分 HTTP 的 JavaScript 接口,比如 requests 和 responses。它同时也提供了一个全局的 fetch () 方法 —— 能够简单的异步的获取资源。

      使用 window.fetch 函数可以代替以前的 .ajax. ajax、.get 和 $.post。

  • 用法

    fetch("http://example.com/movies.json")
      .then(function (response) {
        return response.json();
      })
      .then(function (myJson) {
        console.log(myJson);
      });

# 6.3、readyState(状态值)

  • readyState 是什么
    • readyState 是 XMLHttpRequest 对象的一个属性,用来标识当前 XMLHttpRequest 对象处于什么状态
  • 5 个状态值
    • 0: 请求未初始化
    • 1: 载入,XMLHttpRequest 对象开始发送请求
    • 2: 载入完成,XMLHttpRequest 对象的请求发送完成
    • 3: 解析,XMLHttpRequest 对象开始读取服务器的响应
    • 4: 完成,XMLHttpRequest 对象读取服务器响应结束

# 6.4、status(状态码

  • status 是什么

    • status 是 XMLHttpRequest 对象的一个属性,表示响应的 http 状态码
  • 在 http1.1 协议下,http 状态码总共可分为 5 大类

    • 1xx:信息响应类,表示接收到请求并且继续处理

    • 2xx:处理成功响应类,表示动作被成功接收、理解和接受

    • 3xx:重定向响应类,为了完成指定的动作,必须接受进一步处理

    • 4xx:客户端错误,客户请求包含语法错误或者是不能正确执行

    • 5xx:服务端错误,服务器不能正确执行一个正确的请求

    • 一些常见的状态码为

      200 OK:成功,很棒。

      301 永久移动:已永久移动到新位置。

      302(临时移动):暂时移到新位置。

      304 未修改:东西跟之前长一样,可以从快取拿就好。

      400 错误的请求:明显的用户端错误,伺服器无法处理这个请求。

      401 未经授权:未认证,可能需要登录或 Token。

      403 Forbidden:没有权限。

      404 未找到:找不到资源。

      500 内部服务器错误:伺服器端错误。

      502 错误的网关:通常是伺服器的某个服务没有正确执行。

      503 服务不可用:伺服器临时维护或快挂了,暂时无法处理请求。

      504 网关超时:伺服器上的服务没有回应。

# 7、Web 端即时通讯技术

  • Web 端即时通讯技术是什么

    即时通讯技术简单的说就是实现这样一种功能:服务器端可以即时地将数据的更新或变化反应到客户端,例如消息即时推送等功能都是通过这种技术实现的。但是在 Web 中,由于浏览器的限制,实现即时通讯需要借助一些方法。这种限制出现的主要原因是,一般的 Web 通信都是浏览器先发送请求到服务器,服务器再进行响应完成数据的现实更新。

  • 大体可以分为两类

    1. 一种是在 HTTP 基础上实现的
      • 短轮询、comet 和 SSE
    2. 不是在 HTTP 基础上实现
      • WebSocket
  • 如何模拟双向通信(四种方式)

    • 短轮询
      • 客户端定时向服务器发送 Ajax 请求,服务器接到请求后马上返回响应信息并关闭连接。
      • 优点 : 后端编写容易
      • 缺点 : 请求中大半是无用,浪费宽带和服务器资源
      • 适用 : 小型应用
    • 长轮询
      • 客户端向服务器发送 Ajax 请求,服务器接到请求后 hold 住连接,直到有新消息才返回响应信息并关闭连接,客户端处理完响应信息后再向服务器发送新的请求。
      • 优点 :在无消息的情况下不会频繁的请求,耗费资源小
      • 缺点
        • 服务器 hold 连接会消耗资源
        • 返回数据顺序无保证,难于管理维护
    • 长连接
      • 在页面嵌入一个隐藏 iframe,将这个隐藏 iframe 的 src 属性设为对一个长连接的请求或是采用 xhr 请求,服务器端就能源源不断的往客户端输入数据
      • 优点
        • 消息及时到达,不发无用请求
        • 管理起来也相对方便
      • 缺点:服务器维护一个长连接会增加开销
    • WebSocket
      • WebSocket 是 Html5 定义的一个新协议,与传统的 http 协议不同,该协议可以实现服务器与客户端之间全双工通信。简单来说,首先需要在客户端和服务器端建立起一个连接,这部分需要 http。连接一旦建立,客户端和服务器端就处于平等的地位,可以相互发送数据,不存在请求和响应的区别。
      • 优点:实现了双向通信
      • 缺点:服务器端的逻辑非常复杂

# 四种 Web 即时通信技术比较

  • 从兼容性角度考虑,短轮询 > 长轮询 > 长连接 SSE>WebSocket;
  • 从性能方面考虑,WebSocket > 长连接 SSE > 长轮询 > 短轮询。

# 8、跨域

  • 跨域是什么
    • 跨域是指从一个域名的网页去请求另一个域名的资源。
    • 跨域的严格一点的定义是:只要 协议,域名,端口有任何一个的不同,就被当作是跨域

# 6 种解决方案

  1. 跨域资源共享(CORS)

    • 定义

      • 定义了必须在访问跨域资源时,浏览器与服务器应该如何沟通。
    • 基本思想

      • CORS 背后的基本思想就是使用自定义的 HTTP 头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是失败
    • 两种请求

      • 简单请求
        • 就是在头信息之中,增加一个 Origin 字段。
      • 非简单请求
        • 会在正式通信之前,增加一次 HTTP 查询请求,称为 "预检" 请求(preflight)
    • 服务端

      • 服务器端对于 CORS 的支持,主要就是通过设置 Access-Control-Allow-Origin 来进行的。如果浏览器检测到相应的设置,就可以允许 Ajax 进行跨域的访问

        //指定允许其他域名访问
        "Access-Control-Allow-Origin:*"; //或指定域
        //响应类型
        "Access-Control-Allow-Methods:GET,POST";
        //响应头设置
        "Access-Control-Allow-Headers:x-requested-with,content-type";
        
  2. jsonp

    • jsonp 是什么

      • JSONP (JSON with Padding 填充式 json) 是 JSON 的一种 “使用模式”,可用于解决主流浏览器的跨域数据访问的问题。
    • 两部分组成

      • 回调函数
        • 回调函数是当响应到来时应该在页面中调用的函数
      • 数据
        • 而数据就是传入回调函数中的 JSON 数据。
    • 原理

      通过 script 标签引入一个 js 文件,这个 js 文件载入成功后会执行我们在 url 参数中指定的函数,并且会把我们需要的 json 数据作为参数传入。所以 jsonp 是需要服务器端的页面进行相应的配合的。(即用 javascript 动态加载一个 script 文件,同时定义一个 callback 函数给 script 执行而已。)

    • 模拟

      <script type="text/javascript">
          function dosomething(jsondata){
              //处理获得的json数据
          }
      </script>
      <script src="http://example.com/data.php?callback=dosomething"></script>
    • 缺点

      • 我们都知道 JSONP 可以实现解决 GET 请求的跨域问题,但是不能解决 POST 请求的跨域问题.
  3. document.domain

    • 方法

      • 通过修改 document.domain 来跨子域
    • 注意

      • 域必须相我们只能把 document.domain 设置成自身或更高一级的父域,且主同。
    • 例如:

      <iframe id = "iframe" src="http://example.com/b.html" onload = "test()"></iframe>
      <script type="text/javascript">
          document.domain = 'example.com';//设置成主域
          function test(){
          }
      </script>
    • 作用域

      • 修改 document.domain 的方法只适用于不同子域的框架间的交互。
  4. window.name

    • window.name 是什么
      • 是一个可读可写的属性,有个很有意思的跨页面特性
    • 方法
      • 页面如果设置了 window.name,即使进行了页面跳转到了其他页面,这个 window.name 还是会保留。
  5. postMessage

    • 定义

      postMessage 是 html5 引入的 API,postMessage () 方法允许来自不同源的脚本采用异步方式进行有效的通信,可以实现跨文本文档,多窗口,跨域消息传递。多用于窗口间数据通信,这也使它成为跨域通信的一种有效的解决方案.

    • 方法使用

      • 发送数据

        • otherWindow.postMessage(message, targetOrigin, [transfer]);
        • otherWindow 【窗口的一个引用,比如 iframe 的 contentWindow 属性】
      • 接收数据

        window.addEventListener("message", receiveMessage, false);
        function receiveMessage(event) {
          var origin = event.origin;
          console.log(event);
        }
    • postMessage 的使用场景

      1. 跨域通信 (包括 GET 请求和 POST 请求)
      2. WebWorker
        • Web Worker 的使用场景
          • 用于收集埋点数据,可以用于大量复杂的数据计算,复杂的图像处理,大数据的处理。因为它不会阻碍主线程的正常执行和页面 UI 的渲染.
      3. Service Worker
        • 离线存储的一个最佳的解决方案
    # WebWorker 和 Service Worker 的关系
    • 相同点
      • 相同点是在常规的 js 引擎线程以外开辟了新的 js 线程去处理一些不适合在主线程上处理的业务
    • 不同点
      • Web Worker 式服务于特定页面的,而 Service Worker 在被注册安装之后能够在多个页面使用
      • Service Worker 常驻在浏览器中,不会因为页面的关闭而被销毁。本质上,它是一个后台线程,只有你主动终结,或者浏览器回收,这个线程才会结束.
      • 生命周期,可调用的 API 也不同
    1. 代理服务器

      • 定义

        代理,也称正向代理,是指一个位于客户端和目标服务器 (target server) 之间的服务器,为了从目标服务器取得内容,客户端向代理发送一个请求并指定目标 (目标服务器),然后代理向目标服务器转交请求并将获得的内容返回给客户端。

      • 代理服务器,需要做以下几个步骤

        1. 接受客户端 请求 。
        2. 将 请求 转发给服务器
        3. 拿到服务器 响应 数据
        4. 将 响应 转发给客户端
    # CORS 和 JSONP 对比
    • CORS 与 JSONP 相比,无疑更为先进、方便和可靠。
    • 区别
      1. JSONP 只能实现 GET 请求,而 CORS 支持所有类型的 HTTP 请求。
      2. 使用 CORS,开发者可以使用普通的 XMLHttpRequest 发起请求和获得数据,比起 JSONP 有更好的错误处理。
      3. JSONP 主要被老的浏览器支持,它们往往不支持 CORS,而绝大多数现代浏览器都已经支持了 CORS)。

# 9、动画

requestanimationframe 的出现替代 setTimeout 完成动画。

  • setTimeout
    • setTimeout 其实就是通过设置一个间隔时间来不断的改变图像的位置,从而达到动画效果的。但利用 seTimeout 实现的动画在某些低端机上会出现卡顿、抖动的现象。导致 setTimeout 的执行步调和屏幕的刷新步调不一致,从而引起丢帧现象。
    • 原因
      1. setTimeout 的执行时间并不是确定的。setTimeout 任务被放进了异步队列中,只有当主线程上的任务执行完以后,才会去检查该队列里的任务是否需要开始执行,因此 setTimeout 的实际执行时间一般要比其设定的时间晚一些。
      2. 刷新频率受屏幕分辨率和屏幕尺寸的影响,因此不同设备的屏幕刷新频率可能会不同,而 setTimeout 只能设置一个固定的时间间隔,这个时间不一定和屏幕的刷新时间相同。
  • requestanimationframe
    • html5 为了满足高性能动画的需求而提供的 API,表意是请求动画帧。

# requestanimationframe 相比 setTimeout

  • 优势
    1. 与 setTimeout 相比,requestAnimationFrame 最大的优势是由系统来决定回调函数的执行时机。
    2. 它能保证回调函数在屏幕每一次的刷新间隔中只被执行一次,这样就不会引起丢帧现象,也不会导致动画出现卡顿的问题。

# 10、事件

  • 事件流

    事件流描述的是从页面中接收事件的顺序,IE 和 Netscape 提出来差不多完全相反的事件流的概念,IE 事件流是事件冒泡流,Netscape 事件流是事件捕获流。

  • DOM 事件级别

    • DOM0 事件

      • 定义

        通过文档对象(document)获取元素引用,使用 DOM0 级方法指定的事件处理程序被认为是元素的方法,处理程序是在元素的作用域进行的,程序中 this 是引用的是当前元素。

      • 3 个特点

        1. 触发时机:DOM0 级的事件处理程式只能在事件冒泡阶段触发。

        2. 每个属性只能绑定一个事件

        3. this 指针的指向

          用 DOM0 级的方式绑定事件是在元素对象的作用域内运行,因此在事件函数内的 this 属性不是引用全局对象,而是引用当前元素对象

    • DOM2 事件

      • 定义

        ’DOM2 级事件’定义了两个方法,用于处理指定和删除事件处理程序的操作:addEventListener () 和 removeEventListener (); 所有的 DOM 节点都包含这两种方法。

      • DOM2 级事件规定的事件流包括三个阶段

        • 事件捕获阶段
        • 处于目标阶段
        • 事件冒泡阶段
      • 优点

        可以添加多个事件处理程序

    • DOM3 级

      • DOM3 级事件就是在 DOM2 基础上增加了更多的事件类型

      • UI 事件,当用户与页面上的元素交互时触发,如:load、scroll

        焦点事件,当元素获得或失去焦点时触发,如:blur、focus

        鼠标事件,当用户通过鼠标在页面执行操作时触发如:dbclick、mouseup

        滚轮事件,当使用鼠标滚轮或类似设备时触发,如:mousewheel

        文本事件,当在文档中输入文本时触发,如:textInput

        键盘事件,当用户通过键盘在页面上执行操作时触发,如:keydown、keypress

        合成事件,当为 IME(输入法编辑器)输入字符时触发,如:compositionstart

        变动事件,当底层 DOM 结构发生变化时触发,如:DOMsubtreeModified

  • 机制

    1. 冒泡机制

      事件会从最内层的元素开始发生,一直向上传播,直到 document 对象。

    2. 捕获机制

      网景提出另一种事件流名为事件捕获 (event capturing)。与事件冒泡相反,事件会从最外层开始发生,直到最具体的元素。

  • 事件代理

    • 定义

      JavaScript 高级程序设计上讲:事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。

    • 关键

      Event 对象提供了一个属性叫 target,可以返回事件的目标节点,我们成为事件源

    • 适合用事件委托的事件

      click,mousedown,mouseup,keydown,keyup,keypress

    • 不合适

      mousemove,每次都要计算它的位置,非常不好把控,在不如说 focus,blur 之类的,本身就没用冒泡的特性,自然就不能用事件委托了

ES6 知识点

# 面试专题总结:ES6 知识点

希望读者依此构建自己的知识树(思维导图)

偷懒一下:可参考我自己总结思维导图 : 点这里

附带:高频面试题积累文档。 来自于(学长、牛客网等平台)

自己开发的博客地址:zxinc520.com

github 地址: 点击

此篇 js - 【ES6 知识总结】 知识点: 全部弄懂了,面试很容易。

详细可参考: ECMAScript 6 入门

# 1、es6 是什么

ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,已经在 2015 年 6 月正式发布了。 ECMA 是标准,Javascript 是 ECMA 的实现。因为 js 也是一种语言,但凡语言都有一套标准,而 ECMA 就是 javascript 的标准。在 2015 年正式发布了 ECMAscript6.0,简称 ES6,又称为 ECMAscript2015。

# 2、var、let、const(声明方式)

  • 类别
    1. 变量提升
    2. 暂时性死区
    3. 重复声明
    4. 块作用域有效
    5. 初始值
    6. 重新赋值
  • 区别
    1. let/const 定义的变量不会出现变量提升,而 var 定义的变量会提升。
    2. 相同作用域中,let 和 const 不允许重复声明,var 允许重复声明。
    3. const 声明变量时必须设置初始值
    4. const 声明一个只读的常量,这个常量不可改变
    5. let/const 声明的变量仅在块级作用域中有效。而 var 声明的变量在块级作用域外仍能访问到。
    6. 顶层作用域中 var 声明的变量挂在 window 上 (浏览器环境)
    7. let/const 有暂时性死区的问题,即 let/const 声明的变量,在定义之前都是不可用的。如果使用会抛出错误。

# 3、 变量的解构赋值

  • 数组解构赋值

    let [aa, bb, cc] = [0, 1, 2];

  • 对象解构赋值

    let { cnName, enName } = {
      id: "151521574",
      cnName: "张生",
      enName: "Ronnie",
    };
    console.log(cnName, enName); //'张生','Ronnie'

# 4、箭头函数

es6 之前的函数的 this 指向调用函数时所在的对象,而箭头函数的 this 指向函数定义时所在的对象

# 箭头函数及其 this 问题

  1. this 对象的指向是可变的,但是在箭头函数中,它是固定的。
  2. this 指向的固定化,并不是因为箭头函数内部有绑定 this 的机制,实际原因是箭头函数根本没有自己的 this,导致内部的 this 就是外层代码块的 this。正是因为它没有 this,所以也就不能用作构造函数。
  3. 箭头函数里面根本没有自己的 this,而是引用外层的 this。
  4. 由于箭头函数没有自己的 this,所以当然也就不能用 call ()、apply ()、bind () 这些方法去改变 this 的指向

# 5、Symbol

  • 是什么?

    symbols 是一种无法被重建的基本类型。这时 symbols 有点类似与对象创建的实例互相不相等的情况,但同时 symbols 又是一种无法被改变的基本类型数据。

    const s1 = Symbol();
    const s2 = Symbol();
    console.log(s1 === s2); // false
  • 作用

    1. symbols 作为对象的属性
    2. 阻止对象属性名冲突 (扩展对象属性很有用)
    3. 模拟私有属性

# 6、Module 模块

可从 IIFE、AMD、CMD、CommonJS、UMD、webpack (require.ensure)、ES Module、<script type=“module” > 这几个角度考虑。

作用 :模块化主要是用来抽离公共代码,隔离作用域,避免变量冲突等。

# 模块化发展历程

  1. IIFE

    • 使用自执行函数来编写模块化

    • 特点:

      在一个单独的函数作用域中执行代码,避免变量冲突。

  2. AMD

    • 使用 requireJS 来编写模块化

    • 特点:依赖必须提前声明好

    • 简单实现

      define("./index.js", function (code) {
        // code 就是index.js 返回的内容
      });
  3. CMD

    • 使用 seaJS 来编写模块化

    • 特点:支持动态引入依赖文件

    • 简单实现

      define(function (require, exports, module) {
        var indexCode = require("./index.js");
      });
  4. CommonJS

    • nodejs 中自带的模块化
    • var fs = require(‘fs’);
  5. UMD

    • 兼容 AMD,CommonJS 模块化语法
  6. webpack(require.ensure)

    • webpack 2.x 版本中的代码分割
  7. ES Modules

    • ES6 引入的模块化,支持 import 来引入另一个 js
    • import a from ‘a’;

# 6.1、AMD 与 CMD 的比较

  • 定义

    AMD 和 CMD 都是用于浏览器端的模块规范

  • AMD

    • AMD 是 RequireJS 在推广过程中对模块定义的规范化产出
    • 其主要内容就是定义了 define 函数该如何书写,只要你按照这个规范书写模块和依赖,require.js 就能正确的进行解析。
  • CMD

    • CMD 其实就是 SeaJS 在推广过程中对模块定义的规范化产出
    • 主要内容就是描述该如何定义模块,如何引入模块,如何导出模块,只要你按照这个规范书写代码,sea.js 就能正确的进行解析
  • AMD 与 CMD 的区别

    1. AMD 推崇依赖前置,CMD 推崇依赖就近
    2. AMD 是提前执行,CMD 是延迟执行。

# 6.2、CommonJS 与 AMD 的比较

在服务器端比如 node,采用的则是 CommonJS 规范。

AMD 和 CMD 都是用于浏览器端的模块规范

  1. CommonJS 规范加载模块是同步的,也就是说,只有加载完成,才能执行后面的操作。

  2. AMD 规范则是非同步加载模块,允许指定回调函数。

    由于 Node.js 主要用于服务器编程,模块文件一般都已经存在于本地硬盘,所以加载起来比较快,不用考虑非同步加载的方式,所以 CommonJS 规范比较适用。

  3. 但是,如果是浏览器环境,要从服务器端加载模块,这时就必须采用非同步模式,因此浏览器端一般采用 AMD 规范。

# 6.3、ES6 与 CommonJS 的比较

注意!浏览器加载 ES6 模块,也使用 <script > 标签,但是要加入 type=“module” 属性。

  1. CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
  2. CommonJS 模块是运行时加载,ES6 模块是编译时输出接口

# 7、异步编程 6 种解决方案

  1. 回调函数(Callback)

    • 回调函数是异步操作最基本的方法

    • ajax(url, () => {

      ​ // 处理逻辑

      })

    • 缺点

      • 容易写出回调地狱(Callback hell)
      • 不能使用 try catch 捕获错误,不能直接 return
  2. 事件监听

    f1.on("done", f2);
  3. 发布订阅

    jQuery.subscribe("done", f2);
  4. Promise

    • 是什么?

      • promise 是目前 JS 异步编程的主流解决方案,遵循 Promises/A+ 方案。Promise 用于异步操作,表示一个还未完成但是预期会完成的操作。
      • Promise 是 ES6 引入的一个新的对象,他的主要作用是用来解决 JS 异步机制里,回调机制产生的 “回调地狱”。它并不是什么突破性的 API,只是封装了异步回调形式,使得异步回调可以写的更加优雅,可读性更高,而且可以链式调用。
    • 剖析

      • promise 本身相当于一个状态机,拥有三种状态

        • pending
        • fulfilled
        • rejected

        一个 promise 对象初始化时的状态是 pending,调用了 resolve 后会将 promise 的状态扭转为 fulfilled,调用 reject 后会将 promise 的状态扭转为 rejected,这两种扭转一旦发生便不能再扭转该 promise 到其他状态。

    • Promise 如何使用

      构造一个 promise 对象,并将要执行的异步函数传入到 promise 的参数中执行,并且在异步执行结束后调用 resolve ( ) 函数,就可以在 promise 的 then 方法中获取到异步函数的执行结果

    • Promise 原型上的方法

      1. Promise.prototype.then(onFulfilled, onRejected)
      2. Promise.prototype.catch(onRejected)
      3. Promise.prototype.finally(onFinally)
    • Promise 静态方法

      1. Promise.all()

        Promise.all 接收一个 promise 对象数组作为参数,只有全部的 promise 都已经变为 fulfilled 状态后才会继续后面的处理

      2. Promise.race()

        这个函数会在 promises 中第一个 promise 的状态扭转后就开始后面的处理(fulfilled、rejected 均可)

      3. Promise.resolve()

      4. Promise.reject()

    • 优点

      将异步操作以同步操作的流程表达出来,promise 链式调用,更好地解决了层层嵌套的回调地狱

    • 缺点

      1. 不能取消执行。
      2. 无法获取当前执行的进度信息(比如,要在用户界面展示进度条)。
      3. 外部无法捕捉 Promise 内部抛出的错误
  5. generator 函数

    • 是什么

      • Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。
      • 如果说 JavaScript 是 ECMAScript 标准的一种具体实现、Iterator 遍历器是 Iterator 的具体实现,那么 Generator 函数可以说是 Iterator 接口的具体实现方式。
      • Generator 函数可以通过配合 Thunk 函数更轻松更优雅的实现异步编程和控制流管理
    • 描述

      • 执行 Generator 函数会返回一个遍历器对象,每一次 Generator 函数里面的 yield 都相当一次遍历器对象的 next () 方法,并且可以通过 next (value) 方法传入自定义的 value, 来改变 Generator 函数的行为。
    • 能封装异步任务的根本原因

      • 最大特点就是可以交出函数的执行权(即暂停执行)。Generator 函数可以暂停执行和恢复执行
    • 两个特征

      • function 关键字与函数名之间有一个星号
      • 函数体内部使用 yield 表达式,定义不同的内部状态(yield 在英语里的意思就是 “产出”)。
    • 过程

      Generator 函数的调用方法与普通函数一样,也是在函数名后面加上一对圆括号。不同的是,调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,也就是上一章介绍的遍历器对象(Iterator Object)

    • Generator 及其异步方面的应用

      • Generator 函数将 JavaScript 异步编程带入了一个全新的阶段
    • 总结

      调用 Generator 函数,返回一个遍历器对象,代表 Generator 函数的内部指针。以后,每次调用遍历器对象的 next 方法,就会返回一个有着 value 和 done 两个属性的对象。value 属性表示当前的内部状态的值,是 yield 表达式后面那个表达式的值;done 属性是一个布尔值,表示是否遍历结束。

    • demo

      var fetch = require("node-fetch");
      function* gen() {
        var url = "https://api.github.com/users/github";
        var result = yield fetch(url);
        console.log(result.bio);
      }
  6. async 和 await

    • 含义

      ES2017 标准引入了 async 函数,使得异步操作变得更加方便。

    • 是什么?

      • 一句话,它就是 Generator 函数的语法糖。
      • 一比较就会发现,async 函数就是将 Generator 函数的星号(*)替换成 async,将 yield 替换成 await,仅此而已。
      • async 函数可以理解为内置自动执行器的 Generator 函数语法糖,它配合 ES6 的 Promise 近乎完美的实现了异步编程解决方案。
    • 相对于 Promise,优势体现在

      1. 处理 then 的调用链,能够更清晰准确的写出代码
      2. 并且也能优雅地解决回调地狱问题
    • 相对 Generator 函数,体现在以下 4 点

      1. 内置执行器。 Generator 函数的执行必须靠执行器,所以才有了 co 函数库,而 async 函数自带执行器。也就是说,async 函数的执行,与普通函数一模一样,只要一行
      2. 更好的语义。 async 和 await,比起星号和 yield,语义更清楚了。async 表示函数里有异步操作,await 表示紧跟在后面的表达式需要等待结果
      3. 更广的适用性。 co 函数库约定,yield 命令后面只能是 Thunk 函数或 Promise 对象,而 async 函数的 await 命令后面,可以跟 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)
      4. 返回值是 Promise。async 函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便多了。你可以用 then 方法指定下一步的操作。
    • 缺点

      当然 async/await 函数也存在一些缺点,因为 await 将异步代码改造成了同步代码,如果多个异步代码没有依赖性却使用了 await 会导致性能上的降低,代码没有依赖性的话,完全可以使用 Promise.all 的方式。

# 总结

  1. JS 异步编程进化史:callback -> promise -> generator -> async + await
  2. async/await 函数的实现,就是将 Generator 函数和自动执行器,包装在一个函数里
  3. async/await 可以说是异步终极解决方案了

# 8、Class

ES6 的 class 可以看作只是一个 ES5 生成实例对象的构造函数的语法糖。它参考了 java 语言,定义了一个类的概念,让对象原型写法更加清晰,对象实例化更像是一种面向对象编程。Class 类可以通过 extends 实现继承。

  • 语法

    • super 关键字的使用
    • static 关键字
  • ES5/ES6 的继承除了写法以外还有什么区别?

    1. class 声明会提升,但不会初始化赋值。Foo 进入暂时性死区,类似于 let、const 声明变量。
    2. class 声明内部会启用严格模式
    3. class 的所有方法(包括静态方法和实例方法)都是不可枚举的
    4. class 的所有方法(包括静态方法和实例方法)都没有原型对象 prototype,所以也没有 [[construct]],不能使用 new 来调用
    5. 必须使用 new 调用 class
    6. class 内部无法重写类名

    ES5 的继承,实质是先创造子类的实例对象 this,然后再将父类的方法添加到 this 上面。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到 this 上面(所以必须先调用 super 方法),然后再用子类的构造函数修改 this

  • 优点

    但是某些时候,我们使用 es6 的类可以让我们的代码的可读性更高

# 9、Set 和 Map

  • Set
    • 是什么
      • Set 是一种叫做集合的数据结构
      • Set 是 ES6 引入的一种类似 Array 的新的数据结构,Set 实例的成员类似于数组 item 成员,区别是 Set 实例的成员都是唯一,不重复的。这个特性可以轻松地实现数组去重。
    • 应用场景
      • 数组去重
    • 特点
      1. 成员唯一、无序且不重复
      2. [value, value],键值与键名是一致的(或者说只有键值,没有键名)
      3. 可以遍历,方法有:add、delete、has
  • WeakSet
    • 特点
      1. 成员都是对象
      2. 成员都是弱引用,可以被垃圾回收机制回收,可以用来保存 DOM 节点,不容易造成内存泄漏
      3. 不能遍历,方法有 add、delete、has
  • Map
    • 是什么
      • Map 是一种叫做字典的数据结构
      • Map 是 ES6 引入的一种类似 Object 的新的数据结构,Map 可以理解为是 Object 的超集,打破了以传统键值对形式定义对象,对象的 key 不再局限于字符串,也可以是 Object。可以更加全面的描述对象的属性。
    • 应用场景
      • 数据存储
    • 特点
      • 本质上是键值对的集合,类似集合
      • 可以遍历,方法很多可以跟各种数据格式转换
  • WeakMap
    • 特点
      • 只接受对象作为键名(null 除外),不接受其他类型的值作为键名
      • 键名是弱引用,键值可以是任意的,键名所指向的对象可以被垃圾回收,此时键名是无效的
      • 不能遍历,方法有 get、set、has、delete
  • Set 和 Map
    • Set 和 Map 主要的应用场景在于数组去重和数据存储
    • 原来 Set 是一种叫做集合的数据结构,Map 是一种叫做字典的数据结构
  • Set 与 WeakSet 区别
    1. WeakSet 只能存放对象
    2. WeakSet 不支持遍历,没有 size 属性
    3. WeakSet 存放的对象不会计入到对象的引用技术,因此不会影响 GC 的回收
    4. WeakSet 存在的对象如果在外界消失了,那么在 WeakSet 里面也会不存在
  • Map 与 WeakMap 区别
    1. WeakMap 只能接受对象作为键名字 (null 除外)
    2. WeakMap 键名指向对象不会计入对象的引用数

# 10、ES6 对 String 字符串类型做的常用升级优化

  • 优化部分
    • ES6 新增了字符串模板,在拼接大段字符串时,用反斜杠 (`) 取代以往的字符串相加的形式,能保留所有空格和换行,使得字符串拼接看起来更加直观,更加优雅。
  • 升级部分
    • ES6 在 String 原型上新增了 includes () 方法,用于取代传统的只能用 indexOf 查找包含字符的方法 (indexOf 返回 - 1 表示没查到不如 includes 方法返回 false 更明确,语义更清晰), 此外还新增了 startsWith (), endsWith (), padStart (),padEnd (),repeat () 等方法,可方便的用于查找,补全字符串。

# 11、ES6 对 Number 数字类型做的常用升级优化?

  • 优化部分
    • ES6 在 Number 原型上新增了 isFinite (), isNaN () 方法,用来取代传统的全局 isFinite (), isNaN () 方法检测数值是否有限、是否是 NaN。ES5 的 isFinite (), isNaN () 方法都会先将非数值类型的参数转化为 Number 类型再做判断,这其实是不合理的,最造成 isNaN (‘NaN’) === true 的奇怪行为–'NaN’是一个字符串,但是 isNaN 却说这就是 NaN。而 Number.isFinite () 和 Number.isNaN () 则不会有此类问题 (Number.isNaN (‘NaN’) === false)。
  • 升级部分
    • ES6 在 Math 对象上新增了 Math.cbrt (),trunc (),hypot () 等等较多的科学计数法运算方法,可以更加全面的进行立方根、求和立方根等等科学计算。

# 12、ES6 对 Array 数组类型做的常用升级优化

  • 优化部分
    • 数组解构赋值。ES6 可以直接以 let [a,b,c] = [1,2,3] 形式进行变量赋值,在声明较多变量时,不用再写很多 let (var), 且映射关系清晰,且支持赋默认值
    • 扩展运算符。ES6 新增的扩展运算符 (…)(重要), 可以轻松的实现数组和松散序列的相互转化,可以取代 arguments 对象和 apply 方法,轻松获取未知参数个数情况下的参数集合。(尤其是在 ES5 中,arguments 并不是一个真正的数组,而是一个类数组的对象,但是扩展运算符的逆运算却可以返回一个真正的数组)。扩展运算符还可以轻松方便的实现数组的复制和解构赋值(let a = [2,3,4]; let b = […a])
  • 升级部分
    • ES6 在 Array 原型上新增了 find () 方法,用于取代传统的只能用 indexOf 查找包含数组项目的方法,且修复了 indexOf 查找不到 NaN 的 bug ([NaN].indexOf (NaN) === -1). 此外还新增了 copyWithin (), includes (), fill (),flat () 等方法,可方便的用于字符串的查找,补全,转换等

# 13、ES6 对 Object 类型做的常用升级优化

  • 优化部分

    1. 对象属性变量式声明。ES6 可以直接以变量形式声明对象属性或者方法,。比传统的键值对形式声明更加简洁,更加方便,语义更加清晰。
    2. 对象的解构赋值
    3. 对象的扩展运算符 (…)
    4. super 关键字。ES6 在 Class 类里新增了类似 this 的关键字 super。同 this 总是指向当前函数所在的对象不同,super 关键字总是指向当前函数所在对象的原型对象。
  • 升级部分

    1. ES6 在 Object 原型上新增了 is () 方法,做两个目标对象的相等比较,用来完善’=‘方法。’=' 方法中 NaN === NaN //false 其实是不合理的,Object.is 修复了这个小 bug。(Object.is(NaN, NaN) // true)

    2. ES6 在 Object 原型上新增了 assign () 方法,用于对象新增属性或者多个对象合并。

      const target = { a: 1 };
      const source1 = { b: 2 };
      const source2 = { c: 3 };
      Object.assign(target, source1, source2);
      target; // {a:1, b:2, c:3}
    3. ES6 在 Object 原型上新增了 getOwnPropertyDescriptors () 方法,此方法增强了 ES5 中 getOwnPropertyDescriptor () 方法,可以获取指定对象所有自身属性的描述对象。结合 defineProperties () 方法,可以完美复制对象,包括复制 get 和 set 属性。

    4. ES6 在 Object 原型上新增了 getPrototypeOf () 和 setPrototypeOf () 方法,用来获取或设置当前对象的 prototype 对象。获取或设置当前对象的 prototype 对象时,都应该采用 ES6 新增的标准用法。

    5. ES6 在 Object 原型上还新增了 Object.keys (),Object.values (),Object.entries () 方法,用来获取对象的所有键、所有值和所有键值对数组。

# 14、ES6 对 Function 函数类型做的常用升级优化

  • 优化部分

    • 箭头函数 (核心)。箭头函数里没有自己的 this, 这改变了以往 JS 函数中最让人难以理解的 this 运行机制
      1. 箭头函数内的 this 指向的是函数定义时所在的对象,而不是函数执行时所在的对象。
      2. 箭头函数不能用作构造函数,因为它没有自己的 this,无法实例化。
      3. 也是因为箭头函数没有自己的 this, 所以箭头函数 内也不存在 arguments 对象。(可以用扩展运算符代替)
  • 升级部分

    • ES6 新增了双冒号运算符,用来取代以往的 bind,call, 和 apply (浏览器暂不支持,Babel 已经支持转码)

      foo::bar;
      // 等同于
      bar.bind(foo);
      
      foo::bar(...arguments);
      // 等同于
      bar.apply(foo, arguments);

# 15、Proxy

Proxy 是 ES6 新增的一个构造函数,这个词的原意是代理,用在这里表示由它来 “代理” 某些操作,可以译为 “代理器”。Proxy 可以理解成,在目标对象之前架设一层 “拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。

# 16、Reflect

  • 是什么
    • Reflect 对象与 Proxy 对象一样,也是 ES6 为了操作对象而提供的新 API
  • 作用
    1. 一是将原生的一些零散分布在 Object、Function 或者全局函数里的方法 (如 apply、delete、get、set 等等),统一整合到 Reflect 上,这样可以更加方便更加统一的管理一些原生 API。
    2. 其次就是因为 Proxy 可以改写默认的原生 API,如果一旦原生 API 别改写可能就找不到了,所以 Reflect 也可以起到备份原生 API 的作用,使得即使原生 API 被改写了之后,也可以在被改写之后的 API 用上默认的 API。

# 17、Iterator

  • 是什么
    • 一种设计标准,来统一所有可遍历类型的遍历方式。Iterator 正是这样一种标准。或者说是一种规范理念
  • 解决的问题
    • Set、Map 都不能用 for 循环遍历,解决这个问题有两种方案,一种是为 Set、Map 单独新增一个用来遍历的 API,另一种是为 Set、Map、Array、Object 新增一个统一的遍历 API,显然,第二种更好,ES6 也就顺其自然的需要一种设计标准,来统一所有可遍历类型的遍历方式。
  • Iterator 标准的具体实现
    • Iterator 标准的具体实现是 Iterator 遍历器。Iterator 标准规定,所有部署了 key 值为 [Symbol.iterator],且 [Symbol.iterator] 的 value 是标准的 Iterator 接口函数 (标准的 Iterator 接口函数:该函数必须返回一个对象,且对象中包含 next 方法,且执行 next () 能返回包含 value/done 属性的 Iterator 对象) 的对象,都称之为可遍历对象,next () 后返回的 Iterator 对象也就是 Iterator 遍历器。

# 18、for…in 和 for…of 有什么区别

  • ES6 规定,有所部署了载了 Iterator 接口的对象 (可遍历对象) 都可以通过 for…of 去遍历,而 for…in 仅仅可以遍历对象。

  • 使用 for…of 的好处

    • 这也就意味着,数组也可以用 for…of 遍历,这极大地方便了数组的取值,且避免了很多程序用 for…in 去遍历数组的恶习。

      上面提到的扩展运算符本质上也就是 for…of 循环的一种实现。

# 19、module、export、import

  • module、export、import 是 ES6 用来统一前端模块化方案的设计思路和实现方案
  • 作用
    • export、import 的出现统一了前端模块化的实现方案,整合规范了浏览器 / 服务端的模块化方法,用来取代传统的 AMD/CMD、requireJS、seaJS、commondJS 等等一系列前端模块不同的实现方案,使前端模块化更加统一规范,JS 也能更加能实现大型的应用程序开发。
  • 注意
    • import 引入的模块是静态加载(编译阶段加载)而不是动态加载(运行时加载)
    • import 引入 export 导出的接口值是动态绑定关系,即通过该接口,可以取到模块内部实时的值

# 20、 Iterator 和 for…of(Iterator 遍历器的实现)

//自定义迭代器
let a = makeiterator([1, 2]);
function makeiterator(arr) {
  var nextindex = 0;
  return {
    next: function () {
      return nextindex < arr.length
        ? { value: arr[nextindex++], done: false }
        : { value: undefined, done: true };
    },
  };
}
// Symbol.iterator遍历器接口
let arr = ["a", "b", "c"];
let iter = arr[Symbol.iterator]();

iter.next(); // { value: 'a', done: false }
iter.next(); // { value: 'b', done: false }
iter.next(); // { value: 'c', done: false }

# 21、循环语法比较及使用场景(for、forEach、for…in、for…of)

for 循环的速度是最快的,是最老的循环,也是优化得最好的,其次是 for-of 这个是 es6 才新增的循环非常好用,最慢是 for-in 我们可以作一下速度排序

for > for-of > forEach > filter > map > for-in

变量类型和计算

# JavaScript 变量类型和计算

拿到 字节跳动实习生 offer 总结

回馈分享一波自己的知识点总结

希望读者依此构建自己的知识树(思维导图)

偷懒一下:可参考我自己总结思维导图 : 点这里

附带:高频面试题积累文档。 来自于(学长、牛客网等平台)

自己开发的博客地址:zxinc520.com

github 地址: 点击

此篇 js - 【变量类型和计算】 知识点: 全部弄懂了,面试很容易。

# 一、变量类型

# 1.1、类型

  • 值类型(基本数据类型)
    • string,number,boolean,undefined,null,symbol 6 种
  • 引用类型
    • Object、Array、Funtion。细分的话:有 Object、Array、Funtion、Date、RegExp 等

# 1.2、值类型(基本数据类型)和引用类型区别

  1. 内存的分配不同
    • 基本数据类型存储在栈中。
    • 复杂数据类型存储在堆中,栈中存储的变量,是指向堆中的引用地址

栈(操作系统):由操作系统自动分配释放 ,存放函数的参数值和局部变量的值等。堆(操作系统): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由 OS 回收,分配方式倒是类似于链表。其实在堆中一般存放变量是一些对象类型

  1. 访问机制不同:值类型按值访问,引用类型按引用访问
  2. 复变量时不同 (a=b)
    • 基本数据类型:a=b; 是将 b 中保存的原始值的副本赋值给新变量 a,a 和 b 完全独立,互不影响
    • 复杂数据类型:a=b; 将 b 保存的对象内存的引用地址赋值给了新变量 a;a 和 b 指向了同一个堆内存地址,其中一个值发生了改变,另一个也会改变。
  3. 参数传递的不同 (实参 / 形参)
    • 函数传参都是按值传递 (栈中的存储的内容):基本数据类型,拷贝的是值;复杂数据类型,拷贝的是引用地址

# 1.3、JavaScript 判断数据类型

  1. typeof

    • typeof 运算符 只能 区分 值类型 的 类型,对于引用类型的 对象、数组 区分不出来
    • 注意:typeof null===“object” typeof new Function (); //function 有效
  2. instanceof

    • instanceof 运算符返回一个布尔值,表示对象是否为某个构造函数的实例
    • 缺点:instanceof 运算符只能用于对象(纯对象和数组),不适用原始类型(Undefined、Null、Boolean、Number 和 String)的值。
  3. Object.prototype.toString.call()

    • 可以通过 Object.prototype.toString 方法准确判断某个对象值属于哪种内置类型。
  4. constructor

    • constructor 属性的作用是,可以得知某个实例对象,到底是哪一个构造函数产生的。

    • var f = new F();
      f.constructor === F; // true
      <!--code0-->
    • 奇特的~运算符

      • ~x 大致等同于 -(x+1)
      • ~ 和 indexOf () 一起可以将结果强制类型转换为真 / 假值,如果 indexOf () 返回 - 1,~ 将其转换为假值 0,其他情况一律转换为真值。
    • ~~ 字位截除

    • 显式解析数字字符串

      • Number()
      • parseInt()
      • parseFloat()
  5. 显式转换为布尔值

    • Boolean()
    • () 显式强制类型转换为布尔值最常用地方法是
  6. 抽象值操作

    • ToString
    • ToNumber
    • ToBoolean

# 2.2、隐式强制类型转换

  1. 转成字符串的

    • 字符串拼接

      var a = [1, 2];
      var b = [3, 4];
      a + b; //"1,23,4"
    • 因为数组的 valueOf () 操作无法得到简单基本类型值,于是调用 toString (),因此两个数组变成了 "1,2" 和 "3,4",+ 将它们拼接后返回。

  2. 隐式强制类型转换为布尔值

    • if () 语句中的条件判断表达式
    • for (…; …; …) 语句中的条件判断表达式
    • while () 和 do … while ()
    • ? : 中的条件判断表达式
    • 逻辑运算符 || 和 && 左边的操作数
  3. 布尔值到数字

  4. || 和 &&(选择器运算符)

    • ES5 规范中说到:&& 和 || 运算符的返回值并不一定是布尔类型,而是两个操作数其中一个的值。
      • 对于 || 来说,如果条件判断结果为 true 就返回第一个操作数的值,如果为 false 就返回第二个操作数的值。
      • 对于 && 来说,如果条件判断结果为 true 就返回第二个操作数的值,如果为 false 就返回第一个操作数的值。

# 2.3、== 和 ===(宽松相等和严格相等)

区别:允许在相等比较中进行强制类型转换,而 = 不允许。

# 2.3.1、经典问题【 if (a == 1 && a == 2 && a == 3) 】

if (a == 1 && a == 2 && a == 3) {
  //... 使之成立
}

# 思考方向 — 【利用隐式转换规则

== 操作符在左右数据类型不一致时,会先进行隐式转换。

a == 1 && a == 2 && a == 3 的值意味着其不可能是基本数据类型。因为如果 a 是 null 或者是 undefined bool 类型,都不可能返回 true。

因此可以推测 a 是复杂数据类型,JS 中复杂数据类型只有 object ,回忆一下,Object 转换为原始类型会调用什么方法?

  • 如果部署了 [Symbol.toPrimitive] 接口,那么调用此接口,若返回的不是基本数据类型,抛出错误。
  • 如果没有部署 [Symbol.toPrimitive] 接口,那么根据要转换的类型,先调用 valueOf/toString
    1. 非 Date 类型对象, hintdefault 时,调用顺序为: valueOf >>> toString ,即 valueOf 返回的不是基本数据类型,才会继续调用 toString ,如果 toString 返回的还不是基本数据类型,那么抛出错误。
    2. 如果 hintstring (Date 对象的 hint 默认是 string) ,调用顺序为: toString >>> valueOf ,即 toString 返回的不是基本数据类型,才会继续调用 valueOf ,如果 valueOf 返回的还不是基本数据类型,那么抛出错误。
    3. 如果 hintnumber ,调用顺序为: valueOf >>> toString

# 7 种解决方案

  1. 利用 [Symbol.toPrimitive] 接口

    let a = {
      [Symbol.toPrimitive]: (function (hint) {
        let i = 1;
        return function () {
          return i++;
        };
      })(),
    };
    console.log(a == 1 && a == 2 && a == 3); //true
  2. 调用 valueOf 接口

    let a = {
      valueOf: (function () {
        let i = 1;
        return function () {
          return i++;
        };
      })(),
    };
    console.log(a == 1 && a == 2 && a == 3); //true
  3. 利用 正则

    let a = {
      reg: /\d/g,
      valueOf() {
        return this.reg.exec(123)[0];
      },
    };
    console.log(a == 1 && a == 2 && a == 3); //true
  4. 利用数据劫持

    • 使用 Object.defineProperty 定义的属性,在获取属性时,会调用 get 方法。利用这个特性,我们在 window 对象上定义 a 属性

      let i = 1;
      Object.defineProperty(window, "a", {
        get: function () {
          return i++;
        },
      });
      console.log(a == 1 && a == 2 && a == 3); //true
  5. 利用 ES6 Proxy

    let a = new Proxy(
      {},
      {
        i: 1,
        get: function () {
          return () => this.i++;
        },
      }
    );
    console.log(a == 1 && a == 2 && a == 3); // true
  6. 重写数组的 join

    let a = [1, 2, 3];
    a.join = a.shift;
    console.log(a == 1 && a == 2 && a == 3); //true
  7. 利用 with 关键字

注意:0 == ‘\n’ //true

# 三、相关典型问题

  • JS 中使用 typeof 能得到的哪些类型
  • 何时使用 === 何时使用 ==
    • 除了判断 对象属性是否为空看是否函数的参数为空 的情况 ,其余的都用 === 。
  • JS 中有哪些 内置函数
  • JS 变量按照 存储方式 分为哪些类型,并描述其特点
  • 如何理解 JSON

this 指向问题

# 专题总结:this 指向问题

拿到 字节跳动实习生 offer 总结

回馈分享一波自己的知识点总结

希望读者依此构建自己的知识树(思维导图)

偷懒一下:可参考我自己总结思维导图 : 点这里

附带:高频面试题积累文档。 来自于(学长、牛客网等平台)

自己开发的博客地址:zxinc520.com

github 地址: 点击

此篇 js - 【this 指向问题】 知识点: 全部弄懂了,面试很容易。

# 一、this 的四条绑定规则

  1. 默认绑定

    • 独立函数调用时,this 指向全局对象(window),如果使用严格模式,那么全局对象无法使用默认绑定, this 绑定至 undefined。
  2. 隐式绑定

    • 函数 this 是指向调用者 (隐式指向)

      function foo() {
        console.log(this.a);
      }
      var obj = {
        a: 2,
        foo: foo,
      };
      obj.foo(); // 2
      • obj1.obj2.foo (); //foo 中的 this 与 obj2 绑定
    • 问题:隐式丢失

      • 描述:隐式丢失指的是函数中的 this 丢失绑定对象,即它会应用第 1 条的默认绑定规则,从而将 this 绑定到全局对象或者 undefined 上,取决于是否在严格模式下运行。
      • 以下情况会发生隐式丢失
        1. 绑定至上下文对象的函数被赋值给一个新的函数,然后调用这个新的函数时
        2. 传入回调函数时
  3. 显式绑定

    • 显式绑定的核心是 JavaScript 内置的 call (…) 和 apply (…) 方法,call 和 apply bind 的 this 第一个参数 (显示指向)
  4. new 绑定

    • 构造函数的 this 是 new 之后的新对象 (构造器)

# 二、call bind apply

改变函数执行时的上下文(改变函数运行时的 this 指向)

# 2.1、apply

  • 第二个参数为数组

  • 自定义实现

    // 自定义apply函数
    Function.prototype.apply1 = function (obj, arg) {
      //context为null或者是undefined时,设置默认值
      if (!obj) {
        obj = typeof window === "undefined" ? global : window;
      }
      obj.fn = this;
      let result = null;
      //undefined 或者 是 null 不是 Iterator 对象,不能被 ...
      if (arg === undefined || arg === null) {
        result = obj.fn(arg);
      } else {
        result = obj.fn(...arg);
      }
      delete obj.fn;
      return result;
    };

# 2.2、call

  • 第二个参数为参数列表

  • 自定义实现

    Function.prototype.call1 = function (obj, ...arg) {
      if (!obj) {
        obj = typeof window === "undefined" ? global : window;
      }
      obj.fn = this;
      let result = null;
      result = obj.fn(...arg);
      delete obj.fn;
      return result;
    };

# 2.3、bind

  • 特点

    • 返回一个函数
    • 可以传入参数(使用 bind 时和 bind 新生成的函数都可以传参)
    • 当 bind 返回的函数作为构造函数的时候,bind 时指定的 this 值会失效,但传入的参数依然生效
  • 注意:bind 这个方法在 IE6~8 下不兼容

  • 自定义实现

    Function.prototype.bind1 = function (obj, ...arg) {
      if (!obj) {
        obj = typeof window === "undefined" ? global : window;
      }
      let self = this;
      let args = arg;
      function f() {}
      f.prototype = this.prototype;
      let bound = function () {
        let res = [...args, ...arguments];
        let _this = this instanceof f ? this : obj;
        return self.apply(_this, res);
      };
      bound.prototype = new f();
      return bound;
    };

# 2.4、区别

call 和 apply 改变了函数的 this 上下文后便执行该函数,而 bind 则是返回改变了上下文后的一个函数。

# 三、相关题目

  1. 怎么利用 call、apply 来求一个数组中最大或者最小值

    let arr = [1, 2, 19, 6];
    Math.max.call(null, ...arr);
    Math.max.apply(null, arr);
    var fn = Math.max.bind(null, ...arr);
    fn();
  2. 如何利用 call、apply 来做继承

  3. apply、call、bind 的区别和主要应用场景

    1. 将类数组 / 含有 length 属性的对象转化为数组
    2. 求数组中的最大和最小值
    3. 数组追加
    4. 利用 call 和 apply 做继承
    5. 判断变量类型

css 面试考点全面总结

# css 面试考点全面总结

拿到 字节跳动实习生 offer 总结

回馈分享一波自己的知识点总结

希望读者依此构建自己的知识树(思维导图)

偷懒一下:可参考我自己总结思维导图 : 点这里

附带:高频面试题积累文档。 来自于(学长、牛客网等平台)

自己开发的博客地址:zxinc520.com

github 地址: 点击

此篇 css 考点 共总结 17 大知识点: 全部弄懂了,面试很容易。

# 1、盒模型 (box model)

# 1.1、是什么?

网页设计中 css 技术所使用的一种思维模型

# 1.2、为什么会出现不同模型

当年微软的 IE 浏览器占据超过 80% 市场份额的时候,想自己独立制定一套浏览器标准,其中就包括 IE 的盒模型,但是有很多公司不同意 IE 的做法,他们遵循的是 W3C 的标准来定制浏览器,也就造成了现在浏览器不同的 CSS 盒模型,但是仍有很多老网站采用的是老 IE 的标准 (怪异模式),因此很多浏览器保留了 IE 的怪异模式。

# 1.3、盒模型的两种标准

  • 标准模型
    • 元素宽高=内容(content)的宽高
  • IE 模型
    • 元素宽高=内容(content)+填充(padding)+边框 (border) 的总宽高

# 1.4、组成

  • content
  • padding
  • border
  • margin

# 1.5、CSS3 中标准或者怪异模式之间的切换(box-sizing)

  • box-sizing : content-box 采用标准模式 也是默认样式
  • box-sizing: border-box 采用 ie 怪异模式

# 2、IFC 与 BFC

# 2.1、BFC

BFC | 块级格式化上下文(Block Formatting Context)

# BFC 布局规则
  1. 内部的 Box 会在垂直方向,一个接一个地放置。

  2. Box 垂直方向的距离由 margin 决定。属于同一个 BFC 的两个相邻 Box 的 margin 会发生重叠

  3. 每个元素的左外边缘(margin-left), 与包含块的左边(contain box left)相接触 (对于从左往右的格式化,否则相反)。即使存在浮动也是如此。除非这个元素自己形成了一个新的 BFC。

  4. BFC 的区域不会与 float box 重叠。

  5. BFC 就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。

  6. 计算 BFC 的高度时,浮动元素也参与计算

# BFC 使用场景
  • 外边距折叠问题
  • 清除浮动
# 触发 BFC 的方法
  • float 属性不为 none
  • 元素的 position 为 absolute 或 fixed
  • display 属性为下列之一:table-cell | table-caption| inline-block | flex | inline-flex
  • overflow 属性不为 visible

# 2.2、IFC

IFC | 行内格式化上下文(Inline Formatting Context)

# IFC 布局规则

在行内格式化上下文中,框 (boxes) 一个接一个地水平排列,起点是包含块的顶部。水平方向上的 margin,border 和 padding 在框之间得到保留。框在垂直方向上可以以不同的方式对齐:它们的顶部或底部对齐,或根据其中文字的基线对齐。包含那些框的长方形区域,会形成一行,叫做行框。

# 3、margin 塌陷及合并问题

注意:margin 塌陷问题和合并问题都只对垂直方向有效

# 3.1、margin 塌陷问题

  • 描述

    这个问题是一个经典的浏览器内核问题。具体表现是当两个元素嵌套到一起时,外层盒模型的 margin-top 取两个元素中 margin-top 较大的值。

    因为在正常的情况下内层元素是相对于外层元素进行移动,但是这时内层元素却相对于整个文档进行移动,好像外层元素没有 “棚顶” 一样,因此叫 margin 塌陷问题。

  • 解决方法

    1. 给外层元素认为加一个 “棚顶”:border。

      这种方法虽然能够解决问题,但是在日常开发中我们不使用它,因为他在外观上对元素进行了改变。

    2. 触发 BFC

      • float 属性不为 none
      • 元素的 position 为 absolute 或 fixed
      • display 属性为下列之一:table-cell | table-caption| inline-block | flex | inline-flex
      • overflow 属性不为 visible

# 3.2、margin 合并问题

  • 描述:具体表现为两个元素并列时,两者相隔的外边距取的是两者所设置 margin 的最大值。
  • margin 合并问题解决办法
    • 我们仍然用 bfc 来解决。可以给其中一个元素包起来,在外层元素中设置 bfc 渲染规则。此时这个元素的渲染规则就改变了,就能够解决这个问题。

# 4、float

# 4.1、浮动模型

块状元素这么霸道都是独占一行,如果现在我们想让两个块状元素并排显示,怎么办呢?不要着急,设置元素浮动就可以实现这一愿望。

任何元素在默认情况下是不能浮动的,但可以通过 float 属性将元素定义为浮动,如 div、p、table、img 等元素都可以被定义为浮动。通过下面代码实现两个 div 元素在一行显示。

# 4.2、清除浮动

  1. 浮动元素后面的同级标签加 clear: both | left | right 属性

    推荐使用:after(伪类) 伪类原理:相当于在父元素里添加一个子元素(默认内联元素),用来清除容器内的浮动元素。                                       	display: "block";
        clear:both;
        height:0;
        content: "";
  2. 触发 BFC

    • float 属性不为 none
    • 元素的 position 为 absolute 或 fixed
    • display 属性为下列之一:table-cell | table-caption| inline-block | flex | inline-flex
    • overflow 属性不为 visible

# 5、flex

# 5.1、描述

2009 年,W3C 提出了一种新的方案 ----Flex 布局,可以简便、完整、响应式地实现各种页面布局。目前,它已经得到了所有浏览器的支持,这意味着,现在就能很安全地使用这项功能。

Flex 布局将成为未来布局的首选方案。

Flex 是 Flexible Box 的缩写,意为 "弹性布局",用来为盒状模型提供最大的灵活。

任何一个容器都可以指定为 Flex 布局。

# 5.2、容器的属性

  • flex-direction
    • row | row-reverse | column | column-reverse
  • flex-wrap
    • flex-wrap: nowrap | wrap | wrap-reverse;
  • flex-flow
    • 属性是 flex-direction 和 flex-wrap 的简写
  • justify-content
    • justify-content: flex-start | flex-end | center | space-between | space-around;
  • align-items
    • align-items: flex-start | flex-end | center | baseline | stretch;
  • align-content
    • align-content: flex-start | flex-end | center | space-between | space-around | stretch;

# 6、CSS 浏览器兼容性的 4 个解决方案

  • 浏览器 CSS 样式初始化

  • 浏览器私有属性

    • 我们经常会在某个 CSS 的属性前添加一些前缀,比如 - webkit- ,-moz- ,-ms-,这些就是浏览器的私有属性。 -webkit- (谷歌,Safari, 新版 Opera 浏览器,以及几乎所有 iOS 系统中的浏览器 (包括 iOS 系统中的火狐浏览器); 简单的说,所有基于 WebKit 内核的浏览器)

      -moz- (火狐浏览器)

      -o- (旧版 Opera 浏览器)

      -ms- (IE 浏览器 和 Edge 浏览器)

    • 对于私有属性的顺序要注意,把标准写法放到最后,兼容性写法放到前面

  • CSS hack 语法

    • 有时我们需要针对不同的浏览器或不同版本写特定的 CSS 样式,这种针对不同的浏览器 / 不同版本写相应的 CSS code 的过程,叫做 CSS hack!

    • 例如 IE:

       <!--[if <keywords>? IE <version>?]>
      	 代码块,可以是html,css,js
      <![endif]-->
  • 自动化插件

    • Autoprefixer 是一款自动管理浏览器前缀的插件,它可以解析 CSS 文件并且添加浏览器前缀到 CSS 内容里,使用 Can I Use(caniuse 网站)的数据来决定哪些前缀是需要的。
    • 目前 webpack、gulp、grunt 都有相应的插件,如果还没有使用,那就赶紧应用到我们的项目中吧,别再让 CSS 兼容性浪费你的时间!

# 7、position(定位)

# 7.1、文档流

简单说就是元素按照其在 HTML 中的位置顺序决定排布的过程。HTML 的布局机制就是用文档流模型的,即块元素(block)独占一行,内联元素(inline),不独占一行。

一般使用 margin 是用来隔开元素与元素的间距;padding 是用来隔开元素与内容的间隔。margin 用于布局分开元素使元素与元素互不相干;padding 用于元素与内容之间的间隔,让内容(文字)与(包裹)元素之间有一段 “距离”。

只要不是 float 和绝对定位方式布局的,都在文档流里面。

# 7.2、属性

  • static
  • relative (相对定位)
  • absolute
  • fixed
  • z-index
    • z-index 指定了一个元素及其子元素的 z-order,元素之间有重叠的时候,z-index 可以决定让哪一个元素在上方。通常来说 z-index 较大的元素会覆盖较小的一个。仅对定位的元素有效。 元素之间重叠默认的顺序是后面的元素会盖住前面的元素。如果设置了 z-index 可以改变这个顺序。但只对同级的元素有效。父元素永远在子元素后面。

# 8、行内元素 和 块级元素

# 区别
  • 块元素,总是在新行上开始;内联元素,和其他元素在一行
  • 块元素,能容纳其他块元素或者内联元素;内联元素,只能容纳文本或其他内联元素
  • 块元素中高度,行高以及顶和底边距都可以控制;内联元素中高,行高及顶和底边距不可改变。

# 9、Sass/Scss、Less、stylus

# 9.1、CSS 预处理器

  • 概念

    CSS 预处理器用一种专门的编程语言,进行 Web 页面样式设计,然后再编译成正常的 CSS 文件,以供项目使用。

  • 优点

    虽然各种预处理器功能强大,但使用最多的,还是以下特性:变量(variables),代码混合( mixins),嵌套(nested rules)以及 代码模块化 (Modules)。

# 9.2、区别

  • 编译环境不一样

    Sass 的安装需要 Ruby 环境,是在服务端处理的,而 Less 是需要引入 less.js 来处理 Less 代码输出 css 到浏览器,也可以在开发环节使用 Less,然后编译成 css 文件,直接放到项目中,也有 Less.app、SimpleLess、CodeKit.app 这样的工具,也有在线编译地址。Stylus 需要安装 node,然后安装最新的 stylus 包即可使用

  • 变量符不一样

    Less 是 @,而 Scss 是Stylus样式中声明变量没有任何限定,你可以使用“, Stylus样式中声明变量没有任何限定,你可以使用“” 符号开始。

  • 输出设置

  • 处理条件语句

  • 引用外部 CSS 文件

  • Sass 和 Less 的工具库不同

# 10、css3 动画

# 10.1、常用特效 / 变换(transform)

  • scale(2D 缩放)
  • rotate(2D 旋转)
  • translate(2D 位移)
  • skew(2D 倾斜)

# 10.2、animation

  • 属性

    • animation-name :规定需要绑定到选择器的 keyframe 名称。
    • animation-duration:规定完成动画所花费的时间,以秒或毫秒计。
    • animation-timing-function:规定动画的速度曲线。
    • animation-delay :规定在动画开始之前的延迟。
    • animation-iteration-count:规定动画应该播放的次数。
    • animation-direction :规定是否应该轮流反向播放动画。
    • animation-fill-mode :规定动画在播放之前或之后,其动画效果是否可见
  • 简写:

    animation: name duration timing-function delay iteration-count direction fill-mode;

  • 举例:

    animation: wang 3s linear 1s infinite alternate forwards ;

# 10.3、keyframes

  • 这个属性用来定义一系列关键帧。也就是在动画运行的全过程中的一个个中间点。

    @keyframes zoomIn {
      0% {
        transform: scale(0);
      }
      60% {
        transform: scale(1.1);
      }
      100% {
        transform: scale(1);
      }
    }

# 11、居中布局

  1. 使用 Flex

  2. 使用绝对定位

  3. inline-block

    .parent2{ text-align: center; } .parent2 span{ display: inline-block;
    height:50% } .parent2 .child{ display: inline-block; color: #fff; }
    <div class="parent2">
      <span></span>
      <div class="child">hello world-2</div>
    </div>
  4. 使用 table 和 table-cell

    .parent1 {
      display: table;
    }
    .parent1 .child {
      display: table-cell;
    }
  5. 子元素是单行文本

    设置父元素的 text-align 和 line-height = height

  6. 利用 grid 布局

    .container {
      display: grid;
    }
    .box {
      justify-self: center;
      align-self: center;
    }
  7. 利用绝对定位和 margin:auto

    /* 无需知道被居中元素的宽高 */
    .box {
      position: absolute;
      left: 0;
      top: 0;
      right: 0;
      bottom: 0;
      margin: auto;
    }
    .container {
      position: relative;
    }

# 12、等高布局

  1. flex 布局

  2. 使用负 margin-bottom 和正 padding-bottom 对冲实现

    .Article > li {
      float: left;
      margin: 0 10px -9999px 0;
      padding-bottom: 9999px;
    }
  3. 模仿 table 布局

    父:display: table; 子: display: table-cell;

  4. grid 布局

# 13、三栏布局

# 13.1、特点

  • 两侧宽度固定,中间宽度自适应
  • 中间部分在 DOM 结构上优先,以便先行渲染
  • 都需要一个格外的 Div.container
  • 允许任意列的高度最高

# 13.2、圣杯布局

  • 特点 :使用了相对定位
  • 优势:在 DOM 结构上显得更加值观和自然

实现

<style>
        *{
            padding: 0;
            margin: 0;
        }
        .container{
            overflow: hidden;
            padding: 0 100px 0 100px;

        }

        .middle,.left,.right{
            position: relative;
            float: left;
        }

        .left{
            width: 100px;
            height: 100px;
            background: red;
            margin-left: -100%;
            left: -100px;
        }

        .right{
            width: 100px;
            height: 100px;
            background: green;
            margin-left: -100px;
            right: -100px;

        }
        .middle{
            background: blue;
            width: 100%;
            height: 300px;
        }
    </style>
</head>
<body>
<div class="container">
    <div class="middle"></div>
    <div class="left"></div>
    <div class="right"></div>
</div>

# 13.3、双飞翼布局

  • 特点:不需要定位,只用了浮动和负边距
  • 优势
    • 不需要使用定位,所以更加简洁
    • 允许的页面最小宽度通常比圣杯布局更小

实现:

<style>
       .container {
           overflow: hidden;
       }
       .middle, .left, .right {
           float: left;
           height: 100px;
       }
       .left {
           width: 100px;
           background: red;
           margin-left: -100%;
       }
       .right {
           width: 100px;
           background: blue;
           margin-left: -100px;
       }
       .middle {
           width: 100%;
           background: aqua;
       }
       .inner {
           margin: 0 100px;
       }
   </style>
</head>
<body>
<div class="container">
   <div class="middle">
       <div class="inner">middle</div>
   </div>
   <div class="left">left</div>
   <div class="right">right</div>
</div>

# 13.4、区别

两者都是为了不让左右俩不遮住 middle,经典圣杯布局通过父亲 padding 给左右俩腾位置从而不会遮住 middle 内容,而双飞翼是 middle 设置 margin,限制内部内容区域,从而左右俩遮的地方不会影响到 middle 内容

对于三栏布局,modern solution 是 flex box/grid 布局,这两者可以轻松实现 mobile-friendly 的方案,也可以控制顺序,middle 依然可以先渲染,2019 年兼容性不错了,如果 APP 无视 IE,这是优选

# 14、多栏布局

# 14.1、栅格系统(grid systems)

  • 特点 :利用浮动实现的多栏布局
  • 表现 : Bootstrap

# 14.2、多列布局

  • 特点:将内容按指定的列数排列

  • 表现 :报纸排版

  • 使用方式: 通过 css3 的 column

    • IE10 及以上和其它现代浏览器
    • 但 -webkit- 以及 -moz- 前缀不能省略
    • 比 flex 弹性布局更稳定、更兼容
  • 语法

    • columns: <‘column-width’> || <‘column-count’>

      设置对象的列数和每列的宽度。复合属性。

    • column-width :设置对象的宽度

    • column-count :用来定义对象中的列数,使用数字 1-10 表示

    • column-gap :设置列与列之间的间距

    • column-rule:<’ column-rule-width ‘> || <’ column-rule-style ‘> || <’ column-rule-color '>

      • 设置对象的列与列之间的边框。复合属性
      • column-rule: 10px solid #090;
    • column-fill:auto | balance

      • 设置对象所有列的高度是否统一

# 15、弹性布局(Flexbox)

  • CSS3 引入的新模式
    • 用来为盒装模型提供的最大的灵活性
    • 目前已经得到了所有现代浏览器的支持
  • 优势
    • 轻松实现视图大小变化时对元素的相对位置的大小的保持
    • 减少了对浮动布局的依赖以及重置元素的大小
  • 注意
    • Webkit 内核的浏览器,必须加上 -webkit 前缀 display:-webkit-flex
    • 子元素的 float、clear 和 vertical-align 属性失效

# 16、流式布局

  • 主要靠百分比进行排版
  • 对应布局
    • 瀑布流布局
      • 表现 :参差不齐的多栏布局
      • 实现方式 : 同样可以用 column 实现

# 17、响应式布局

  • 特点
    • 一个网站能够兼容多个终端
    • 解决不用设备之间分辨率之间的兼容问题
  • 实现方式
    • css3 的媒体查询
    • 检测设备屏幕大小,通过 css 媒体查询来有针对性的更改页面的布局
  • Copyrights © 2015-2021 zhou chen
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信