JavaScript之面向对象学习七(动态原型模式、寄生构造函数模式、稳妥构造函数模式创建自定义类型)

时间:2022-09-24 11:26:39

一、动态原型模式

在面向对象学习六中的随笔中,了解到组合构造函数模式和原型模式创建的自定义类型可能最完善的!但是人无完人,代码亦是如此!

有其他oo语言经验的开发人员在看到独立的构造函数和原型时,很可能会感到非常困惑。因为对象在其他oo语言中往往是封装在一块的,而构造函数确是和原型分开的,所以并没有真正意义上的封装,所以动态原型模式正是致力与解决这一问题的一个方案!

动态原型模式将所有的信息都封装在构造函数中(包括原型和实例属性),通过在构造函数中实例化原型(仅在必要的情况下)实现封装,又保持了同时使用构造函数和原型的优点。

    function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.friends=["小超","大超"];
if(typeof this.sayName!="function") {//这段判断语句的作用是限制Person.prototype属性(原型属性对象)只生成一次,要不然每次实例化一个Person对象都会去写一遍原型对象
Person.prototype.sayName = function () {
alert(this.name);
}
Person.prototype.sayHello=function(){
alert("Hello");
}
}
}
var person=new Person("张三",22,"coder");
person.sayName();

注意:typeof this.sayName!="function" 中的this,因为创建Person构造函数时,会创建一个prototype属性,该属性实际上就是Person.prototype的原型对象,prototype属性是一个指针,指向Person.prototype的原型对象,所以构造函数拥有所有Person.prototype的原型对象的属性和方法,而创建Person.prototype圆形对象时,会生成一个constructor属性,该属性也是一个指针,指向Person构造函数,用于判断对象实例的类型!

因为Person构造函数够拥有Person.prototype的原型对象的所有属性和方法,所以可以用this判断原型中是否存在该方法!

当第一次实例化Person对象的时候,原型就已经完成初始化,所以当第二次实例化的时候,原型就不会初始化,而且if语句检查的可以是原型的任意属性和方法,不需要每一个都检查,只需要检查其中一个,对于采用这种模式创建的自定义类型,可以同时使用constructor和instanceof来检查他们的类型,代码如下:

    function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.friends=["小超","大超"];
if(typeof this.sayName!="function") { //这段判断语句的作用是限制Person.prototype属性(原型属性对象)只生成一次,要不然每次实例化一个Person对象都会去写一遍原型对象
Person.prototype.sayName = function () {
alert(this.name);
}
Person.prototype.sayHello=function(){
alert("Hello");
}
}
}
var person=new Person("张三",22,"coder");
var person1=new Person("李四",22,"coder");
alert(person.constructor); //输出: Person构造函数所对应的方法体
alert(person instanceof Person); //输出:true 说明person是Person对象的实例
alert(person.constructor==person1.constructor); //输出:true 说明两个实例的原型对象的constructor属性都指向Person构造函数即他们是同一类型

二、寄生构造函数模式

当你需要创建一个自定义类型的时候,当前面的随笔中的模式都不适用的情况下,可以使用寄生构造函数模式。这种模式的基本思想是创建一个函数,该函数的作用仅仅是封装创建对象的代码。

代码如下:

//寄生构造函数模式
function Person(age,name) {
var o=new Object();
o.age=age;
o.name=name;
o.sayName=function(){
alert(this.name);
}
return o;
}
var person=new Person(22,"张三");
person.sayName(); //工厂模式
function createPeron(name,age,job){
var object=new Object();
object.name=name;
object.age=age;
object.job=job;
object.sayName=function(){
alert(this.name);
}
object.sayForm=function(){
alert(typeof this);
}
return object;
}
var person=createPeron("张三",22,"coder");
person.sayName();
person.sayForm();

在上面寄生模式的例子中,Person函数创建了一个新对象,并以相应的属性和方法初始化该对象,然后又返回这个对象。

然后分析其与工厂模式的区别:

1、寄生模式创建对象时使用了New关键字

2、寄生模式的外部包装函数是一个构造函数

除了上面这2个区别寄生模式和工厂模式几乎一样,构造函数在不返回值的情况下,默认返回对象的新实例。而通过在构造函数的末尾添加一个return 语句,可以重写调用构造函数是返回的值

