美文网首页让前端飞程序员
web前端进阶之Javascript设计模式面向对象篇

web前端进阶之Javascript设计模式面向对象篇

作者: jia林 | 来源:发表于2018-07-30 21:27 被阅读53次

前言:在此说明Javascript设计模式所讲内容和知识点来自双越老师(wangEditor富文本开源作者)的视频,内容通俗易懂,受益匪浅,结合自己的学习心得整理成笔记,与大家分享,愿在前端的道路上越走越远.....

从“写好代码”到“设计代码”的过程,不仅是技术的提升,更是编程思维的提升,而这其中最关键的就是设计模式,是否理解并掌握设计模式,也是衡量程序员能力的标准之一。

学习前提

  • 使用过jquery类库
  • 有ES6基础,用过node.js和npm
  • 对vue、react有所了解

搭建开发环境

代码是基于ES6的,需要webpack和Babel进行转义

1、初始化npm环境

  • npm init 会出现提示,一直按回车,最后输入yes即可


    image.png

2、安装webpack(当下流行的打包工具)

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 私有的

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、编程应该是 简单&抽象,简单的前提是抽象,抽象后才简单

关于抽象:抽取事物的共同特征就是抽取事物的本质特征,舍弃非本质的特征。所以抽象的过程也是一个裁剪的过程。在抽象时,同与不同,决定于从什么角度上来抽象。抽象的角度取决于分析问题的目的。

相关文章

网友评论

    本文标题:web前端进阶之Javascript设计模式面向对象篇

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