美文网首页
前端分享

前端分享

作者: 小雨雪smile | 来源:发表于2020-08-14 15:24 被阅读0次

编辑器

工欲善其事必先利其器,好用的开发工具可以让开发事半功倍

1. VSCode(重点)

image.png
image.png

简称VSCode,Visual Studio Code中文版是微软推出的带 GUI 的代码编辑器,是一款免费开源的现代化轻量级代码编辑器,支持几乎所有主流开发语言,软件功能非常强大,界面简洁明了。操作方便快捷,设计得人性化。软化主要改进了文档视图,完善对Markdown的支持。

特点:

  • 插件数量多,安装方便,重点是免费,可随意下载, 分发, 商用等。
  • 微软提供支持全职团队来开发的一款编辑器。
  • 该软件同时支持主流三大平台。
image.png

海量安装插件,安装方便:
1.例如安装VSCode汉化:


image.png

VScode的插件推荐:

  • Chinese (Simplified) Language Pack for Visual Studio Code 汉化VScode的插件
  • TSLint 代码检查插件,让我们的代码更加的规范和正确。
  • Vetur Vue工具插件,可以让Vue代码高亮显示、进行Vue代码检查、提示Vue代码、格式化代码等。
  • Beautify 代码美化工具,也就是对我们的代码进行格式化。
  • Auto Close Tag (自动闭合HTML/XML标签)
  • Auto Rename Tag 自动完成另一侧标签的同步修改
  • GitLens 方便查看git日志
  • Path Intellisense 自动提示文件路径

2.集成终端,可直接命令行运行:


image.png

3.集成git可视化监听,一键提交


image.png

4.同时打开多个编辑器(查看多个文件)