作用:寄生模式可以在特殊的情况下为对象来创建构造函数,原因在于我们可以通过构造函数重写对象的值,并通过return返回  重写调用构造函数(创建的对象的实例)之后的对象实例的新的值。

假设我们想创建一个具有额外方法的特殊数组。由于不能直接修改Array构造函数,所以我们可以使用寄生模式。代码如下:

function SpecialArray() {
//创建数组
var array=new Array();
//添加值 arguments获取的是实参,不是形参,所以SpecialArray()并没有形参接收传递过来的参数
array.push.apply(array,arguments);
array.toPipedString=function(){
return this.join("|");
}
return array;
}
var colors=new SpecialArray("red","blue","black");
alert(colors.toPipedString()); //输出:red|blue|black

我们利用寄生构造函数模式,在不修改Array构造函数的情况下,通过为Array对象创建构造函数达到修改Array对象的目地;

在分析上面的代码:

1、var array=new Array();创建了一个Array对象

2、return array;在经过一系列的修改之后返回修改之后的Array对象

3、var colors=new SpecialArray("red","blue","black"); 创建了一个SpecialArray对象,接收的确是修改之后的Array对象的实例值

所以return array;返回的对象是Array类型,而且colors接收了这个返回的对象,所以colors并不是SpecialArray构造函数的实例,而是Array的实例,下面的代码可以说明:

alert(colors instanceof SpecialArray); //输出:false
alert(colors instanceof Array); //输出:true

所以,由于存在上述问题,如果能使用其他的模式的情况下,建议不要使用这种模式.

三、稳妥构造函数模式

道格拉斯 *  克罗克福德 发明了JavaScript中的稳妥对象这个概念.所谓稳妥对象,指的是没有公共属性,而且其方法也不引用this的对象。稳妥对象最适合用在一些安全的环境中(这些环境会禁止使用new和this),或者防止数据被其他的应用改动。

稳妥构造函数与寄生构造函数模式类似,但是也有两点区别:

1、稳妥模式不使用new操作符调用构造函数

2、新创建对象的实例方法不引用this

其代码如下:

function Person(name,age) {
//创建要返回的对象
var o=new Object();
//可以在这里定义私有变量和函数
//添加方法
o.sayName=function(){
alert(name);
}
//返回对象
return o;
}
var person=Person("张三",22);
person.sayName(); //使用稳妥构造函数模式只能通过其构造函数内部的方法来获取里面的属性值

上面的代码定义了一个person变量,里面保存的是一个稳妥对象,而除了吊用他的sayName()方法外,没有别的方法可以访问其数据成员。即使有其他的代码会给这个对象添加方法和数据成员,但也不可能有别的方法访问到传入到构造函数中的原始数据。稳妥构造函数模式提供的这种安全性。是的它非常适合在某些安全执行环境中。

