JacaScript进阶之设计模式系列1

本文探讨了JavaScript中的面向对象编程,包括封装、继承的不同方式,如原型链继承、借用构造函数继承、组合继承。还介绍了简单工厂模式和安全模式类的概念,强调在团队项目中减少全局变量的使用,以及在不同需求下如何有效地复用代码。最后,讨论了安全的工厂方法在创建多类对象时避免耦合的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


本系列文章主要是JavaScript设计模式一书的笔记,最初看到这本书是在几个月前,但由于当时对JS基础了解的不多,看的过程中只感觉很有用,但很多底层的语法都不清楚。最近在把vue源码学完后,再来看,里面的内容已经都了解了,现在主要就是把书里的思路等都记录下来,毕竟看书不去实践总是感觉很空。

JS面向对象编程

这部分主要是讲js中对象的创建,我把这些整理起来放在了
JS面试系列1中的 8.创建一个对象

封装

js中创建一个类并赋值有两种方法
var Book = function(id,bookname,price) {
	this.id = id;
	this.bookname=bookname;
	this.price = price;
}

也可以通过原型添加属性和方法,但以下两种方式不能混用
Book.prototype.display = function(){}
Book.prototype = {display:function(){}}
一种是一一为对原型对象属性赋值,另一种是讲一个对象赋值给类的原型对象

问题: 通过this添加的属性和方法同在Prototype中添加的属性和方法有什么区别?

  1. 通过this添加,是在当前对象上添加的, 然而js是一种基于原型prototype的语言,所以每创建一个对象时,都有一个原型指向其继承的属性、方法
  2. 这样通过prototype继承的方法并不是对象自身的,所以在使用时,需要通过prototype一级一级来查找
  3. 同时,this定义的属性方法是对象自身拥有的,这样,通过类每创建一个新对象时,this指向的属性和方法都会得到创建,而通过prototype继承的属性和方法不会再次创建

问题:JS如何实现私有属性、私有方法共有属性等

  1. 由于JS的函数级作用域,声明在函数内部的变量以及方法在外界是访问不到的,可通过此特性创建类的私有变量和方法
  2. 在函数内部通过this创建的属性和方法,在类创建对象时,每个对象自身都有一份,并且可以被外部访问到。因此可以通过this创建的属性是对象共有属性和共有方法

问题:在类的外部通过点语法定义的属性和方法以及在外部通过prototype定义的尚需经和方法有什么作用?

  1. 通过new关键字创建新对象时,由于类外面通过点语法添加的属性和方法没有执行到,所以新创建的对象中无法获取他们,但是可以通过类来使用。
  2. 因此在类外面通过点语法定义的属性以及方法被称为类的静态共有属性和类的静态共有方法
  3. 而通过prototype创建的属性或者方法在类的实例对象中是可以通过this访问到的,所以将prototype对象中的属性和方法称为共有属性和共有方法

通过New关键字创建的对象实质是对新对象this的不断赋值,并将prototype指向类的prototype所指向的对象
而类的构造函数外面通过点语法定义的属性和方法是不会添加到新创建的对象上去的,测试代码如下

var Book = function (id,name,price) {
	// 私有属性
	var num = 1;
	// 私有方法
	function checkId(){}
	// 特权方法
	this.getName = function(){};
	....
	// 对象公有属性
	this.id=id;
	// 对象公有方法
	this.copy = function(){}
	// 构造器
	this.setName(name)
}


// 类的静态公有属性(对象不能访问)
Book.isChinese = true;
// 类的静态公有方法(对象不能访问)
Book.resetTime = function(){}
Book.prototype={
	// 公有属性
	isJSBook:false,
	display:function(){}
}


var b = new Book(11,'JS设计模式',50);
console.log(b.num);// undefined
console.log(b.isJSbook) // false
console.log(b.id);//11
console.log(b.isChinese)//undefined
console.log(Book.isChinese);// true
Book.resetTime()

继承

这里的继承部分我就用B站上的视频教程中的笔记。笔记我也直接记在代码里了

原型链继承

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>01_原型链继承</title>
</head>
<body>
<!--
方式1: 原型链继承
  1. 套路
    1. 定义父类型构造函数
    2. 给父类型的原型添加方法
    3. 定义子类型的构造函数
    4. 创建父类型的对象赋值给子类型的原型
    5. 将子类型原型的构造属性设置为子类型
    6. 给子类型原型添加方法
    7. 创建子类型的对象: 可以调用父类型的方法
  2. 关键
    1. 子类型的原型为父类型的一个实例对象

-->
<script type="text/javascript">
  // 1. 定义父类型构造函数
  function Supper() {   this.supProp = 'Supper property'  }
 
  // 2. 给父类型的原型添加方法
  Supper.prototype.showSupperProp = function () {
    console.log(this.supProp)
  }

  // 3. 定义子类型的构造函数
  function Sub() {
    this.subProp = 'Sub property'
  }
  
	 // 6. 子类型添加原型方法
  Sub.prototype.showSubProp = function () {
    console.log(this.subProp)
  }

  // =====
  // 4. 创建父类型的对象赋值给子类型的原型
  Sub.prototype = new Supper()
  // 5. 让子类型的原型的constructor指向子类型
  Sub.prototype.constructor = Sub
  // =====

 
 
  // 创建子类型对象
  var sub = new Sub()
  sub.showSupperProp()
  sub.showSubProp()
  
  console.log(sub)  // Sub
</script>
</body>
</html>

