【设计模式】学习JS设计模式?先掌握面向对象!

设计模式

今天开始初学设计模式,在此记录以便往后复习。javascript

什么是设计模式

一个模式就是一个可重用的方案,可应用于在软件设计中的常见问题,另外一种解释就是一个咱们如何解决问题的模板 - 那些能够在许多不一样的状况里使用的模板。 --w3cschool前端

我为何要学习设计模式

  • 高级工程师面试必考知识点
  • 写出更好的代码,设计模式是必经之路
  • 掌握设计模式更容易阅读流行框架源码
  • 想要成为项目负责人,从零架构工程,设计模式是基石
  • 没事装个逼
  • ......

我学习设计模式的四个阶段

  • 巩固面向对象相关知识,es6 class语法,封装,继承,多态等
  • 掌握设计原则
  • 掌握设计模式
  • 最后作一个练习案例

面向对象

学习设计模式,面向对象是基础,那就先来复习一下java

面向对象和函数式编程的区别

数据存放方式

  • 对于面向对象,数据存放在对象的属性里面,以及全局变量。
  • 对于函数式,数据存放在各级做用域里面,做用域包括全局做用域。

数据访问方式

数据存放方式决定了访问的方式。git

  • 对于面向对象来讲,访问数据(全局变量除外)须要先获取对象的引用,而后再进行操做(直接访问——公共属性,或者调用成员函数/方法访问——私有属性)
  • 对于函数式,访问数据是直接访问(经过函数入参或者做用域链查找)

基于类的面向对象和基于原型的面向对象

基于类的面向对象

类定义了全部用于具备某一特征对象的属性。类是抽象的事物,而不是其所描述的所有对象中的任何特定的个体。另外一方面,一个实例是一个类的实例化,是其中的一个成员es6

基于原型的面向对象

构造函数(constructor)实例(instance)原型(prototype)自己都是对象。基于原型的语言具备所谓的原型对象的概念,新对象能够从中得到原始的属性。github

JavaScript中有一个颇有意思的__proto__属性用于访问其原型对象,构造函数,实例,原型自己都有__proto__指向原型对象。最后顺着原型链都会指向Object这个构造函数,然而Object的原型对象的原型是null面试

经过原型添加方法

Student.prototype.study = function() {
  console.log('我在学习' + this.subject);
}
复制代码

为何要将方法挂载到原型上?

对于每个实例对象,其方法的函数体是如出一辙的,方法的执行结果只根据其实例对象决定,然而生成的每一个实例都须要生成一个方法去占用一分内存。编程

ES6以前的面向对象

ES6以前建立对象有两种方式,这两种方式在特定的使用场景分别有其优势和缺点, 下面咱们来分别介绍这两种建立对象的方式。后端

  • 第一种是使用对象字面量的方式
  • 第二种是使用构造函数的方式

使用对象字面量的方式

咱们经过对象字面量的方式建立两个student对象,分别是student1student2设计模式

var student1 = {
  name: '小呆',
  age: 22,
  subject: '前端开发'
};

var student2 = {
  name: '小傻',
  age: 22,
  subject: '后端开发'
};
复制代码

对象字面量的方式在建立单一简单对象的时候是很是方便的。可是,它也有其缺点:

  • 在生成多个实例对象时, 咱们须要每次重复写不少属性,写起来特别的麻烦。
  • 虽然都是学生的对象, 可是看不出两个对象之间有什么联系。

使用构造函数的方式

构造函数就其实就是一个普通的函数,当对构造函数使用new进行实例化时,会将其内部this的指向绑定到实例对象上.

咱们来建立一个Student构造函数(构造函数一般使用大写字母开头,和普通函数作区分)。

function Student (name, age, subject) {
  this.name = name;
  this.age = age; 
  this.subject = subject;
  console.log(this);
}
复制代码

咱们在构造函数中打印出this的指向。由于构造函数其实就是一个普通的函数, 那么咱们即可以使用普通函数的调用方式去调用Student