新建文件 Ctrl+N
历史打开文件之间切换 Ctrl+Tab,Alt+Left,Alt+Right
切出一个新的编辑器(最多3个)Ctrl+\,也可以按住Ctrl鼠标点击Explorer里的文件名
左中右3个编辑器的快捷键Ctrl+1 Ctrl+2 Ctrl+3
3个编辑器之间循环切换 Ctrl+`
编辑器换位置,Ctrl+k然后按Left或Right

5.自动保存:File -> AutoSave ,或者Ctrl+Shift+P,输入 auto

2.Sublime Text

image.png

sublime text也是前端开发比较常见的工具之一,特点是可通过配置插件来构建一个比较丰富的开发环境,同时sublime text比较轻(比Hbuilder还要轻),如果熟悉了它的快捷键,那么使用起来还是比较便捷的。Sublime Text的主要功能包括:拼写检查,书签,完整的Python API,Goto功能,即时项目切换,多选择,多窗口等等。

特点:

  • 体积较小,运行速度快
  • 在Linux中sublime无法输入中文,解决方法繁琐
  • 收费软件、轻量插件多,安装复杂,需要翻墙,对于前端新手,不推荐使用

3.HBuilder

image.png

由于HBuilder是国产的软件,符合中文用户的编程习惯,所以也有很大一部的前端开发喜欢使用,可以方便的制作手机APP。
特点:

  • 可以调用android ios模拟器查看界面效果
  • 不能直接在别的界面打开.css .js文件
  • 使用Hbuilder有一个问题,就是之前开发过的页面如果通过导入功能导入到Hbuilder中会出现中文乱码。

框架选择

React

React框架是起源于Facebook的项目,可以轻易的解决跨浏览器兼容的问题,主要是通过对DOM的模拟减少与DOM的交互做到的。React的模块化把组件进行了隔离,出现问题的时候更方便程序员对其进行修改,而且由于JavaScript,因此更有利于搜索引擎的优化。

优点:引入了一个叫做虚拟DOM的概念,运行速度快;提供了标准化的API,解决了跨浏览器问题、兼容性更好;代码更加模块化,重用代码更容易,可维护性高。

缺点:React是目标是UI组件,通常可以和其它框架组合使用,并不适合单独做一个完整的框架。

Angular

Angular拥有很好的应用程序,是一个以JavaSpript编写的库,模板功能也异常强大,本身就带有丰富的Angular指令。一方面可以通过指令扩宽HTML,一方面可以通过表达式绑定数据到HTML。

优点:模板功能强大丰富并且是声明式的,是一个比较完善的前端MVC框架,自带了丰富的Angular指令;ng模块化比较大胆的引入了Java的一些东西(依赖注入),能够很容易的写出可复用的代码,对于敏捷开发的团队来说非常有帮助。

缺点:验证功能错误信息显示比较薄弱,需要写很多模板标签;ngView只能有一个,不能嵌套多个视图;比较笨重,没有让用户选择一个轻量级的版本

Vue(重点)

Vue是相对比较轻量级的框架,是通过进行双向数据绑定来达到驱动页面的效果,大多程序员在学习新框架的时候都会先从Vue开始。Vue比较简单,官方文档介绍的很清楚,可以非常快速的通过异步批处理的方式对DOM进行更新,也能把可复用的、解耦的组件组合在一起使用,更能允许多种模块的安装,场景使用也更加灵活。

特点

  1. 轻量级的框架

vue.js 能够自动追踪依赖的模板表达式和计算属性,提供 MVVM 数据绑定和一个可组合的组件系统,具有简单、灵活的 API,使读者更加容易理解,能够更快上手。

  1. 双向数据绑定

声明式渲染是数据双向绑定的主要体现,同样也是 vue.js 的核心,它允许采用简洁的模板语法将数据声明式渲染整合进 DOM。

  1. 指令

Vue.js 与页面进行交互,主要就是通过内置指令来完成的,指令的作用是当其表达式的值改变时相应地将某些行为应用到 DOM 上。

  1. 组件化

组件(Component)是 Vue.js 最强大的功能之一。组件可以扩展 html 元素,封装可重用的代码。

在 Vue 中,父子组件通过 props 传递通信,从父向子单向传递。子组件与父组件通信,通过触发事件通知父组件改变数据。这样就形成了一个基本的父子通信模式。

在开发中组件和 html、JavaScript 等有非常紧密的关系时,可以根据实际的需要自定义组件,使开发变得更加便利,可大量减少代码编写量。

组件还支持热重载(hotreload)。当我们做了修改时,不会刷新页面,只是对组件本身进行立刻重载,不会影响整个应用当前的状态。css 也支持热重载。

  1. 客户端路由

Vue-router 是 Vue.js 官方的路由插件,与 Vue.js 深度集成,用于构建单页面应用。Vue 单页面应用是基于路由和组件的,路由用于设定访问路径,并将路径和组件映射起来,传统的页面是通过超链接实现页面的切换和跳转的。

  1. 状态管理

状态管理实际就是一个单向的数据流,State 驱动 View 的渲染,而用户对 View 进行操作产生 Action,使 State 产生变化,从而使 View 重新渲染,形成一个单独的组件。

优点:渐进式构建能力是Vue.js最大的优势,Vue有一个简洁而且合理的架构,使得它易于理解和构建。Vue有一个强大的充满激情人群的社区,这为Vue.js增加了巨大的价值,使得为一个空白项目创建一个综合的解决方案变得十分容易。

缺点:在模型-视图应用程序和状态容器类型的应用程序之间的互相转换可能会令人感到困惑;它类似于Web组件的模式,而不是真正的Web组件。

1.与angularjs的区别

相同点:

  • 都支持指令:内置指令和自定义指令。
  • 都支持过滤器:内置过滤器和自定义过滤器。
  • 都支持双向数据绑定。
  • 都不支持低端浏览器。

不同点:

  • AngularJS的学习成本高,比如增加了Dependency Injection特性,而Vue.js本身提供的API都比较简单、直观。

  • 在性能上,AngularJS依赖对数据做脏检查,所以Watcher越多越慢。
    Vue.js使用基于依赖追踪的观察并且使用异步队列更新。所有的数据都是独立触发的。
    对于庞大的应用来说,这个优化差异还是比较明显的。

2.与React的区别

相同点:

react采用特殊的JSX语法,Vue.js在组件开发中也推崇编写.vue特殊文件格式,对文件内容都有一些约定,两者都需要编译后使用。
中心思想相同:一切都是组件,组件实例之间可以嵌套。
都提供合理的钩子函数,可以让开发者定制化地去处理需求。
都不内置列数AJAX,Route等功能到核心包,而是以插件的方式加载。
在组件开发中都支持mixins的特性。

不同点:

React依赖Virtual DOM,而Vue.js使用的是DOM模板。React采用的Virtual DOM会对渲染出来的结果做脏检查。

Vue.js在模板中提供了指令,过滤器等,可以非常方便,快捷地操作DOM。

vue常用的UI框架

移动端

1、Vant(重点)
有赞前端团队基于有赞统一的规范实现的 Vue 组件库,提供了一整套 UI 基础组件和业务组件。



特性

  • 60+ 高质量组件


    image.png
  • 90% 单元测试覆盖率
  • 完善的中英文文档和示例
  • 支持按需引入
  • 支持主题定制
  • 支持国际化
  • 支持 TS
  • 支持 SSR
  • 生态化 现有的一些生态或能力:
    基于 vue-cli 的脚手架 vue-cli-template-vant
    支持 nuxt 服务器端渲染
    支持 Typescript 类型检测
    支持 i18n 多语言定制
    支持通过 postcss 插件进行主题定制
    官方 Demo 仓库 - vant-demo
    基于相同视觉规范的小程序组件库 - zanui-weapp
    开源社区的移动商城示例项目 - vant--mobile-mall

2、Mint UI
饿了么开源的移动端UI组件库,基于vue.js的移动端UI框架,包含丰富的 CSS 和 JS 组件,能够满足日常的移动端开发需求。



特性
Mint UI 包含丰富的 CSS 和 JS 组件,能够满足日常的移动端开发需要
可按需加载组件。可以只加载声明过的组件及其样式文件,无需再纠结文件体积过大
采用 CSS3 处理各种动效,避免浏览器进行不必要重绘和重排,使用户获得流畅顺滑的体验
Mint UI 做到了轻量化。即使全部引入,压缩后的文件体积也仅有 ~30kb

3、Cube UI
滴滴团队开发的基于 Vue.js 实现的精致移动端组件库。支持按需引入和后编译,轻量灵活;扩展性强,可以方便地基于现有组件实现二次开发。



特性

  • 质量可靠:由滴滴内部组件库精简提炼而来,为集成提供保障。
  • 体验极致:以迅速响应、动画流畅、接近原生为目标,在交互体验方面追求极致。
  • 标准规范:遵循统一的设计交互标准,高度还原设计效果。
  • 扩展性强:支持按需引入,轻量灵活;扩展性强,可以方便地实现二次开发。

4、Muse UI
基于 Vue 2.0 优雅的 Material Design UI 组件库,基本实现 Material Design 设计规范类的所有组件,另外还开发许多新的功能特性。


特性

  • 组件丰富:拥有40多个UI 组件,用于适应不同业务环境
  • 可定制化:自定义主题方式极为优雅,仅需少量代码即可完成主题样式替换
  • 基于vue2.x:Muse UI 基于 Vue2.0 开发,小巧,api友好,可用于开发的复杂单页应用

5、NutUI
京东自研的轻量级移动端Vue组件库,目前最新的版本是2.2.5,有近50多个组件。开发和服务于移动Web界面的企业级前中后台产品。



特性

  • 50+ 高质量组件
  • 40+ 京东移动端项目正在使用
  • 基于京东APP 7.0 视觉规范
  • 支持按需加载
  • 详尽的文档和示例
  • 支持定制主题
  • 支持多语言(国际化)
  • 支持 TypeScript
  • 支持服务端渲染(Vue SSR)
  • 单元测试加持
  • 配套有基于Webpack的构建工具,可快速创建已内置本组件库的Vue工程

pc端

1.Element(重点)
Element,一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的桌面端组件库

image.png
  • 丰富的 feature:丰富的组件,自定义主题,国际化。
  • 文档 & demo:提供友好的文档和 demo,维护成本小,支持多语言。
  • 安装 & 引入:支持 npm 方式和 cdn 方式,并支持按需引入。
  • 工程化:开发,测试,构建,部署,持续集成。
  1. iView
    一套基于 Vue.js 的高质量UI 组件库
    image.png
  1. vue-element-admin
    vue-element-admin是基于 Vue2.0,配合使用 Element UI 组件库的一个前端管理后台集成解决方案。它使用了最新的前端技术栈,提炼了典型的业务模型,提供了丰富的功能组件,它可以帮助你快速搭建企业级中后台产品原型。
    image.png

在vue调试方面

可以选择安装chrome插件vue Devtools。打开vue项目,在console控制台选择vue面板。在Devtools工具中,可以选择组件,查看对应组件内的数据信息。也可以选择Vuex选项,查看该项目内Vuex的状态变量信息。



你不知道的 vue-devtools

开发需要用到的环境: node

node.js是运行在服务器端的JavaScript
node.js开发就是利用ES+第三方开源库+node平台环境API进行编程

自动化打包:webpack、gulp、grunt

旧时代前端  以传统的静态页面 (html js css )编写 + css3特效 transfrom位移、旋转等等 
+ jquery zepto swiper 等常用js插件 也能手写一些简单特效等等 。处理pc手机游览器兼容。
grunt gulp 打打包之类 

新时代前端  以nodejs和npm为核心 需要什么就install什么 然后require 什么 。
熟悉gulp webpack less sass 常用构建工具。
他们是一种写app方式写html. 连入口文件都基本叫app.js

Gulp
Gulp强调的是前端开发的工作流程。我们可以通过配置一系列的task,定义task处理的事务
(例如文件压缩合并、雪碧图、启动server、版本控制等),然后定义执行顺序,
来让Gulp执行这些task,从而构建项目的整个前端开发流程。
说的形象点,“Gulp就像是一个产品的流水线,整个产品从无到有,都要受流水线的控制,
在流水线上我们可以对产品进行管理。”

Webpack
Webpack是一个前端模块化方案,更侧重模块打包。我们可以把开发中的所有资源
(图片、js文件、css文件等)都看成模块,通过loader(加载器)和plugins(插件)
对资源进行处理,打包成符合生产环境部署的前端资源。
Webpack就是需要通过其配置文件(Webpack.config.js)中 entry 配置的一个入口文件(JS文件),
然后在解析过程中,发现其他的模块,如scss等文件,
再调用配置的loader或者插件对相关文件进行解析处理。

两者区别
虽然Gulp 和 Webpack都是前端自动化构建工具,但看2者的定位就知道不是对等的。
Gulp严格上讲,模块化不是他强调的东西,旨在规范前端开发流程。
Webpack更明显的强调模块化开发,而那些文件压缩合并、预处理等功能,不过是他附带的功能。

包管理:yarn、npm 、bower



接口测试工具:Postman

Postman背景介绍
Postman是一款功能强大的网页调试与发送网页HTTP请求的Chrome插件
这款网页调试工具不仅可以调试简单的css、html、脚本等简单的网页基本信息,
它还可以发送几乎所有类型的HTTP请求!
Postman在发送网络HTTP请求方面可以说是Chrome插件类产品中的代表产品之一。
Postman的操作环境

postman适用于不同的操作系统,Postman Mac、Windows X32、Windows X64、Linux系统,
还支持postman 浏览器扩展程序、postman chrome应用程序等。

反向代理工具:nginx 具体使用

ninx 功能介绍
Nginx因为它的稳定性、丰富的模块库、灵活的配置和低系统资源的消耗而闻名.
业界一致认为它是Apache2.2+mod_proxy_balancer的轻量级代替者,
不仅是因为响应静态页面的速度非常快,而且它的模块数量达到Apache的近2/3。
对proxy和rewrite模块的支持很彻底,还支持mod_fcgi、ssl、vhosts ,
适合用来做mongrel clusters的前端HTTP响应。
nginx和Apache一样使用模块化设计,nginx模块包括内置模块和第三方模块,
其中内置模块中包含主模块和事件模块

nginx处理请求逻辑图


nginx 的优点
1. 高并发。静态小文件
2.占用资源少。2万并发、10个线程,内存消耗几百M。
3.功能种类比较多。web,cache,proxy。每一个功能都不是特别强。
4.支持epoll模型,使得nginx可以支持高并发。
5. nginx 配合动态服务和Apache有区别。(FASTCGI 接口)
6.利用nginx可以对IP限速,可以限制连接数。
7.配置简单,更灵活。

nginx应用场合
1.静态服务器。(图片,视频服务)另一个lighttpd。并发几万,html,js,css,flv,jpg,gif等。
2.动态服务,nginx——fastcgi 的方式运行PHP,jsp。(PHP并发在500-1500,MySQL 并发在300-1500)。
3.反向代理,负载均衡。日pv2000W以下,都可以直接用nginx做代理。
4.缓存服务。类似 SQUID,VARNISH。

内网穿透工具:FRP

frp 的作用
利用处于内网或防火墙后的机器,对外网环境提供 http 或 https 服务。
对于 http 服务支持基于域名的虚拟主机,支持自定义域名绑定,使多个域名可以共用一个80端口。
利用处于内网或防火墙后的机器,对外网环境提供 tcp 服务,例如在家里通过 ssh 访问处于公司内网环境内的主机。
可查看通过代理的所有 http 请求和响应的详细信息。(待开发)

代码检查 - eslint/editorconfig/prettier/husky/tslint

1.开发过程中,如果写出不符合我们规范的代码,能够及时提醒开发者,便于及时修复(eslint的能力)

2.保存/粘贴代码时、使用格式化快捷键时,能够自动按照我们制定的规范、格式化代码(prettier的能力)

3.不同开发者如果使用不同的编辑器(sublime/vscode)或系统(windows/mac),能够执行统一的代码风格标准。
比如:缩进是tab还是space,结尾end_of_line是lf还是crlf(editorconfig的能力 —— 用于覆盖编辑器的默认配置)

数据可视化库

echarts - 它是用纯 JavaScript 编写的,基于 zrender 画布。它支持以画布、SVG(4.0+) 和 VML 的形式绘制图表。除了 PC 和移动浏览器外,ECharts 还可以与 node 上的 node-canvas 一起使用,以便进行高效的服务器端渲染(SSR)
Highcharts JS - 是一个广受欢迎的基于 SVG 的 JavaScript 图表库,针对旧的浏览器可降级到 VML 和画布。世界上最大的 100 家公司中,有 72 家公司(Facebook、Twitter 等)使用了这个库,这使得它成为世界上最流行的 JS 图表 API

Chart.js - 一个非常受欢迎的开源 HTML5 图表库,它使用画布元素构建响应式 Web 应用。ChartJS 提供了混合图表类型,新的图表轴类型和漂亮的动画。它的设计简单而优雅,有 8 种基本的图表类型,你可以将该库与 moment.js 结合在一起使用,用于渲染时间轴

three.js- ThreeJS 用 WebGL 创建 3D 动画。该项目的灵活性和抽象性意味着它也可用于 2 维或 3 维的数据可视化

Raphael - 是一个 JavaScript 矢量库,可在 Web 中绘制矢量图形。该库使用 SVG W3C 和 VML 作为创建图形的基础,因此每个图形对象也是 DOM 对象,你可以附加 JavaScript 事件处理程序。Raphael 目前支持 Firefox 3.0+、Safari 3.0+、Chrome 5.0+、Opera 9.5+ 和 Internet Explorer 6.0+

知识点

跨域解决方案(细节篇)

axios怎么封装,才能提升效率?(细节篇)

H5与原生混合开发交互

JavaScript 代码优化建议(细节篇)

关于http协议

JavaScript基础: 类与继承

JavaScript不像Java语言本身就具有类的概念,JavaScript作为一门基于原型(ProtoType)的语言,时至今日,仍然有很多人不建议在JavaScript中大量使用面对对象的特性。但就目前而言,很多前端框架,例如React都有基于类的概念。首先明确一点,类存在的目的就是为了生成对象,而在JavaScript生成对象的过程并不不像其他语言那么繁琐,我们可以通过对象字面量语法轻松的创建一个对象:

var person = {
    name: "MrErHu", 
    sayName: function(){
        alert(this.name);
    }
};

但是当我们希望创建无数个相似的对象时,我们就会发现对象字面量的方法就不能满足了,那可以采用工厂模式去创建一系列的对象:

function createObject(name){
    return {
        "name": name,
        "sayName": function(){
            alert(this.name);
        }
    }
}

但是这样方式有一个显著的问题,我们通过工厂模式生成的各个对象之间并没有联系,没法识别对象的类型,这时候就出现了构造函数。在JavaScript中构造函数和普通的函数没有任何的区别,仅仅是构造函数是通过new操作符调用的。

 function Person(name, age, job){
    this.name = name;
    this.sayName = function(){
        alert(this.name);
    };    
 }

var obj = new Person();
obj.sayName();

我们知道new操作符会做以下四个步骤的操作:

  1. 创建一个全新的对象
  2. 新对象内部属性[Prototype]连接到构造函数的原型
  3. 构造函数的this会绑定新的对象
  4. 如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象

这样我们通过构造函数的方式生成的对象就可以进行类型判断。但是单纯的构造函数模式会存在一个问题,就是每个对象的方法都是相互独立的,而函数本质上就是一种对象,因此就会造成大量的内存浪费。回顾new操作符的第三个步骤,我们新生成对象的内部属性[[Prototype]]会连接到构造函数的原型上,因此利用这个特性,我们可以混合构造函数模式原型模式,解决上面的问题。

function Person(name, age, job){
    this.name = name;
}

Person.prototype = {
    constructor : Person,
    sayName : function(){
        alert(this.name);
    }
}

var obj = new Person();
obj.sayName();

我们通过将sayName函数放到构造函数的原型中,这样生成的对象在使用sayName函数通过查找原型链就可以找到对应的方法,所有对象共用一个方法就解决了上述问题,即使你可能认为原型链查找可能会耽误一点时间,实际上对于现在的JavaScript引擎这种问题可以忽略。对于构造函数的原型修改,处理上述的方式,可能还存在:

Person.prototype.sayName = function(){
    alert(this.name);
}

我们知道函数的原型中的constructor属性是执行函数本身,如果你是将原来的原型替换成新的对象并且constructor对你又比较重要记得手动添加,因此第一种并不准确,因为constructor是不可枚举的,因此更准确的写法应该是

Object.defineProperty(Person, "constructor", {
    configurable: false,
    enumerable: false,
    writable: true,
    value: Person
});

到现在为止,我们会觉得在JavaScript中创建个类也太麻烦了,其实远远不止如此,比如我们创建的类可能会被直接调用,造成全局环境的污染,比如:

Person('MrErHu');
console.log(window.name); //MrErHu

不过我们迎来了ES6的时代,事情正在其变化,ES6为我们在JavaScript中实现了类的概念,上面的的代码都可以用简介的类(class)实现:

  class Person {
    constructor(name){
        this.name = name;
    }
    
    sayName(){
        alert(this.name);
    }
}

通过上面我们就定义了一个类,使用的时候同之前一样:

let person = new Person('MrErHu');
person.sayName(); //MrErHu

我们可以看到,类中的constructor函数负担起了之前的构造函数的功能,类中的实例属性都可以在这里初始化。类的方法sayName相当于之前我们定义在构造函数的原型上。其实在ES6中类仅仅只是函数的语法糖:

typeof Person  //"function"

相比于上面自己创建的类方式,ES6中的类有几个方面是与我们自定义的类不相同的。首先类是不存在变量提升的,因此不能先使用后定义:

l

et person = new Person('MrErHu')
class Person { //...... } 

上面的使用方式是错误的。因此类更像一个函数表达式。
  其次,类声明中的所有代码都是自动运行在严格模式下,并且不能让类脱离严格模式。相当于类声明中的所有代码都运行在"use strict"中。
  再者,类中的所有方法都是都是不可枚举的。
  最后,类是不能直接调用的,必须通过new操作符调用。其实对于函数有内部属性[[Constructor]][[Call]],当然这两个方法我们在外部是没法访问到的,仅存在于JavaScript引擎。当我们直接调用函数时,其实就是调用了内部属性[[Call]],所做的就是直接执行了函数体。当我们通过new操作符调用时,其实就是调用了内部属性[[Constructor]],所做的就是创建新的实例对象,并在实例对象上执行函数(绑定this),最后返回新的实例对象。因为类中不含有内部属性[[Call]],因此是没法直接调用的。顺便可以提一句ES6中的元属性 new.target

所谓的元属性指的就是非对象的属性,可以提供给我们一些补充信息。new.target就是其中一个元属性,当调用的是[[Constructor]]属性时,new.target就是new操作符的目标,如果调用的是[[Call]]属性,new.target就是undefined。其实这个属性是非常有用的,比如我们可以定义一个仅可以通过new操作符调用的函数:

  function Person(){
    if(new.target === undefined){
        throw('该函数必须通过new操作符调用');
    }
  }

或者我们可以用JavaScript创建一个类似于C++中的虚函数的函数:

class Person {
  constructor() {
    if (new.target === Person) {
      throw new Error('本类不能实例化');
    }
  }
}

继承

在没有ES6的时代,想要实现继承是一个不小的工作。一方面我们要在派生类中创建父类的属性,另一方面我们需要继承父类的方法,例如下面的实现方法:

function Rectangle(width, height){
  this.width = width;
  this.height = height;
}

Rectangle.prototype.getArea = function(){
  return this.width * this.height;
}

function Square(length){
  Rectangle.call(this, length, length);
}

Square.prototype = Object.create(Rectangle.prototype, {
  constructor: {
    value: Square,
    enumerable: false,
    writable: false,
    configurable: false
  }
});

var square = new Square(3);

console.log(square.getArea());
console.log(square instanceof Square);
console.log(square instanceof Rectangle);

首先子类Square为了创建父类Rectangle的属性,我们在Square函数中以Rectangle.call(this, length, length)的方式进行了调用,其目的就是在子类中创建父类的属性,为了继承父类的方法,我们给Square赋值了新的原型。除了通过Object.create方式,你应该也见过以下方式:

 Square.prototype = new Rectangle();
 Object.defineProperty(Square.prototype,     "constructor", {
    value: Square,
    enumerable: false,
    writable: false,
    configurable: false
 })


Object.create是ES5新增的方法,用于创建一个新对象。被创建的对象会继承另一个对象的原型,在创建新对象时还可以指定一些属性。Object.create指定属性的方式与Object.defineProperty相同,都是采用属性描述符的方式。因此可以看出,通过Object.createnew方式实现的继承其本质上并没有什么区别。但是ES6可以大大简化继承的步骤:

class Rectangle{
    constructor(width, height){
        this.width = width;
        this.height = height;
    }
    
    getArea(){
        return this.width * this.height;
    }
}

class Square extends Rectangle{
    construct(length){
        super(length, length);
    }
}

我们可以看到通过ES6的方式实现类的继承是非常容易的。Square的构造函数中调用super其目的就是调用父类的构造函数。当然调用super函数并不是必须的,如果你默认缺省了构造函数,则会自动调用super函数,并传入所有的参数。
  
  不仅如此,ES6的类继承赋予了更多新的特性,首先extends可以继承任何类型的表达式,只要该表达式最终返回的是一个可继承的函数(也就是讲extends可以继承具有[[Constructor]]的内部属性的函数,比如null和生成器函数、箭头函数都不具有该属性,因此不可以被继承)。比如:

class A{}
class B{}

function getParentClass(type){
    if(//...){
        return A;
    }
    if(//...){
        return B;
    }
}

class C extends getParentClass(//...){
}

可以看到我们通过上面的代码实现了动态继承,可以根据不同的判断条件继承不同的类。
  
  ES6的继承与ES5实现的类继承,还有一点不同。ES5是先创建子类的实例,然后在子类实例的基础上创建父类的属性。而ES6正好是相反的,是先创建父类的实例,然后在父类实例的基础上扩展子类属性。利用这个属性我们可以做到一些ES5无法实现的功能:继承原生对象。

function MyArray() {
  Array.apply(this, arguments);
}

MyArray.prototype = Object.create(Array.prototype, {
  constructor: {
    value: MyArray,
    writable: true,
    configurable: true,
    enumerable: true
  }
});

var colors = new MyArray();
colors[0] = "red";
colors.length  // 0

colors.length = 0;
colors[0]  // "red"

可以看到,继承自原生对象ArrayMyArray的实例中的length并不能如同原生Array类的实例
一样可以动态反应数组中元素数量或者通过改变length属性从而改变数组中的数据。究其原因就是因为传统方式实现的数组继承是先创建子类,然后在子类基础上扩展父类的属性和方法,所以并没有继承的相关方法,但ES6却可以轻松实现这一点:

class MyArray extends Array {
  constructor(...args) {
    super(...args);
  }
}

var arr = new MyArray();
arr[0] = 12;
arr.length // 1

arr.length = 0;
arr[0] // undefined

我们可以看见通过extends实现的MyArray类创建的数组就可以同原生数组一样,使用length属性反应数组变化和改变数组元素。不仅如此,在ES6中,我们可以使用Symbol.species属性使得当我们继承原生对象时,改变继承自原生对象的方法的返回实例类型。例如,Array.prototype.slice本来返回的是Array类型的实例,通过设置Symbol.species属性,我们可以让其返回自定义的对象类型:

class MyArray extends Array {
  static get [Symbol.species](){
    return MyArray;
  }
    
  constructor(...args) {
    super(...args);
  }
}

let items = new MyArray(1,2,3,4);
subitems = items.slice(1,3);

subitems instanceof MyArray; // true

最后需要注意的一点,extends实现的继承方式可以继承父类的静态成员函数,例如:

class Rectangle{
    // ......
    static create(width, height){
        return new Rectangle(width, height);
    }
}

class Square extends Rectangle{
    //......
}

let rect = Square.create(3,4);
rect instanceof Square; // true

相关文章

网友评论

      本文标题:前端分享

      本文链接:https://www.haomeiwen.com/subject/kjigdktx.html