JavaScript的原型模式

JavaScript的原型模式

一、函数的prototype 属性

在我们创建的每个函数中,都有一个prototype(原型)属性,这个属性是一个对象,它的用途是来包含可以由特定类型的实例共享的属性和方法。也就是说,不用在构造函数中定义对象信息,而可以将这些信息直接添加到原型对象中,从而可以由该构造函数构造出来的所有对象共享,如:

javascript;gutter:true; function Person(){} Person.prototype.country = "America"; Person.prototype.showCountry = function(){ alert(this.country); }</p> <pre><code>var person1 = new Person(); var person2 = new Person(); person1.showCountry();//America person2.showCountry();//America </code></pre> <pre><code> 由上述例子,Person的两个实例person1和person2共享了country属性和showCountry()方法。在上述例子中,构造函数中并没有country属性和showCountry()方法,由其构造的实例person1和person2中自然也不会有country和showCountry()方法,然而两个实例却都可以访问上述属性和方法,其实是访问的原型对象中的country属性和showCountry()方法。 我们说,每一个创建的函数中,都会自动生成一个prototype属性,该属性指向一个原型对象。而在该原型对象中又会有一个constructor属性,它指向该原型对象的引用所在的函数。就上例而言,Person会有一个prototype属性,指向其实例的原型对象,而在这个原型对象中,又有一个constructor属性,它又指向Person。 **二、对象的__proto__** **属性** 自定义的构造函数,其原型属性(prototype)默认只会得到一个constructor属性,至于其他的方法,都是从Object对象继承而来。而由该构造函数生成的新实例,默认会有一个_指针属性(在Firefox、Safari、Chrome和Flash的ActionScript中,是__proto__,并且通过脚本可以取到,而在其他实现中,这个属性对脚本则是完全不可见的),它也指向其构造函数的原型属性。 下图表明了"构造函数"、"构造函数的原型属性"、"实例",三者之间的关系: ![JavaScript的原型模式](https://johngo-pic.oss-cn-beijing.aliyuncs.com/articles/20230605/2012110219365113.png) 可见看到,实例的__proto__属性并非实例和构造函数之间的连接,而存在于实例与构造函数的原型属性。简单验证: ;gutter:true;
alert(person1.__proto__ == person2.__proto__);//true
alert(person1.__proto__ == Person.prototype);//true
alert(Person.prototype.constructor == Person);//true

有些实现无法通过脚本访问到本质的”proto“属性,但是,所有的实现却都支持用构造函数的原型属性的isPrototypeOf()方法来确定对象间是否存在这种关系。如:

javascript;gutter:true; alert(Person.prototype.isPrototypeOf(person1));//true</p> <pre><code> **三、对象访问属性的原则:** 实例对象访问属性的时候会执行一次搜索: 1. 按属性名,即key,在实例对象中搜索该属性,如果搜索到则返回对应的value 2. 如果在实例对象中没有搜到,则顺着本质上的"__proto__"属性找到原型对象,从原型对象中搜索key **这正是多个对象实例,共享原型所保存的属性和方法的基本原理。** **四、关于属性访问原则的几个notes** **:** ;gutter:true;
//1.对象实例添加属性,屏蔽掉原型中的同名属性的,注意这里并非修改了原型中的属性值
person1.country = "China";
person1.showCountry();//"China"—来自实例
person2.showCountry();//"America"—来自原型
//2.通过delete操作符而不是修改实例属性只为null,来重新访问同名的原型属性
person1.country = null;// 只是修改了实例属性而非原型中的属性值
person1.showCountry();//null—来自实例

delete person1.country;//delete 操作符,完全删掉该实力属性
person1.showCountry();//"America"—来自原型

person2.showCountry();//"America"—来自原型

五、判断一个属性的存在情况:

1.判断对象能否访问给定名的属性:使用in操作符

2.判断一个属性是否存在于对象的实例属性hasOwnProperty()方法

javascript;gutter:true; function Person(){} Person.prototype.name = "Angela"; Person.prototype.showName = function(){ alert(this.name); }; var person = new Person(); //1."name"为原型属性 alert("name" in person);//true alert(person.hasOwnProperty("name"));//false //2.person1的"name"为实例属性(屏蔽掉了原型属性)      person.name = "Isabel"; alert(person.name);//"Isabel" alert("name" in person);//true alert(person.hasOwnProperty("name"));//true //3.删除实例属性"name" delete person.name; alert(person.name);//"Angela" alert("name" in person);//true alert(person.hasOwnProperty("name"));//true</p> <pre><code> 3.可以仿照hasOwnProperty()方法写一个hasPrototypeProperty()方法,判断是否访问的是原型属性: ;gutter:true;
function hasPrototypeProperty(object, propertyName){
         //非实例属性,而又能被对象访问,则返回true
return (!object.hasOwnProperty(propertyName)) && (propertyName in object);
}

