js面向对象程序设计之构造函数
再上一篇的开头说了创建对象的两种方式,一种是Object构造函数的方式,一种是对象字面量的方法。但这些方式创建多个对象的时候都会产生大量的重复代码。经过技术的进步也演化出来许多的创建对象的模式。本章会介绍 工厂模式,原型模式,构造函数模式和构造函数与原型模式的混合使用。
1,工厂模式
工厂模式是一个比较广为人知的模式,这种模式将细节抽象出来。代码如下
- function createPerson(name,age,job){
- var o =new Object();
- o.name=name;
- o.age=age;
- o.job=job;
- o.sayName=function(){
- alert(this.name)
- }
- return o;
- }
- var person1=createPerson("ds",12,"dada");
- var person2=createPerson("ds2",122,"dada2");
给他所需要的材料,他就会返回一个对象。解决了大量重复代码的问题。可是又有一个问题,就是每个返回的对象都是Object。每个对象的的类型部分却别开来。所以有个新的模式来解决这个问题。
2,构造函数模式
废话不多说,还是先上代码吧。
- function Person(name,age,job){
- this.name=name;
- this.age=age;
- this.job=job;
- this.sayName=function(){
- alert(this.name)
- }
- }
- var person1=new Person("ds",12,"dada");
- var person2=new Person("ds2",122,"dada2");
第二种的使用了new的关键字。实际上经历了4步。1,创建了一个新对象。2,将构造函数的作用域赋给创建的变量。3,执行代码。4,返回新的对象。
person1和person2是不同的实例。两个对像有一个Constructor(构造函数)指向Person的函数本身。而Constructor就是用来表示对象的类型。这也是构造函数模式和工厂模式的不同。而且想Object和Array的就是js中原生的构造函数。创建自定义的构造函数意味着可以为他的实例标识为一种特定的类型。
其实构造函数也是函数知识调用的方式不同而已。构造函数也可以普通的方式调用。
- var p=new Object();
- Person.call(p,"sss",22,"222");
但是构造函数并不是没有问题,比如说上面的那个SayName(),其实person1和person2的SayName其实并不是同一个Funcition的实例。也就是说每new一个对象,SayName本身的方法也被new了一次。所有实例的SayName都是独立的并没有指向同一个方法。解决的方法也有可以将方法提取到全局变量。
- function Person(name,age,job){
- this.name=name;
- this.age=age;
- this.job=job;
- this.sayName=sayName;
- }
- function sayName()
- {
- alert(this.name)
- }
但是问题又来了,sayName作为全局变量的话却只能被Person调用,有点不太符合全局变量的定义。重要的是如果方法很多的话又要在全局变量中添加许许多多的方法。好在这些问题都可以通过原型模式来解决
3,原型模式
在说原型模型的时候,应该先说明一下prototype(原型)是什么?其实它就是一个指针,指向一个对象。这个对象就是可以给特定类型的所有实例共享的属性和方法。还是看代码吧。
- function Person(){
- }
- Person.prototype.name="shaoqi";
- Person.prototype.age=28;
- Person.prototype.job="IT";
- Person.prototype.sayName=function(){
- alert(this.name);
- }
- var person1=new Pserson();
- person1.sayName();
上图解释了对象,实例,原型和构造函数之间的关系。
在js中,只要创建一个新函数就会为该函数创建一个prototype的属性。指向函数的原型对象。原型对象有都会有个constructor的指针,指向原函数。每个实例也有个指针[[prototype]],指向原型。其实每个实例和构造函数是没有直接关系的,是通过原型将实例和构造函数关联起来。
其实构造函数还有个更简单的写法。
- function Person(){
- }
- Person.prototype={
- name:"shaoqi",
- age:23,
- job:"It",
- SayName:function(){
- alert(this.name);
- }
- }
但这样又有个问题,这样就相当与重写了整个prototype,js本身是不会给它生成constructor的。也就是说这个原型对象中没有指向Person的指针。但可以给它显示的给它赋值。
- function Person(){
- }
- Person.prototype={
- constructor:Person,
- name:"shaoqi",
- age:23,
- job:"It",
- SayName:function(){
- alert(this.name);
- }
- }
但还有个小问题,就是当这样显示的给原型指定构造函数的话,它的[[Enumerable]]会默认为true,原生的constructor是不能被枚举的。如果一定要兼容ECMAScript5的话可以改写成下面代码
- function Person(){
- }
- Person.prototype={
- name:"shaoqi",
- age:23,
- job:"It",
- SayName:function(){
- alert(this.name);
- }
- }
- Object.defineProperty(Perosn.prototype,"constructor",{
- enumerable:false,
- value:Person
- })
defineProperty这个方法的话在上一遍中有提到过。用来给属性显示的赋值特性的方法。
原型模式还有个动态性,应为本身在实例中它是已指针的形式存在的。可以动态的给原型添加方法和属性。可以动态的添加到实例上面。当然原型对象也不是完美的,也是存在问题的。下面来看一段代码。
- function Person(){
- }
- Person.prototype={
- constructor:Person,
- name:"shaoqi",
- age:23,
- job:"It",
- friends:["11","22","33"],
- SayName:function(){
- alert(this.name);
- }
- }
- var person1=new Person();
- var person2=new Person();
- person1.friends.push("44");
- alert(person1.friends);
- alert(person2.friends);
问题就在于所有的实例(person1,person2)的指针都指向了一个数组。person1对数组操作之后,会改变原型中的数据,那么person2的Friend也就变了。实际实例一般都是要有属于自己的全部属性的。所以这才是很少有人单独使用原型模式的原因。
4,组合使用构造函数和原型模式
其实看到这里,我们会发现原型模式和构造模式的一些问题。构造函数模式是所有的属性都是为每一个实例重新的初始化一个出来。而原型模式则是为所有的实例公用一个属性或者方法。其实两种方法都有点极端,在一个对象中,其实情况是多样的,有些属性需要独立,有些需要共享。所有就有了这种组合模式的出现。可谓是取了两种模式之长。
- function Person(name,age,job)
- {
- this.name=name;
- this.age=age;
- this.job=job;
- this.friends=["11","22"]
- }
- Person.prototype={
- constructor:Person,
- sayName:function(){
- alert(this.name);
- }
- }
将需要独立出来属性放在构造函数里面进行初始化,然后类似方法的属性则通过原型的方式进行赋值。
这是在ECMAScript5中使用最广泛的创建自定义类型方式。
5,动态原型模式
其实在对象语言中如C#中是没有原型一说的,只有一个构造函数。为了尽量的消除这点差异便又来动态原型模式。它就是将所有的信息都封装到了构造函数中。
- function Person(name,age,job)
- {
- this.name=name;
- this.age=age;
- this.job=job;
- this.friends=["11","22"];
- if(typeof this.sayName != "function")
- {
- Person.prototype.sayName=function(){
- alert(this.name)
- }
- }
- }
不同之处就是在构造函数中有个判断。这个判断保证里面的代码只在初始化的时候运行一遍。这样就可用同一个构造函数来完成组合模式的作用了。
6,寄生模式
这是一个比较少用的模式。其实他和工厂方式没什么太大的区别。看个例子。
- function Person(name,age,job){
- var o =new Object();
- o.name=name;
- o.age=age;
- o.job=job;
- o.sayName=function(){
- alert(this.name)
- }
- return o;
- }
- var person1=new Person("dd",12,"dd");
是不是和工厂模式一毛一样??其实唯一的区别就在于调用的时候,寄生模式使用了new的方法进行初始化。这种模式一般使用在什么场景呢?
假设我们想在原生的Array的基础上改造一下便可以这样
- function SpecialArray()
- {
- var value=new Array();
- value.push.apply(values,arguments);
- value.toPopedString=function(){
- return this.join("|");
- }
- return value;
- }
- var colors=new SpecialArray("11","2","33");
- alert(colors.toPopedString());
在原有的类型上进行改造,可以用到寄生模式,有点类似于c#中的扩展。但是这样产生的实例和构造函数,原型是没有任何关系的。所以建议在可以使用它模式的前提下就不要用这种模式了。
这一篇吧对象的构造函数说完了,也说了原型的原理。下一篇就开始继承和原型链了。
js面向对象程序设计之构造函数的更多相关文章
- JS面向对象程序设计(OOP:Object Oriented Programming)
你是如何理解编程语言中的面向对象的? 我们研究JS和使用JS编程本身就是基于面向对象的思想来开发的,JS中的一切内容都可以统称为要研究的“对象”,我们按照功能特点把所有内容划分成“几个大类,还可以基于 ...
- js面向对象1----了解构造函数
一.构造函数与实例的区别 1 构造函数 构造函数主要是一种用于生成对象的饼干模具,这些对象具有默认属性和属性方法,它可以创建多个共享特定特性和行为的对象. 构造函数只是一个函数,但当函数遇到了ne ...
- js面向对象程序设计之属性和对象
写在博客之前的话,这是我这个刚毕业的菜鸟的第一篇博客.一口吃不成一个胖子,我也希望写的第一篇东西就让读的人醍醐灌顶.我会抱着怀疑的态度来看自己写的文章,如果有写错的地方,请大家不要被误导,如果有大神提 ...
- js面向对象程序设计之继承
在面向对象语言中继承分成两种:接口继承和实现继承.解释一下,接口继承只继承方法的签名,而实现继承则是继承实际的方法.但是ECMAScript中的函数没有签名所以无法进行接口继承,只能是实现实现继承.而 ...
- JS面向对象(1)——构造函数模式和原型模式
1.构造函数模式 构造函数用来创建特定的类型的对象.如下所示: function Person(name,age,job){ this.name=name; this.job=job; this.ag ...
- js面向对象小结(工厂模式,构造函数,原型方法,继承)
最近过了一遍尼古拉斯泽卡斯的高级程序设计第三版(红皮书)第六章:面向对象程序设计,现在把总结出来的东西和大家分享一下. 主要内容如下: 1.工厂模式 2.构造函数模式 3.原型模式 4.继承 一.工厂 ...
- js面向对象、创建对象的工厂模式、构造函数模式、原型链模式
JS面向对象编程(转载) 什么是面向对象编程(OOP)?用对象的思想去写代码,就是面向对象编程. 面向对象编程的特点 抽象:抓住核心问题 封装:只能通过对象来访问方法 继承:从已有对象上继承出新的对象 ...
- js面向对象(对象/类/工厂模式/构造函数/公有和原型)
https://www.cnblogs.com/sandraryan/ 什么是对象 js中一切都是对象(有行为和特征).js允许自定义对象,也提供了内建对象(string date math等) 对象 ...
- 带你一分钟理解闭包--js面向对象编程
上一篇<简单粗暴地理解js原型链--js面向对象编程>没想到能攒到这么多赞,实属意外.分享是个好事情,尤其是分享自己的学习感悟.所以网上关于原型链.闭包.作用域等文章多如牛毛,很多文章写得 ...
随机推荐
- 工作中常见的hive语句总结
hive的启动: 1.启动hadoop2.开启 metastore 在开启 hiveserver2服务nohup hive --service metastore >> log.out 2 ...
- mysql-介绍、下载安装以及软件基本管理
一.mysql介绍 mysql是一个关系型数据库管理系统,它是一个基于socket编写的C/S架构的软件. 客户端软件: mysql自带:如mysql命令,mysqldump命令等. python模块 ...
- 初学css 行内元素与块级元素
行内元素与块级元素直观上的区别 1.行内元素会在一条直线上排列,都是同一行的,水平方向排列块级元素各占据一行,垂直方向排列.块级元素从新行开始结束接着一个断行. 2.块级元素可以包含行内元素和块级元素 ...
- hasattr() getattr() setattr() 函数使用详解??
hasattr(object, name)函数: 判断一个对象里面是否有name属性或者name方法,返回bool值,有name属性(方法)返回True,否则返回False. **注意:name要使用 ...
- python 导入re模块语法及规则
正则表达式是功能比较强大的模块,应用在很多地方,抓网页,数据分析,数据验证等,下面讲述python 导入re模块语法及规则. 1,re模块语法 re.match 从头开始匹配 re.search 匹配 ...
- C# log4net 日志写入到数据库
原文:C# log4net 日志写入到数据库 效果图: 1:第一步创建SQL表结构 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ...
- 基于cdn方式的vue+element-ui的单页面架构
一.下载vue2.x,下载element-ui.js以及css 二.html文件 <!DOCTYPE html> <html> <head> <meta ch ...
- 2020年的ARM处理器将超越英特尔
2020年ARM真的会超越英特尔成为世界芯片霸主吗?迄今为止,基于ARM的笔记本电脑一直很流行,但在一两年内你可能会对它们产生不同的印象.该公司对其未来的处理器架构的性能预期提供了一个罕见的看法,这些 ...
- 20.Nodejs基础知识(上)——2019年12月16日
2019年12月16日18:58:55 2019年10月04日12:20:59 1. nodejs简介 Node.js是一个让JavaScript运行在服务器端的开发平台,它让JavaScript的触 ...
- Task6.神经网络基础
BP: 正向计算loss,反向传播梯度. 计算梯度时,从输出端开始,前一层的梯度等于activation' *(与之相连的后一层的神经元梯度乘上权重的和). import torch from tor ...