设计模式(7)[JS版]-JavaScript设计模式之原型模式如何实现???
1.什么是原型模式
原型模式(prototype)是指用原型实例指向创建对象的种类,并且通过拷贝这些原型创建新的对象。 原型模式不单是一种设计模式,也被称为一种编程泛型。 从设计模式的角度讲,原型模式是用于创建对象的一种模式。我们不再关心对象的具体类型,而是找到一个对象,然后通过克隆来创建一个一模一样的对象。在其他语言很少使用原型模式,但是JavaScript作为原型语言,在构造新对象及其原型时会使用该模式。
2 参与者
原型模式的主要参与者有:
客户端( Client) : 通过要求一个原型克隆自己来创建一个新的对象。
原型( Prototype) :创建一个接口来克隆自己
克隆( Clones ) :正在创建的克隆对象
3 实例讲解
在示例代码中,我们有一个CustomerPrototype对象,它可以克隆给定原型对象(Customer)。它的构造函数接受一个Customer类型的原型,然后调用克隆方法生成一个新的Customer对象,其对象属性值使用原型对象的值进行初始化。
这是原型模式的经典实现,但JavaScript可以使用其内置的原型更有效地实现这一功能,后边我们将会修改这一代码。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JS原型模式:公众号AlbertYang</title>
</head>
<body>
</body>
<script>
//客户原型类
function CustomerPrototype(proto) {
this.proto = proto;
this.clone = function() {
var customer = new Customer();
customer.name = proto.name;
customer.age = proto.age;
return customer;
};
}
//客户类
function Customer(name, age) {
this.name = name;
this.age = age;
this.say = function() {
console.info("%c%s", "color:red;font-size:18px", "姓名: " + this.name + ", 年龄: " + this.age);
};
}
function run() {
var proto = new Customer("张三", 18);
var prototype = new CustomerPrototype(proto);
var customer = prototype.clone();
customer.say();
}
run();
</script>
</html>
4 使用 Object.create实现原型模式
在现有的文献里查看原型模式的定义,没有针对JavaScript的,你可能发现很多讲解的都是关于类的,但是现实情况是基于原型继承的JavaScript完全避免了类(class)的概念。我们只是简单从现有的对象进行拷贝来创建对象。
在ECMAScript5标准中提出,使用Object.create方法来创建指定的对象,其对象的prototype有指定的对象(也就是该方法传进的第一个参数对象),也可以包含其他可选的指定属性。例如Object.create(prototype, optionalDescriptorObjects)。
4.1 Object.create()的用法
Object.create()方法用于创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。
语法:
Object.create(proto[, propertiesObject])
参数:
proto
新创建对象的原型对象。
propertiesObject
可选。如果没有指定为 undefined
,则是要添加到新创建对象的不可枚举(默认)属性(即其自身定义的属性,而不是其原型链上的枚举属性)对象的属性描述符以及相应的属性名称。这些属性对应Object.defineProperties()
的第二个参数。
返回值:一个新对象,带着指定的原型对象和属性。
4.2 用 Object.create实现继承
下面的例子演示了如何使用Object.create()来实现类式继承。这是所有JavaScript版本都支持的单继承。
4.2.1 单继承
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JS原型模式:公众号AlbertYang</title>
</head>
<body>
</body>
<script>
// 父类
function Shape() {
this.x = 0;
this.y = 0;
}
// 父类的方法
Shape.prototype.move = function(x, y) {
this.x += x;
this.y += y;
console.info('Shape moved.');
};
// 子类
function Rectangle() {
Shape.call(this); // call方法用来调用父类Shape的构造函数
}
// 子类继承父类
Rectangle.prototype = Object.create(Shape.prototype);
//重新指定构造函数
Rectangle.prototype.constructor = Rectangle;
console.log(Rectangle);
var rect = new Rectangle();
console.log(rect);
console.log('rect是Rectangle的实例吗?',
rect instanceof Rectangle); // true
console.log('rect是Shape的实例吗?',
rect instanceof Shape); // true
rect.move(1, 1); // 输出, 'Shape moved.'
</script>
</html>
4.2.2 多继承
如果你希望能继承到多个对象,则可以使用混入的方式。
function MyClass() {
SuperClass.call(this);
OtherSuperClass.call(this);
}
// 继承一个类
MyClass.prototype = Object.create(SuperClass.prototype);
// 混合其它类
Object.assign(MyClass.prototype, OtherSuperClass.prototype);
// 重新指定constructor
MyClass.prototype.constructor = MyClass;
MyClass.prototype.myMethod = function() {
// 。。。。。
};
Object.assign 会把 OtherSuperClass原型上的函数拷贝到 MyClass原型上,使 MyClass 的所有实例都可以使用 OtherSuperClass 的方法。Object.assign 是在 ES2015 引入的,且可用 polyfilled。要支持旧浏览器的话,可用使用 jQuery.extend() 或者 _.assign()。
4.3 使用propertyObject参数
<script>
var o;
// 创建一个原型为null的空对象
o = Object.create(null);
o = {};
// 上面的一句就相当于:
o = Object.create(Object.prototype);
o = Object.create(Object.prototype, {
// foo会成为所创建对象的数据属性
foo: {
writable: true,
configurable: true,
value: "hello"
},
// bar会成为所创建对象的访问器属性
bar: {
configurable: false,
get: function() {
return 10
},
set: function(value) {
console.log("Setting `o.bar` to", value);
}
}
});
function Constructor() {}
o = new Constructor();
// 上面的一句就相当于:
o = Object.create(Constructor.prototype);
// 如果在Constructor函数中有一些初始化代码,Object.create不能执行那些代码
// 创建一个以另一个空对象为原型,且拥有一个属性p的对象
o = Object.create({}, {
p: {
value: 42
}
})
// 省略了的属性特性默认为false,所以属性p是不可写,不可枚举,不可配置的:
o.p = 24
console.log(o.p) //42
o.q = 12
for (var prop in o) {
console.log(prop); //q
}
delete o.p //false
//创建一个可写的,可枚举的,可配置的属性p
o2 = Object.create({}, {
p: {
value: 42,
writable: true,
enumerable: true,
configurable: true
}
});
</script>
4.4 Polyfill
Polyfill 是一块代码(通常是 Web 上的 JavaScript),用来为旧浏览器提供它没有原生支持的较新的功能。比如说 polyfill 可以让 IE7 使用 Silverlight 插件来模拟 HTML Canvas 元素的功能,或模拟 CSS 实现 rem 单位的支持,或 text-shadow,或其他任何你想要的功能。
下边这个 polyfill 涵盖了Object.create主要的应用场景,它创建一个已经选择了原型的新对象,但没有把第二个参数考虑在内。尽管在 ES5 中 Object.create支持设置为[[Prototype]]为null,但因为JS以前一些老版本的限制,此 polyfill 无法支持该特性。
if (typeof Object.create !== "function") {
Object.create = function (proto, propertiesObject) {
if (typeof proto !== 'object' && typeof proto !== 'function') {
throw new TypeError('Object prototype may only be an Object: ' + proto);
} else if (proto === null) {
throw new Error("This browser's implementation of Object.create is a shim and doesn't support 'null' as the first argument.");
}
if (typeof propertiesObject !== 'undefined') throw new Error("This browser's implementation of Object.create is a shim and doesn't support a second argument.");
function F() {}
F.prototype = proto;
return new F();
};
}
4.5 改写原型模式实现
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JS原型模式:公众号AlbertYang</title>
</head>
<body>
</body>
<script>
// 因为不是构造函数(类),所以不用大写
var customer = {
name:'',
age:'',
say: function() {
console.info("%c%s", "color:red;font-size:18px", "姓名: " + this.name + ", 年龄: " + this.age);
}
};
function run() {
// 使用Object.create创建一个新客户
var myCustomer = Object.create(customer);
myCustomer.name = '张三';
myCustomer.age = '18';
myCustomer.say();
}
run();
</script>
</html>
5 总结
原型模式在JavaScript里的使用简直是无处不在,其它很多模式有很多也是基于prototype的,就不多说了,这里大家要注意的依然是浅拷贝和深拷贝的问题,免得出现引用问题。
今天的学习就到这里,你可以使用今天学习的技巧来改善一下你曾经的代码,如果想继续提高,欢迎关注我,每天学习进步一点点,就是领先的开始。
- ADO.NET入门教程(七) 谈谈Command对象高级应用
- ADO.NET入门教程(八) 深入理解DataAdapter(上)
- 深入理解DIP、IoC、DI以及IoC容器
- C#委托使用详解(Delegates)
- LINC switch系列之安装指南
- 设计模式成长记(一) 抽象工厂模式(Abstract Factory)
- 基于Open vSwitch的VxLAN隧道实验网络
- 来来来,快来围观那个Kotlin
- 怎样提高网站访问速度缩短网页加载时间
- 使用Kotlin开发一个Spring Boot Webflux Streaming应用(附视频)
- 插入排序算法
- Javascript 的addEventListener()及attachEvent()区别分析
- 设计模式-用接口来实现filter!梭梭就是干!
- OpenDaylight实现轮询策略的负载均衡服务
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 从源码看 Jetpack(6)-ViewModel源码详解
- linux配置c++11编译环境
- Java 多线程编程(聊聊线程池)
- Java 多线程编程(“锁”事碎碎念)
- Spring Cloud Alibaba技术栈(下)
- Electron安装过程深入解析(读完此文解决Electron安装失败导致的无法启动,无法打包的问题)
- Kafka中副本机制的设计和原理
- Cocoapods更新出错
- 用 Wolfram 语言映射美国的山火
- Mathematica在中学数学教与学中的应用
- 总结一些ES不常用的filter
- 如何将炫酷的报表直接截图发送邮件——在Superset 0.37使用Schedule Email功能
- Kettle构建Hadoop ETL实践(五):数据抽取
- Kafka监控必备——Kafka-Eagle 2.0.2正式发布
- iOS Universal link 入门指南