借用构造函数继承

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>02_借用构造函数继承</title>
</head>
<body>
<!--
方式2: 借用构造函数继承(假的)
1. 套路:
  1. 定义父类型构造函数
  2. 定义子类型构造函数
  3. 在子类型构造函数中调用父类型构造
2. 关键:
  1. 在子类型构造函数中通用call()调用父类型构造函数
-->
<script type="text/javascript">
//1. 定义父类型构造函数
  function Person(name, age) {
    this.name = name
    this.age = age
  }
  // 2. 定义子类型构造函数
  function Student(name, age, price) {
	  // 3. 在子类型构造函数中调用父类型构造
    Person.call(this, name, age)  // 相当于: this.Person(name, age)
    /*this.name = name
    this.age = age*/
    this.price = price
  }

  var s = new Student('Tom', 20, 14000)
  console.log(s.name, s.age, s.price)

</script>
</body>
</html>

组合继承

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>03_组合继承</title>
</head>
<body>
<!--
方式3: 原型链+借用构造函数的组合继承
1. 利用原型链实现对父类型对象的方法继承
2. 利用super()借用父类型构建函数初始化相同属性
-->
<script type="text/javascript">
  function Person(name, age) {
    this.name = name
    this.age = age
  }
//  1. 利用原型链实现对父类型对象的方法继承
  Person.prototype.setName = function (name) {
    this.name = name
  }

  function Student(name, age, price) {
//  2. 利用super()借用父类型构建函数初始化相同属性
    Person.call(this, name, age)  // 为了得到属性
    this.price = price
  }
  Student.prototype = new Person() // 为了能看到父类型的方法
  Student.prototype.constructor = Student //修正constructor属性
  Student.prototype.setPrice = function (price) {
    this.price = price
  }

  var s = new Student('Tom', 24, 15000)
  s.setName('Bob')
  s.setPrice(16000)
  console.log(s.name, s.age, s.price)

</script>
</body>
</html>

简单工程模式

把特征相同的类封装在一个函数中,这样只需要通过这个函数就可以创建需要的对象。例子如下


// 篮球基类
var BasketBall = function (){
	this.intro='篮球'
}
BasketBall.prototype = {
	getMember:function (){
		console.log('每个队伍需要5名队员')
	},
	getBallSize:function () {
		console.log('篮球有特定大小')
	}
}
// 足球基类
var FootBall = function (){
	this.intro='篮球'
}
FootBall.prototype = {
	getMember:function (){
		console.log('每个队伍需要5名队员')
	},
	getBallSize:function () {
		console.log('篮球有特定大小')
	}
}

var SportsFactory = function (name) {
	switch (name){
		case 'NAB':
			return new BasketBall();
		case 'wordCup':
			return new FootBall();
	}
}

var football = SportsFactory('wordCup')
console.log(football)
console.log(football.intro)
football.getMember()

当函数中的几个类有相同的内容时,是可以抽象提取出来共用的,可以用简单工厂的方式实现
例如创建一些书,这些书共有的属性可以封装到一个类中,这里为了简便我就用上面的已有的代替,然后不同的属性可以作为参数传递进来处理

	function createBook(name,time,type) {
		var o = new BasketBall()
		o.name = name
		o.time = time
		o.type = type
		o.show = function (){
			console.log(this.name)
		}
		return o
	}

	var book1 = createBook('js',2134,'js')
	book1.show() // js
	console.log(book1.intro) //篮球
  book1.getMember()//每个队伍需要5名队员

第一种是通过类的实例化对象创建的, 第二种是通过创建一个新对象然后包装增强其属性和功能来实现的。
差异性

  1. 前面的通过类创建对象,如果这些类继承同一父类,那么其父类上的原型方法可以公用
  2. 而后面的寄生方式创建的对象都是一个个体,他们的方法无法共用

记录

团队项目时尽量少创建全局变量
对于同一类对象在不同需求中的重复性使用,很多时候不需要重复创建,代码复用是面向对象编程的一条准则
通过简单工程来创建一些对象,可以让这些对象共用一些代码资源而又私有一些资源
使用场合限制在创建单一对象

通过对产品类的抽象使其创建爱你业务主要负责用于创建多类产品的实例

安全模式类

安全模式类式防止忽略new关键字时造成的错误

var Demo = function (){
  	if (!(this instanceof Demo)){
  		return new Demo()
    }
  }
  Demo.prototype = {
  	show:function (){
		  console.log('成功获取')
    }
  }
  var d = Demo();
  d.show();

安全的工厂方法

	// 安全模式创建类的工厂
	var Factory = function (type, content) {
		if (this instanceof Factory) {
			var s = new this[type](content);
			return s
		} else {
			return new Factory(type, content);
		}
	}

	// 工厂原型中设置创建所有类型数据对象的基类
  Factory.prototype = {
		Java: function (content){

    },
    UI:function (content) {
			this.content = content
      (function (content){
      	var div = document.createElement('div');
      	div.innerHTML = content
        //.....
      })(content)
    },
    //....
  }

  var data = [
    {type:'JavaScript',content:'js'},
    {type:'JavaScript',content:'js'},
    {type:'JavaScript',content:'js'}
  ]
	for (let i = 0; i < data.length; i++) {
    Factory(data[i].type, data[i].content)
	}

记录
对于创建多类对象,之前的简单工厂模式就不大适用了。
通过工厂方法模式我们可以轻松创建多个类的实例对象,这样的工厂方法对象在创建对象的方法也避免了使用者与对象类之间的耦合,用户不必关心创建该对象的具体类

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值