前言:在此说明Javascript设计模式所讲内容和知识点来自双越老师(wangEditor富文本开源作者)的视频,内容通俗易懂,受益匪浅,结合自己的学习心得整理成笔记,与大家分享,愿在前端的道路上越走越远.....
从“写好代码”到“设计代码”的过程,不仅是技术的提升,更是编程思维的提升,而这其中最关键的就是设计模式,是否理解并掌握设计模式,也是衡量程序员能力的标准之一。
学习前提
- 使用过jquery类库
- 有ES6基础,用过node.js和npm
- 对vue、react有所了解
搭建开发环境
代码是基于ES6的,需要webpack和Babel进行转义
1、初始化npm环境
-
npm init 会出现提示,一直按回车,最后输入yes即可
image.png
2、安装webpack(当下流行的打包工具)
- 普通安装:npm install webpack webpack-cli --save-dev
- 淘宝镜像(http://npm.taobao.org/)安装:npm install webpack webpack-cli --save-dev --registry=https://registry.npm.taobao.org
3、安装webpack-dev-server(是webpack集成本地服务的一个环境,写完代码需要在本地预览,修改文件后可以自动刷新)
- npm install webpack-dev-server html-webpack-plugin --save-dev
4、安装babel(解析ES6语法)
- npm install babel-core babel-loader babel-polyfill babel-preset-es2015 babel-preset-latest babel-plugin-transform-decorators-legacy --save-dev
5、创建文件
-
文件目录结构
image.png -
创建webpack.dev.config.js文件进行配置
// 引入 node.js path文件
const path = require('path')
// require 网页模板插件
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
// 入口文件
entry: './src/index.js',
/*
出口文件
@param __dirname 当前目录
@param filename 目录文件名
*/
output: {
path: __dirname,
filename: './release/bundle.js' // release 文件夹运行时会自动创建
},
/*
plugins插件列表,是一个数组
@param HtmlWebpackPlugin html模板
*/
plugins: [
new HtmlWebpackPlugin({
template: './index.html' // 自动生成的bundle.js 会自动注入到index.html
})
],
// 本地开发环境服务器
devServer: {
// 需要获取文件,从本地release文件夹里面获取
contentBase: path.join(__dirname, "./release"), // 根目录
open: true, // 自动打开浏览器
port: 9000, // 端口
},
// 模块
module: {
// 规定
rules: [{
// 检验js文件
test: /\.js?$/,
// 忽略的文件
exclude: /(node_modules)/,
// 进行babel处理
loader: 'babel-loader'
}]
}
}
- 创建.babelrc文件
{
"presets": ["es2015", "latest"],
"plugins": ["transform-decorators-legacy"]
}
- 更改package.json文件(json文件里面不能添加注释)
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
// 运行webpack命令,将配置指向webpack.dev.config.js文件,生成开发模式--mode development
"dev": "webpack-dev-server --config ./webpack.dev.config.js --mode development"
},
-
运行npm run build,自动生成release文件
image.png -
运行npm run dev,自动打开浏览器
image.png
面向对象
概念
1、类,即模板,通过模板实例化很多对象,和es5的构造函数原理相同,里面放属性和方法
//1、创建一个人(People)的模板
//2、人具有姓名(name),年龄(age)的属性
//3、人可以执行动作吃饭(eat())、讲话(speak())等方法,方法里面执行逻辑操作
//1、创建一个人(People)的模板
class People {
constructor(name, age) {
//2、人具有姓名(name),年龄(age)的属性
this.name = name,
this.age = age
}
//3、人可以执行动作吃饭(eat())、讲话(speak())等方法,方法里面执行逻辑操作
eat() {
console.log('this is eat')
}
speak() {
console.log(this.name, this.age)
}
}
2、对象(实例),通过类可以赋值给很多对象
// 1、创建一个叫zhang的对象
// 2、因为人的constructor里面需要传参数,所以将zhang的姓名(name)和年龄(age)传进去
// 3、zhang便有人(People)的方法,可以吃(eat()),可以说(speak())
const zhang = new People('zhang', 20)
console.log(zhang.eat()) // zhangeat
console.log(zhang.speak()) // 20
// 创建实例2
const wang = new People('wang', 30)
console.log(wang.eat())
console.log(wang.speak())
三要素:继承、封装、多态
1、继承,子类继承父类
- 父类
//1、创建一个人(People)的模板,相当于父类
class People {
constructor(name, age) {
//2、人具有姓名(name),年龄(age)的属性
this.name = name,
this.age = age
}
//3、人可以执行动作吃饭(eat())、讲话(speak())等方法,方法里面执行逻辑操作
eat() {
console.log(`${this.name}eat`)
}
speak() {
console.log(`${this.age}`)
}
}
- 子类(学生)
// 实现子类继承父类
// 人分为好多种,比如学生、白领,通过extends继承人的属相和方法
class Student extends People {
constructor(name,age,schoolNum){
// 通过关键字super将name,age交给父类处理
super(name,age)
// 自己处理学号
this.schoolNum = schoolNum
}
// 因为是学生,具有学习的方法
study(){
console.log(`学号为${this.schoolNum}的学生学习`)
}
}
// 创建实例(学生jialin)
const jiaLin = new Student('jialin',20,20120102)
// jialin具有学习的方法,同时也具有人(People)的吃饭(eat())方法和说话(speak())方法
console.log(jiaLin.study())
console.log(jiaLin.eat())
console.log(jiaLin.speak())
- 子类(白领)
// 创建白领这个类,继承人的属性和方法
class whiteCollar extends People{
constructor(name,age,workNum){
// 通过关键字super将name,age交给父类构造函数处理
super(name,age)
// 自己处理工号
this.workNum = workNum
}
// 因为是白领,具有工作的方法
work(){
console.log(`工号号为${this.workNum}的人工作`)
}
}
// 创建实例(白领mumu)
const mumu = new whiteCollar('mumu',30,123)
// mumu具有工作的方法,同时也具有人(People)的吃饭(eat())方法和说话(speak())方法
console.log(mumu.work()) // 工号号为123的人工作
console.log(mumu.eat())
console.log(mumu.speak())
通过以上案列可以知道子类(学生、白领)不仅继承(拥有)了父类(人People)的属性和方法,还有属于自己的方法(学习study、工作work),自己也可以定义属性和方法
- 总结
1、People 是父类,公共的,不仅仅服务于Student和whiteCollar
2、继承可将公共方法抽离出来,提高复用,减少冗余,这是软件设计最基础和最高效的方式
2、封装,数据的权限和保密。简单来说,将对象里面的某些属性和方法不想让别人看见,有一些是可以开放出去(javascript不是很明显,typescript[是js的超集]具有明显的特征,如public、private、protexted关键字)
public 完全开放
protectted 受保护的
private 私有的
- 演示的为typescript语法,代码可以在此运行http://www.typescriptlang.org/play/index.html
class People {
// ts中的属性要先声明
name
// 默认的是public 相当于public age
age
// 定义protected 受保护的属性,只有自己或者子类可以访问
protected weigth
// 定义private 私有的属性,别人用不了
private girlFriend
constructor(name, age) {
this.name = name
this.age = age
// 给weigth赋值
this.weigth = 120
this.girlFriend = "zdy"
}
eat() {
console.log(`${this.name}eat`)
}
}
class Student extends People{
schoolNum
constructor(name,age,schoolNum) {
super(name, age)
this.schoolNum = schoolNum
}
getWeight() {
// 获取weight,这个属性来自父类,对子类是开放的
console.log(this.weigth)
// 这里获取不到 girlFriend,是父类私有的,不对外开放
}
}
// 创建实例
let mumu = new Student('mumu',20,201210)
mumu.getWeight() //120
// console.log(mumu.girlFriend) 编译是会报错,因为girFriend是私有属性
- 总结
1、减少耦合,不该外露的不外露
2、利于数据、接口的权限管理
3、es6目前不支持,一般认为_开头的属性是private,比如var _num = 20
3、多态,同一接口的不同实现,简单来讲就是父类定义一个接口,子类实现不同的功能
- 代码演示
class People{
constructor(name){
this.name = name
}
saySomething(){
}
}
// a继承 People
class A extends People {
constructor(name){
super(name)
}
saySomething(){
alert(`${this.name}`)
}
}
// b继承 People
class B extends People {
constructor(name){
super(name)
}
saySomething(){
alert(`${this.name}`)
}
}
// a、b使用父类People的saySomething()方法,展示不一样的结果,此为多态
let a = new A('jialin')
alert(a.saySomething())
let b = new A('mumu')
alert(b.saySomething())
- 总结
1、保持子类的开放性和灵活性
2、面向接口编程(不用管子类如何实现,就看父类有多少接口)
面向对象在前端实际应用
1、可以理解jquery就是个class
2、$('p')是jquery的一个实例
- 代码演示
class jQuery {
constructor(selector) {
// 获取数组的slice
let slice = Array.prototype.slice
// 获取节点,利用slice.call将其结果返回给一个数组,因为可能是多个dom节点
let dom = slice.call(document.querySelectorAll(selector))
// 获取dom的长度
let len = dom ? dom.length : 0
// 进行循环
for (let i = 0; i < len; i++) {
// 将dom的数组元素赋值给this也就是实例的元素,元素的k就是数组的k,0,1,2...
this[i] = dom[i]
}
// 赋值数组的长度
this.length = len
this.selector = selector || ''
}
append(node) {
//...
}
addClass(name) {
//...
}
html(data) {
//...
}
// 此处省略若干 API
}
// 入口
window.$ = function(selector) {
// 这里其实就是工厂模式
return new jQuery(selector)
}
console.log($('p'))
为什么使用面向对象
1、程序的执行离不开顺序、判断、循环操作,也就是将其结构化
2、面向对象就是将零散的数据结构化
3、对于计算机而言,结构化的才是最简单的
4、编程应该是 简单&抽象,简单的前提是抽象,抽象后才简单
关于抽象:抽取事物的共同特征就是抽取事物的本质特征,舍弃非本质的特征。所以抽象的过程也是一个裁剪的过程。在抽象时,同与不同,决定于从什么角度上来抽象。抽象的角度取决于分析问题的目的。
网友评论