工厂模式、原型模式、构造函数模式比较
源码下载前面简单将结果对象的创建,本节课主要从代码重用、节约内存的角度来讲解对比几种创建对象的方式
方式一
创建了两个对象extend1、extend2,并分别添加了属性
let extend1 = {}; extend1.nation = "汉"; extend1.country = "中国"; extend1.job = "军人"; extend1.name = "霍去病"; extend1.dynasty = "汉"; let extend2 = {}; extend2.nation = "汉"; extend2.country = "中国"; extend2.job = "军人"; extend2.name = "岳飞"; extend2.dynasty = "宋";
方式二(工厂模式)
通过一个函数fun来进行来描述对象的创建,两次调用即可,至少从形式上,没必要在把添加属性的操作写两遍。观察上面两个对象,可以发现他们的属性名全部一样,前三个的属性值又一样, 那就给方式二这种创建方式提供了机会,属性值不同可以采用函数参数传输的方式解决。这种方式也称之为工厂模式,就是通过函数批量创建对象,可以批量创建任意多个对象,这里只是简单创建了 两个对象extend1、extend2作为演示。
function fun(name,dynasty) { let Base = {}; Base.nation = "汉"; Base.country = "中国"; Base.job = "军人"; Base.name = name; Base.dynasty = dynasty; return Base; } let extend1 = fun("霍去病","汉"); let extend2 = fun("岳飞","宋");
方式三(构造函数模式)
这种模式前面教程说过,通过关键字new完成创建,这种情况下,代码初始化后,会形成三个内存块,一个是函数fun,fun名字就是函数数据的索引,两个new执行后会分别 开辟两个内存区域,把构造函数的数据复制过来,不过属性name、dynasty会通过各自调用函数时传入的参数重新赋值。
function fun(name,dynasty) { this.nation = "汉"; this.country = "中国"; this.job = "军人"; this.name = name; this.dynasty = dynasty; } let extend1 = new fun("霍去病","汉"); let extend2 = new fun("岳飞","宋");
方式四(原型模式)
大家在创建对象的时候除了关心软件方面的代码重用问题,不知是否关注内存的占用管理问题。从对象的属性来来看,extend1、extend2两个对象有三个属性属性值是一样的, 这时候如果把这三个数据单独存储,然后把数据的索引地址,传递给每个对象,这样的话,在使用new开辟内存时,就不需要那么大,只需要存储各自不同的属性或属性名相同属性值不同的属性。 正式基于这样的考量,Javascript语言引入了prototype,prototype是每一个函数默认具有的属性,这个属性也是一个对象,具体来说这个属性指向一个对象的索引地址(引用),这个对象称为原型对象, 可以把extend1、extend2属性值相同的属性定义在prototype原型对象里面。具体代码查看下面第1到第5行。prototype是fun函数的属性,所以可以使用点符号赋值,同时prototype又是一个对象,赋值的时候 使用大括号{ }添加键值对(也就是属性名:属性值)。prototype既然是对象,当然也可以通过点符号添加属性,查看下面第6到第8行的代码,可以用这三行代码替代第1到第5行的代码。 使用prototype可以保证,new对构造函数执行操作的时候,此时定义的对象extend1或extend2都会关联到prototype定义的原型对象,对象extend1或extend2不会去复制原型对象中的代码数据,只是会通过指针 跳转到这个地方,调用相关的属性或方法,调用的时候可以直接写,比如extend1.job,返回的数据是“军人”,不需要这样写extend1.prototype.job,可你你会说prototype不是一个对象,对象访问子对象的属性 不应该连词使用点符号吗?事实上不用这样,prototype具体来说只是一个指针性质的对象,会直接跳转过去。引入prototype的原因正式为了解决这个问题,如果把prototype换成其他Javascript未定义的关键字 ,你自己用几个英文组的名字,那只能把两次使用点符号来调用属性。这里也带出来了一个对象的使用规则就是对象里面可以层层嵌套对象,就像数组一样数组里面层层嵌套数组。
function fun(name,dynasty) { this.name = name; this.dynasty = dynasty; } 1 fun.prototype = { 2 nation:"汉", 3 country :"中国", 4 job: "军人" 5 }; let extend1 = new fun("霍去病","汉"); let extend2 = new fun("岳飞","宋");
6 fun.prototype.nation = "汉"; 7 fun.prototype.country = "中国"; 8 fun.prototype.job = "军人";
总结
很多的Javascript教程可能会介绍各种各样的创建对象方式,其实没必要去记忆,关键在于你把每一个关键字搞明白,其它只不过是按照语法自由组合,比如你不把new、prototype对内存的默认处理 规则搞清楚,只是一味的打比方,最后的结果就是太混乱,记不住语法结构,用的时候没有思路。多数编程语言,不管对硬件封装程度有多大,但是内存管理都需要了解,区别只是每个人对内存的认知形式有所区别, 有数字电路基础的人理解起来会非常清晰,没有基础你可以按照自己的思路尝试把内存当作一个黑箱,抽象理解它。
类、实例
Javascript语言在ES5及其以前的标准里面没有java、C++中专门用来创建类的关键字class,这里说到类,大家自然想知道类的概念。与类相关的汉语词有分类、类别、同类、类型,也就是说类是对一些东西的分类抽象。 以下面代码为例,代码先定义了一个构造函数fun,然后使用操作符两次操作构造函数,创建了extend1、extend2两个对象,构造函数fun可以理解为类,他是一个模板,这两个对象可以理解为类的实例,new的执行过程就是把 类模板实例化的过程。构造函数fun可以多次使用,正因为如此才可以称为模板,称为类。实际的例子可以简称为实例。类更改,实例会跟着更改。对于Javascript语言而言没有专门用一个关键字来定义类,也就是说,只要你创建对象的 方式包含有类的思想就可以称为类,面向对象本身说的也是编程思想。比如上面的代码方式二工厂模式创建对象,没有利用构造函数,但是代码中定义的函数fun也是一个模板,多次执行这个函数就可以生成多个对象。
function fun(name,dynasty) { this.nation = "汉"; this.country = "中国"; this.job = "军人"; this.name = name; this.dynasty = dynasty; } let extend1 = new fun("霍去病","汉"); let extend2 = new fun("岳飞","宋");