JavaScript之面向对象学习七(动态原型模式、寄生构造函数模式、稳妥构造函数模式创建自定义类型)的更多相关文章

  1. JavaScript之面向对象学习六原型模式创建对象的问题,组合使用构造函数模式和原型模式创建对象

    一.仔细分析前面的原型模式创建对象的方法,发现原型模式创建对象,也存在一些问题,如下: 1.它省略了为构造函数传递初始化参数这个环节,结果所有实例在默认的情况下都将取得相同的属性值,这还不是最大的问题 ...

  2. JavaScript之面向对象学习五(JS原生引用类型Array、Object、String等等)的原型对象介绍

    1.原型模式的重要性不仅仅体现在创建自定义类型方面,就连所有的原生的引用类型(Obejct.Array.String等等)都在构造函数的原型上定义方法和属性.如下代码可以证明: alert(typeo ...

  3. JavaScript之面向对象学习一

    1.通过Object构造函数和对象字面量来创建对象缺点:使用同一个接口创建很多的对象,会产生大量的重复代码.比如我需要创建人的对象,并且需要三类人,医生.工程师.老师,他们可以抽象出很多属性,比如姓名 ...

  4. JavaScript之面向对象学习八(继承)

    简介:继承是OO语言中的一个最为人津津乐道的概念.许多OO语言都支持两种继承方式:接口继承和实现继承.接口继承只继承方法签名,而实现继承则继承实际的方法. 但是JS的函数并没有签名,所以在ECMASc ...

  5. PHP面向对象学习-属性 类常量 类的自动加载 构造函数和析构函数 访问控制(可见性)

    在类的成员方法里面,可以用 ->(对象运算符):$this->property(其中 property 是该属性名)这种方式来访问非静态属性.静态属性则是用 ::(双冒号):self::$ ...

  6. JavaScript之面向对象学习二(原型属性对象与in操作符)获取对象中所有属性的方法

    1.原型属性对象于in操作符之in单独使用 有两种方式使用in操作符:单独使用和在for-in循环中使用.在单独使用中,代码如下: function Person(){ } Person.protot ...

  7. JavaScript之面向对象学习四原型对象的动态性

    1.由于在原型中查找值的过程是一次搜索,因此我们对原型对象所做的任何修改都能够立即从实例上反映出来---即便是先创建了实例后修改原型也是如此.代码如下: function Person(){ } va ...

  8. JavaScript之面向对象学习三原型语法升级

    1.到目前为止,我们是时候分析下前面的使用原型语法来定义对象有哪些不足的地方,代码如下: function Person(){ } Person.prototype.name="张三&quo ...

  9. PHP面向对象学习七 总结

    1.对象描述的配置 方法名 __tostring() 我们可以直接打印对象句柄,从而获得该方法的基本信息或其他内容. class My{ function __tostring ( ){ echo & ...

随机推荐

  1. 流式大数据处理的三种框架:Storm,Spark和Samza

    许多分布式计算系统都可以实时或接近实时地处理大数据流.本文将对三种Apache框架分别进行简单介绍,然后尝试快速.高度概述其异同. Apache Storm 在Storm中,先要设计一个用于实时计算的 ...

  2. MySQL服务器状态变量

    一,最值得检查的状态变量使用show global status进行检测二.变量部分1.Aborted_clients如果这个变量持续增加,确定连接是否被关闭了.如果不是检查网络性能,并且检查max_ ...

  3. SQLSERVER 数据库性能的基本

    很久没有写文章了,在系统正式上线之前,DBA一般都要测试一下服务器的性能 比如你有很多的服务器,有些做web服务器,有些做缓存服务器,有些做文件服务器,有些做数据库服务器 做数据库服务器的那台服务器性 ...

  4. 小议common lisp程序开发流程 - Ever 17 - 博客频道 - CSDN.NET

    小议common lisp程序开发流程 - Ever 17 - 博客频道 - CSDN.NET 小议common lisp程序开发流程 分类: lisp 2011-04-17 20:59 1316人阅 ...

  5. Js中有关变量声明和函数声明提升的问题

    在ECMAScript5中没有块级作用域一说,只有函数作用域和全局作用域,在其中声明的变量和函数和其他语言的展现形式不同,在某些情况下不一定需要先定义后使用,函数和变量的使用可以在其声明之前,这到底是 ...

  6. Elixir游戏服设计一

    在Erlang游戏服设计总结http://www.cnblogs.com/rubyist/p/5530575.html里, 我提到我想要的游戏服设计方法,希望以应用做为基础构建块.最近我在学习elix ...

  7. 用spark-submit启动程序

    来源:http://spark.apache.org/docs/latest/submitting-applications.html 提交程序常用的一些选项 ./bin/spark-submit \ ...

  8. 【原创】大叔经验分享(16)Context namespace element 'component-scan' and its parser class [org.springframework.context.annotation.ComponentScanBeanDefinitionParser] are only available on JDK 1.5 and higher

    今天尝试运行一个古老的工程,配置好之后编译通过,结果运行时报错: org.springframework.beans.factory.BeanDefinitionStoreException: Une ...

  9. boot跳转到app后,中断不起作用的原因

    boot跳转到app后 osKernelStart()之前,中断有问题,不起作用 原因是因为boot跳转之前__disable_irq(); 跳转到APP后,并不是一切从头开始,__disable_i ...

  10. web前端设计最好用的工具

    一.FSCapture FastStone Capture(FSCapture)是经典好用的屏幕截图软件,还具有图像编辑和屏幕录制两大功能,可以捕捉全屏图像,或者活动窗口.窗口内的控件对象截图.支持手 ...