六、更简单的原型语法

如果需要给原型添加很多属性,每次都用Person.prototype可能会很麻烦,更简单语法是:

javascript;gutter:true; function Person(){} Person.prototype = { name: "Angela", showName: function(){ alert(this.name); } };</p> <pre><code> 然而,前面说过每个函数都会获得一个默认的prototype属性,该prototype属性中又包含了一个constructor属性,指向原来的函数。而这里相当于创建了个"对象字面量",赋值给了Person.prototype,本质上完全重写了prototype对象,它的constructor属性也不再指向Person了,而是指向Object,尽管instanceof仍然能得到正确的结果: ;gutter:true;
alert(person instanceof Object);//true
alert(person instanceof Person);//true
alert(Object.getPrototypeOf(person).constructor == Object);//true
alert(person.constructor == Person);//false

( 注:上面两种方式都可以获取到对象的constructor)

如果constructor很重要,可以在赋值对象中显示添上constructor属性,如:

javascript;gutter:true; 如果constructor很重要,可以在赋值对象中显示添上constructor属性,如: function Person(){} Person.prototype = { constructor: Person, //在对象字面量中添加constructor属性 name: "Angela", showName: function(){ alert(this.name); } };</p> <pre><code> **七、原型的动态性** ;gutter:true;
function Person(){}
var person = new Person();//先定义了一个Person实例对象
Person.prototype.sayHello = function(){
alert("Hello");
};//然后才给原型添加方法
person.sayHello();//"Hello"

虽然先定义了的Person的实例对象,然后才给原型添加的方法,但是,实例仍然能够访问到该方法。这是由于原型的动态性。 原理:对象访问的原则。

八、原型对象的问题

原型的最大问题是由其共享的本性所导致的。原型中的属性被所有实例共享,事实上,这只对于方法很合适,对于基本值属性也可以(因为只要向实例中添加一个同名属性就可以屏蔽掉原型中的该属性),然而对于引用类型的属性来说,却有不小的问题:

javascript;gutter:true; function Person(){} Person.prototype = { constructor: Person, name: "Elena", friends: ["Caroline", "Bonnie"], showName: function(){ alert(this.name); } }; var person1 = new Person(); var person2 = new Person();</p> <pre><code> 如上例,现在需要给person1的friends属性中添加一个"Katherine",可以有两种方法来: ;gutter:true;
//1.修改方式1
person1.friends = ["Caroline", "Bonnie", "Katherine"];
document.writeln(person1.friends+"");//Caroline,Bonnie,Katherine
document.writeln(person2.friends+"");//Caroline,Bonnie
//修改方式2
person1.friends.push("Katherine");
document.writeln(person1.friends+"");//Caroline,Bonnie,Katherine
document.writeln(person2.friends+"");//Caroline,Bonnie,Katherine

如上,如果用第一种修改方式,当然没有问题,但是用第二种方式的时候,发现 虽然只修改了 person1 的friends 属性,但是却引起了person2 的friends 同样发生了变化

这其实是由friends的”引用”类型所造成的:

  1. 修改方式1中,实质是为实例person1添加了新属性friends,并为其赋值了一个新的数组,这一属性屏蔽了原型中的同名属性。
  2. 修改方式二中,只是通过person1.friends引用修改了原型属性friends的属性值。

因此,最好少用单独的原型模式,最好的是构造函数模式与原型模式混合使用。

九、构造函数模式与原型模式混合使用

混合模式:构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。如:

javascript;gutter:true;
function Person(name, age, country){
//实例属性用构造函数模式定义
this.name = name;
this.age = age;
this.country = country;
this.friends = ["Elena", "Bonnie"];
}
Person.prototype = {
//方法和需要共享的属性用原型模式定义
constructor: Person,
showName: function(){
alert(this.name);
}
};
var person1 = new Person("Damon", 170, "America");
var person2 = new Person("Stefan", 168, "America");
person1.friends.push("Caroline");
document.writeln(person1.friends+"");//Elena,Bonnie,Caroline
document.writeln(person2.friends+"");//Elena,Bonnie
document.writeln(person1.friends == person2.friends);//false

混合模式的两个主要好处:

  1. 每个实例都会有一份自己的实例属性的副本,同时又共享着对方法的引用,不用既持有属性又持有方法,最大限度的节省了内存。
  2. 混合模式还支持向构造函数传递参数

Original: https://www.cnblogs.com/xxcbdhxx/archive/2012/11/02/2751803.html
Author: 北鱼扶摇
Title: JavaScript的原型模式

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/589962/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • HIT软构博客6–设计模式

    设计模式可以分为1.创建型模式 2.结构型模式 3.行为类模式 1.创建型模式 ​ 工厂方法模式 ​ 当client不知道要创建哪个类的具体实例或不想在客户端代码指明要创建具体哪个…

    Java 2023年6月5日
    079
  • SpringBoot使用Filter

    对全部请求进行Filter Filter: package com.example.demo.filter; import org.springframework.core.Ord…

    Java 2023年6月9日
    076
  • NC-日志配置及代码详解

    日志目录: /nchome/nclogs/servername/ ,其中servername集群时目录类似为master,ncMem01等。非集群时目录为:server1(服务名)…

    Java 2023年6月13日
    078
  • Nginx 的安装和使用

    Nginx是一款轻量级的 Web 服务器、 反向代理服务器及电子邮件(IMAP/POP3)代理服务器,是 lgor Sysoev 为俄罗斯访问量第二的 rambler.ru 站点设…

    Java 2023年6月16日
    088
  • Redis+Lua实现简易的秒杀抢购

    1 商品抢购 主要逻辑是:减库存,记录抢购成功的用户 @RestController public class DemoController { @Resource private…

    Java 2023年6月7日
    085
  • ELKB-ElasticSearch-Logstash-Kibana-beats 个人理解

    先说一下ELK,E是ElasticSearch,L是Logstash,K是Kibana,还有一个Beats。按照从采集到展示的顺序介绍下各个组件的作用。 1.Beats Beats…

    Java 2023年6月7日
    066
  • Redis变慢?深入浅出Redis性能诊断系列文章(二)

    (本文首发于”数据库架构师”公号,订阅”数据库架构师”公号,一起学习数据库技术) 本篇为Redis性能问题诊断系列的第二篇,本文主要…

    Java 2023年6月16日
    049
  • Java基础常见知识&面试题总结(上)

    Java基础常见知识&面试题总结(上) 1. 基础概念与常识 1.1 Java 语言有哪些特点? 简单易学; 面向对象(封装,继承,多态); 平台无关性( Java 虚拟机…

    Java 2023年6月9日
    098
  • 【Java面试手册-基础篇】能否在Java中终止main方法?

    答案是肯定的,可以使用 System.exit() 方法终结 main() 方法。 示例代码如下: package com.magic.main; public class Mai…

    Java 2023年6月8日
    072
  • 面试中常见智力题

    确实,这种题旧根脑筋急转弯一样,你见过,才能立马想到,没见过,那确实有点难在现场回答出来。 桶装水 只有两个无刻度的水桶,一个可以装6L水,一个可以装5L水,如何在桶里装入3L的水…

    Java 2023年6月15日
    067
  • Java并发编程(一)JUC同步类

    JUC 是学习 Java 并发编程的小伙伴不可避免的一个 pkg,JUC提供了对并发编程的底层支持,比如我们熟悉的线程池、MQ、线程同步… 都有JUC的影子,下面我们一…

    Java 2023年6月9日
    078
  • Spring Boot【快速入门】

    转自: https://www.cnblogs.com/wmyskxz/p/9010832.html Spring Boot 概述 Build Anything with Spri…

    Java 2023年5月30日
    065
  • nginx Segmentation fault (core dumped)

    1,问题描述 nginx运行正常,某些时候登陆服务器 nginx -t命令,突然出现 Segmentation fault (core dumped) 2,解决步骤 对nginx进…

    Java 2023年5月30日
    088
  • Spring RestTemplate用法

    RestTemplate简介 RestTemplate对HTTP请求进行了封装,进行请求的时候可以保留cookie,在下次请求的时候使用; postForEntity与postFo…

    Java 2023年5月30日
    083
  • element-image组件,大图预览始终从第一张图开始,不能定位当前图片位置解决方案

    转载:https://blog.csdn.net/x11819130/article/details/101779426 Original: https://www.cnblogs…

    Java 2023年6月5日
    0102
  • Windows安装VNC服务端

    下载VNC服务端 由于服务器在IDC机房,只能使用系统自带远程桌面连接到服务器进行安装VPC服务端 但在安装过程发现,如果是通过远程桌面连接到服务器进行安装,VNC Mirror …

    Java 2023年5月30日
    077
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球