今天开始初学设计模式,在此记录以便往后复习。javascript
一个模式就是一个可重用的方案,可应用于在软件设计中的常见问题,另外一种解释就是一个咱们如何解决问题的模板 - 那些能够在许多不一样的状况里使用的模板。 --w3cschool前端
学习设计模式,面向对象是基础,那就先来复习一下java
数据存放方式决定了访问的方式。git
类定义了全部用于具备某一特征对象的属性。类是抽象的事物,而不是其所描述的所有对象中的任何特定的个体。另外一方面,一个实例是一个类的实例化,是其中的一个成员es6
构造函数(constructor),实例(instance),原型(prototype)自己都是对象。基于原型的语言具备所谓的原型对象的概念,新对象能够从中得到原始的属性。github
在JavaScript
中有一个颇有意思的__proto__
属性用于访问其原型对象,构造函数,实例,原型自己都有__proto__
指向原型对象。最后顺着原型链都会指向Object
这个构造函数,然而Object
的原型对象的原型是null
面试
Student.prototype.study = function() {
console.log('我在学习' + this.subject);
}
复制代码
对于每个实例对象,其方法的函数体是如出一辙的,方法的执行结果只根据其实例对象决定,然而生成的每一个实例都须要生成一个方法去占用一分内存。编程
在ES6
以前建立对象有两种方式,这两种方式在特定的使用场景分别有其优势和缺点, 下面咱们来分别介绍这两种建立对象的方式。后端
咱们经过对象字面量的方式建立两个student
对象,分别是student1
和student2
。设计模式
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
这个构造函数实例化而来的, 咱们可以清楚的知道各实例对象之间的联系。
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
, 对应的是Es6
中Student
类中的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
原型对象,全部的实例都能继承该方法,多个实例的方法指向了同一个内存地址。
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();//静态方法
复制代码
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
复制代码
若是在实例上调用静态方法,会抛出一个错误,表示不存在该方法。
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'
复制代码
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() //我是类方法只可经过函数自己调用
复制代码
面向对象的基本知识差很少都掌握了,接下来就开始学习设计原则!
这是我一个开源的收藏网址的项目
项目地址👉👉点击进入,能够直接设置为浏览器主页或者桌面快捷方式进行使用,本人在用,长期维护。
彻底开源,你们能够随意研究,二次开发。固然仍是十分欢迎你们点个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