Student('小呆', 22, '前端开发'); //window{}
复制代码

能够看到,采用普通方式调用Student时, this的指向是window。下面使用new来实例化该构造函数, 生成一个实例对象student1

let student1 = new Student('小呆', 22, '前端开发'); 
//Student {name: "阿辉", age: 22, subject: "前端开发"}
复制代码

能够看到,当咱们采用new生成实例化对象student1时, this再也不指向window, 而是指向的实例对象自己。

上面的就是采用构造函数的方式生成实例对象的方式, 而且当咱们生成其余实例对象时,因为都是采用Student这个构造函数实例化而来的, 咱们可以清楚的知道各实例对象之间的联系。

new都作了什么?

  • 建立一个新对象
  • 分配内存,将构造函数的做用域赋值给新对象,即this指向这个新对象
  • 执行构造函数中的代码
  • 返回新对象

ES6中的面向对象

ES6中提供了基于类class的语法。但class本质上是ES6提供的一颗语法糖,但实际上JavaScript是一门基于原型的面向对象语言。

ES6中使用class来建立对象

//定义类
class Student {
  //构造方法
  constructor(name, age, subject) {
    this.name = name;
    this.age = age;
    this.subject = subject;
  }

  //类中的方法
  study(){
    console.log('我在学习' + this.subject);
  }
}
//实例化类
let student3 = new Student('小痴', 24, '前端开发');
student3.study(); //我在学习前端开发
复制代码

上面的代码定义了一个Student类, 能够看到里面有一个constructor方法, 这就是构造方法,而this关键字则表明实例对象。也就是说,ES5中的构造函数Student, 对应的是Es6Student类中的constructor方法。

Student类除了构造函数方法,还定义了一个study方法。方法之间不要用逗号分隔,加了会报错。并且,类中的方法所有是定义在原型上的,咱们能够用下面的代码进行验证。

console.log(student3.__proto__.study === Student.prototype.study); //true
console.log(student3.hasOwnProperty('study')); // false
复制代码

上面的第一行的代码中, student3.__proto__是指向的原型对象,其中Student.prototype也是指向的原型的对象,结果为true就能很好的说明类中的方法所有是定义在原型上的。

第二行代码是验证student3实例中是否有study方法,结果为false, 代表实例中没有study方法,这也更好的说明了上面的结论。

而将study方法挂载到prototype原型对象,全部的实例都能继承该方法,多个实例的方法指向了同一个内存地址。

实例与类

javascript中实例方法与类方法的区别

  • 类属性(静态属性):经过类直接访问,不须要声明类的实例来访问
  • 类方法(静态方法):经过类直接访问,不须要声明类的实例来访问
  • 实例属性(动态属性):不能经过类直接访问,须要经过该类声明的实例才能访问
  • 实例方法(动态方法):不能经过类直接访问,须要经过该类声明的实例才能访问
Person = function(){
}
Persion.sex = "woman";  //类属性
Person.eat= function(){  //类方法
  alert("eat");
}
Person.prototype.age = 10; //实例属性
Person.prototype.dance = function(){ //实例方法
  alert("dance");
}
const person = new Person();
person.age;//实例属性
person.dance();//实例方法
Person.sex;//静态属性
Person.eat();//静态方法
复制代码

ES6中的实例与类

静态属性和实例属性

静态属性须要使用static关键字

class Foo {
  static num = 1;
    age = 2
}
const foo = new Foo();
console.log(Foo.num);// 1
console.log(foo.num);// undefined
console.log(Foo.age);// undefined
console.log(foo.age);// 2
复制代码

若是在实例上调用静态方法,会抛出一个错误,表示不存在该方法。

静态方法须要使用static关键字

class Foo {
  static classMethod() {
    return 'hello';
  }
}

Foo.classMethod() // 'hello'

