一、为什么要用原型模式。
早期采用工厂模式或构造函数模式的缺点:
1.工厂模式:函数creatPerson根据接受的参数来构建一个包含所有必要信息的person对象,这个函数可以被无数次的调用,工厂模式尽管解决了创建多个相似对象的问题,却没有解决对象识别的问题(返回的是自定义的一个对象o,不明确对象o的类型)。
2. 构造函数模式:构造函数的调用和其他oo语言一样,用new操作符来构建一个Person的实例;javascript中的构造函数也是函数(所以也可以直接像普通函数那样直接调用方法名)只不过可以用来创建对象,这是和其他oo语言不一样的地方(其他oo语言的构造函数不是函数,不能直接调用方法名,必须用new操作符来创建对象才可以),相同点是构造函数首字母都要大写,非构造函数首字母都是小写。
构造函数模式解决了创建多个相似对象的问题和对象识别的问题,但是不足的地方是,采用这种模式会创建多个完成同样任务的Function实例。
拿上面的例子来分析内存:
此时person1.isName===person2.isName 是为false的
不同实例上的同名函数是不相等的,我们希望有一种方法,即可以解决创建多个相似对象的问题和对象识别的问题,还能让每个实例共享相同的方法。
上面的例子可以做改进将方法提到构造函数的外面,单独写成一个全局函数,this.isName指向一个指针:
不同的实例是共享了方法,但是如果有很多个方法,有几个我们就要定义几个全局函数,这样我们自定义的引用类型就失去了封装性,为了解决这一系列的问题,所以才有了原型模式。
二、理解原型模式。
1.理解原型模式先理解原型对象:
我们创建的每一个函数(javascript中函数也是一个对象)都有一个原型属性(prototype),原型属性实质上是一个指针,它指向一个对象,这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法(通俗的说就是这个特定类型的所有实例都可以共享原型对象包含的属性和方法)。
2.原型对象的两种赋值方法:
第一种:
此时实例对象person1和person2的属性和方法都是用的原型对象共享的,所以上面的例子输出的结果为:
(可以看到person1和person2共享一个sayName方法,它们的方法是相同的)
第二种:
和创建一个引用对象一样,也可以采用字面量的形式给原型对象赋值。最终结果和上面的结果相同,但是一个例外就是constructor属性不再指向Person了,因为字面量的方式重写了原型对象,此时的contructor指向的是Object对象(这里不懂看后面的内存分析了解原理)。
但是我们可以指明constructor指向Person:
3.原型对象的内存分析:
引用了一下《javascript高级程序第三版》中的内存分析图。
Person构造函数、Person原型对象、Person现有的两个实例之间的关系:
每一个函树都会有一个原型属性(prototype),它是一个指针,指向原型对象;默认情况下,原型对象会包含一个constructor(构造函数)属性(原型最初只包含constructor属性),这个属性包含一个指向prototype属性的指针;拿上面的图做例子,Person.prototype.constructor指向Person,通过这个构造函数就可以继续为原型对象添加其他的属性和方法。
当调用构造函数创建一个新实例后,该实例内部会包含一个内部属性(指针),它指向构造函数的原型对象;这个连接存在于实例与构造函数的原型对象之间,而不是存在于实例与构造函数之间;也就是说这个内部属性和构造函数没有直接的关系。
4.原型对象中的值不能被对象实例重写:
实例:
测试结果:
可以看到原型对象中的name没有被改变,person1.name来自实例,person2.name来自原型。
在原型模式中当通过person1.name读取属性值时,首先会去实例上查找有没有名称为name的属性,有的话就不会再去原型对象上查找;如果实例上没有,则就会去原型上搜索。
也就是说当我们给实例上添加了一个属性,这个属性就会阻止我们去原型*问这个同名属性,但是不会修改那个属性。
内存分析,图中省略了与Person构造函数之间的关系:
通过delete操作符可以直接删除实例中的属性:delete person1.name。
5.原型的动态性:
第4点说了不能通过对象实例来修改(重写)原型对象中的值,不是说原型对象中的值不能被修改,通过如下方式仍然可以修改:
测试结果如下:
由于在原型中查找值的过程是一次搜索,因此在对原型对象做的任何修改都会立即从实例上反映出来,即使是先创建实例后修改原型也如此。
实例:
测试结果:
内存分析:
( friend改为person5,function中省略了内容)
但是如果在先创建实例后修改原型的情况下,用字面量赋值的方式来重写原型对象,这就会切断现有原型与任何之前存在的对象实例之间的联系(不是先创建实例后修改原型的情况下仍然可以用这种方式重写原型对象)。
实例:
测试结果:
内存分析:
(图引用《JavaScript高级程序设计第3版》,图中值未改,原理一样)
6.原生对象的模型:
所有原生引用类型(Object、Array、String等)都在其构造函数的原型上定义了方法。例如:Array.prototype.sort(),String.prototype.substring等,通过原生对象的模型不仅可以读取到所有的默认方法的引用,还可以像修改自定义对象的原型一样修改原生对象的原型,所以也可以给原生对象添加方法,但是在实际运用中我们不要这样去修改原生对象的模型(命名冲突,重写原生方法等等问题都是我们在实际开发中所不希望遇到的)。
7.关于原型对象的几种操作:
1) isPrototypeOf检测实例对象是否包含指向某个原型对象的指针,包含则返回true,否则false。
2) Object.getPrototypeOf(实例对象) ,返回的就是这个对象的原型,下面的结果为true。
Object.getPrototypeOf(person1).name通过这种方式可以访问到原型对象中的name值。
3)hasOwnProperty(从object中继承而来)检测一个属性是否存在于实例中,是则返回true,否则返回false。
用in也可以检测属性值,只不过用这种方式检测的无论属性是在实例中还是原型中都会返回true。关于检测属性我的另一篇文章中有:Javascript之数据检测
4)Object.keys(原型对象)返回一个包含所有可枚举属性的字符串数组:
测试结果:
8.原型对象的缺点:
原型对象的好处是原型中的所有属性和方法可以被很多实例共享,缺点是当原型中包含引用类型的值的属性时,一个实例对象对这个引用类型的属性做了修改,在其他实例对象中也可以体现出来。
9.好的实践:
1)组合使用构造函数模式和原型模式:
2)动态原型模式:
除了上面两种模式以外还有寄生构造函数模式和稳妥构造函数模式这里就不总结了,没有用到过没有什么体会,以上只是我看《JavaScript高级程序第三版》的学习笔记,用自己的理解的方式归纳整理了一下。
理解javascript中的原型模式的更多相关文章
-
理解javascript中的策略模式
理解javascript中的策略模式 策略模式的定义是:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换. 使用策略模式的优点如下: 优点:1. 策略模式利用组合,委托等技术和思想,有效 ...
-
理解JavaScript中的原型继承(2)
两年前在我学习JavaScript的时候我就写过两篇关于原型继承的博客: 理解JavaScript中原型继承 JavaScript中的原型继承 这两篇博客讲的都是原型的使用,其中一篇还有我学习时的错误 ...
-
浅谈JavaScript中的原型模式
在JavaScript中创建对象由很多种方式,如工厂模式.构造函数模式.原型模式等: <pre name="code" class="html">/ ...
-
简单理解javascript中的原型对象,实现对之间共享属性和行为
javascript中提供了构造函数.可以方便的创建对象. 典型的构造函数例如以下: function Person(name, age) { this.name = name; this.age = ...
-
如何理解JavaScript中的原型和原型链
首先是一张关系图,避免抽象化理解时产生的困难 Function对象 函数对象是JavaScript学习中不可避免的一部分,而且这一部分相对重要且抽象 函数的创建方式有2种: 字面量创建 var foo ...
-
一句话简单理解javascript中的原型对象
通过构造函数F创建的对象实例p 这个对象p的原型对象是 构造函数中prototype属性指向的对象s,这个对象p中也有个非标准的__proto__属性指向构造函数prototype属性所指向的对象s, ...
-
JavaScript中的原型模式
我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法.使用原型对象的好处是可以让所有对象实例共享它 ...
-
深入理解JavaScript中创建对象模式的演变(原型)
深入理解JavaScript中创建对象模式的演变(原型) 创建对象的模式多种多样,但是各种模式又有怎样的利弊呢?有没有一种最为完美的模式呢?下面我将就以下几个方面来分析创建对象的几种模式: Objec ...
-
前端知识体系:JavaScript基础-原型和原型链-理解原型设计模式以及 JavaScript中的原型规则
理解原型设计模式以及 JavaScript中的原型规则(原文地址) 1.原型对象:我们创建的每一个函数(JavaScript中函数也是一个对象)都有一个原型属性 prototype,原型属性实质上是一 ...
随机推荐
-
华硕笔记本BIOS设置详解
BIOS是英文Basic Input/Output System的缩写,即基本输入输出系统,合理的BIOS设置可以让我们的电脑更好的为我们服务,但由于大部分笔记本的BIOS界面是英文,并且BIOS里面 ...
-
android 学习随笔二十七(JNI:Java Native Interface,JAVA原生接口 )
JNI(Java Native Interface,JAVA原生接口) 使用JNI可以使Java代码和其他语言写的代码(如C/C++代码)进行交互. 问:为什么要进行交互? 首先,Java语言提供的类 ...
-
2-jQuery - AJAX load() 方法【进阶篇】
第一篇的AJAX load() 方法过于简单,补充一下,完整的. 格式 $(selector).load(URL,data,callback); 源码 <!DOCTYPE html> &l ...
-
记录git常用操作命令
GIT的常用操作 0.写在前面 作为一名开发者,熟悉使用 git 代码管理工具是一项必备的基本技能.git 相较 SVN 而言,其优点不言而喻.git 的功能非常强大,其包括的操作命令也非常的多,但是 ...
-
JavaScript的基本使用
一.JavaScript的简单介绍 JavaScript是一种属于网络的脚本语言(简称JS),已经被广泛用于Web应用开发,常用来为网页添加各式各样的动态功能,为用户提供更流畅美观的浏览效果.通常Ja ...
-
在安卓手机上通过虚拟机运行Windows XP
转自:https://www.ithome.com/html/android/302170.htm 细数当年的桌面版Windows,似乎针对ARM架构处理器的版本并不多,小编曾用过一段时间的Windo ...
-
Retrofit添加自定义转换器
Retrofit2开始支持多种 Converter 并存,在之前,如果你遇到这种情况:一个 API 请求返回的结果需要通过 JSON 反序列化,另一个 API 请求需要通过 proto 反序列化,唯一 ...
-
MATLAB R2018a 输入中文却显示方框问号的问题
[问题] 安装完成软件后,我把编辑区字体重设为 consolas : 就会出现 输入中文注释却没办法正常显示的问题: [解决办法] 把字体改成 Monospaced (查了一下 说是MATLAB默认字 ...
-
python附录-re.py模块源码(含re官方文档链接)
re模块 python官方文档链接:https://docs.python.org/zh-cn/3/library/re.html re模块源码 r"""Support ...
-
redis 缓存用户账单策略
最近项目要求分页展示用户账单列表,为提高响应使用redis做缓存,用到的缓存策略和大家分享一下. 需求描述:展示用户账单基本信息以时间倒序排序,筛选条件账单类型(所有,订单收入.提现.充值...). ...