const foo = new Foo();
foo.classMethod()
// TypeError: foo.classMethod is not a function

复制代码

静态方法能够与非静态方法重名

若是静态方法包含this关键字,这个this指的是类,而不是实例。

class Foo {
  static bar() {
    this.baz();
  }
  static baz() {
    console.log('hello');
  }
  baz() {
    console.log('world');
  }
}
const foo = new Foo();
Foo.bar() // hello
foo.bar() // world
复制代码

父类的静态方法,能够被子类继承。

class Foo {
  static classMethod() {
    return 'hello';
  }
}

class Bar extends Foo {
}

Bar.classMethod() // 'hello'
复制代码

静态方法也是能够从super对象上调用的

class Foo {
  static classMethod() {
    return 'hello';
  }
}

class Bar extends Foo {
  static classMethod() {
    return super.classMethod() + ', too';
  }
}

Bar.classMethod() // "hello, too"
复制代码

封装,继承,多态

面向对象三大特性:封装继承多态

封装

封装就是将数据操做的细节隐藏起来,只暴露对外的接口,外界调用端不须要也不可能知道细节,只能经过经过对外暴露的接口来访问该对象

class School {
  // 构造器:建立对象完成初始化操做
  constructor(id, name) {
    this.id = id
    this.name = name
  }
  // 实例方法
  schoolName() {
    console.log(this.name)
  }
  // 类方法
  static schoolOnly() {
    console.log('我是类方法只可经过函数自己调用')
  }
}
复制代码

继承

继承表示子类继承父类,子类除了拥有父类全部的特征之外,还有一些更具体的特性;

class Student extends School {
  constructor(sId, sName, id, name) {
    super(id, name)
    this.sId = sId
    this.sName = sName
  }
  studentName() {
    console.log(this.sName)
  }
  say() {
    console.log('I am a student')
  }
}
复制代码

多态

由继承产生的多个不一样的类,对同一个方法能够有不一样的响应,好比学生和老师均可以继承自学校,可是它们分别实现了本身的say方法;此时针对某一个实例,咱们无需了解他是学生仍是老师,咱们能够直接调用say方法,程序会自动判断应该如何执行这个方法;

class Teacher extends School {
  constructor(tId, tName, id, name) {
    super(id, name)
    this.tId = tId
    this.tName = tName
  }
  TeacherName() {
    console.log(this.tName)
  }
  say() {
    console.log('I am a teacher')
  }
}
测试
let school = new School(1, '第一小学')
let student = new Student(10, 'Daming', 1, '第一小学')
let teacher = new Teacher(100, 'MrLi', 1, '第一小学')
console.log(student) //Object Student {id: 1, name: "第一小学", sId: 10, sName: "Daming"}
console.log(teacher) //Object Teacher {id: 1, name: "第一小学", tId: 100, tName: "MrLi"}
student.studentName() //Daming
student.schoolName()  //第一小学
student.say() //I am a student
teacher.say() //I am a teacher
School.schoolOnly() //我是类方法只可经过函数自己调用
复制代码

面向对象的基本知识差很少都掌握了,接下来就开始学习设计原则!

打个广告

16.gif 这是我一个开源的收藏网址的项目

项目地址👉👉点击进入,能够直接设置为浏览器主页或者桌面快捷方式进行使用,本人在用,长期维护。

彻底开源,你们能够随意研究,二次开发。固然仍是十分欢迎你们点个Star⭐⭐⭐
👉👉源码连接(gitee)       👉👉源码连接(github)

连接整合

🔊项目预览地址(GitHub Pages):👉👉alanhzw.github.io

🔊项目预览备用地址(本身的服务器):👉👉warbler.duwanyu.com

🔊源码地址(gitee):👉👉gitee.com/hzw_0174/wa…

🔊源码地址(github):👉👉github.com/alanhzw/War…

🔊个人博客:👉👉www.duwanyu.com

相关文章
相关标签/搜索