虽然Object构造函数或对象字面量都可以用来创建单个对象,但这种方式有个弊端:使用同一个接口创建很多对象,会产生大量的重复代码。为了解决这个问题,于是百家争鸣,各种工厂模式的变体应运而生。

1.工厂模式

这种模式是软件工厂领域的广为人知的设计模式,它抽象了创建具体对象的过程,用函数来封装以特定接口创建对象的细节,举个栗子:

 1 function createPerson(name,age,job){2             var o=new Object();3              o.name=name;4              o.age=age;5              o.job=job;6              o.sayname = function(){ 7 alert(this.name); 8  }; 9 return o; 10  } 11 12 var person1=createPerson("Sleipnir",23,"Software"); 13 var person2=createPerson("xiaoxiao",24,"Student");

这个模式虽然可以无数次调用,解决了创建多个相似对象的问题,但没有解决对象识别的问题(即怎样知道一个对象的类型)

2.构造函数模式

我们先用构造函数模式把工厂模式的例子重写一遍:

 1  function Person(name,age,job){2                 this.name=name;3                 this.age=age;4                 this.job=job;5                 this.sayname = function(){6                    alert(this.name); 7  }; 8  } 9 10 var person1=new Person("Sleipnir",23,"Software"); 11 var person2=new Person("xiaoxiao",24,"Student");

跟之前的工厂模式,我们可以看出区别:1.没有在函数内再创建对象

2.直接将属性和方法赋给了this对象

3.没有return语句

在创建Person的实例时,必须用到new操作符。以这种方式调用构造函数会经历以下4个步骤:1.创建一个新对象  2.将构造函数的作用域赋值给新对象(因此this就指向了这个新对象) 3.执行构造函数中的代码(为新对象添加属性和方法)   4.返回新对象

刚才说了,构造函数胜于工厂模式的大方在于,它能解决实例的对象类型问题,将来可以将它的实例标识为一种特定类型:

1 console.log(person1 instanceof Person);  //true
2 console.log(person1 instanceof Object]);  //true
3 console.log(person2 instanceof Person);  //true
4 console.log(person2 instanceof Object);  //true
5 console.log(Person instanceof Object);  //true

person1和person2之所以同时是Object的实例,是因为所有对象均继承自Object(下面会讲到继承)

2.1 调用构造函数的方式

上面已经写了Person的构造函数,我们来用几种不同的方式调用:

 1 //当做构造函数使用2 var person= new Person("Sleipnir",23,"Software");3 person.sayname();  //"Sleipnir"4 5 //作为普通函数使用6 Person("xiaoxiao",25,"Student");  //添加到全局window7 window.sayname();  //"xiaoxiao"8 9 //在另一个对象的作用域中调用
10 var o=new Object();
11 person.call(o,"xiaoxiao",25,"Student");
12 o.sayname();  //"xiaoxiao"

第一种是当做构造函数使用,之前已经提过,;第二种是作为普通函数调用,因为this对象都是指向全局对象,所以属性和方法都被添加了window对象;第三种是在o对象的作用域中使用call()方法来调用Perso函数

2.2 构造函数的问题

构造函数虽然不错,但也有瑕疵。主要问题就是每个方法需要在每个实例上重新创建一遍,但是,如果在构造函数里面创建Function实例或者在构造函数外部定义函数来供构造函数创建的实例调用的话,那我们所谓的构造函数的全局作用域就名不副实,自定义的引用类型包括定义的方法也就没有封装性可言了。

好在程序员们都是不服输的人,历史的车轮总是向前滚动,于是原型模式就登上历史舞台了。

3.原型模式

概念先不说,举个栗子再解释:

 1 function Person(){2 }3 Person.prototype.name="Sleipnir";4 Person.prototype.age=23;5 Person.prototype.job="Software";6 Person.prototype.sayname=function(){ 7 alert(this.name); 8  }; 9 10 var person1=new Person(); 11 person1.sayname(); //"Sleipnir" 12 var person2=new Person(); 13 person2.sayname(); //"Sleipnir" 14 15 alert(person1.sayname==person2.sayname);

我们创建的每一个函数都有一个prototype(原型)属性,这个属性是个指针,指向一个对象。所以,prototype就是通过调用构造函数而创建的那个对象实例的原型对象。上面例子中的person1和person2所拥有的属性和方法都是来源于构造函数的原型对象中的

想要理解原型模式,就要理解【构造函数】【原型】【实例】这三种之间的关系,抽象出来简单来说,原型是构造函数的属性,而实例是通过构造函数的原型而创建的,实例和构造函数没有关系,原型里还有一个constructor是指向构造函数的,constructor就是类似于指针的存在,构造函数通过constructor的指针作用,就把原型和实例连接起来了。这也是最简单的原型继承关系。

3.1 访问实例/原型的属性

有时候,我们根据原型创建的实例,这个实例里有一部分是原型的属性,有一部分也是实例自己的属性,于是就要判断哪些属性属于原型,哪些属性属于实例自己

有两个方法:hasOwnProperty() 和 in操作符

先看hasOwnProperty():

 1 function Person(){2 }3 Person.prototype.name="Sleipnir";4 Person.prototype.age=23;5 Person.prototype.job="Software";6 Person.prototype.sayname=function(){ 7 alert(this.name); 8  }; 9 10 var person1=new Person(); 11 var person2=new Person(); 12 13 console.log(person1.hasOwnProperty("name")); //false 14 15 person1.name="xiaoxiao"; 16 console.log(person1.name); //"xiaoxiao" 17 console.log(person1.hasOwnProperty("name")); //true 18 19 console.log(person2.name); //"Sleipnir" 20 console.log(person2.hasOwnProperty("name")); //false 21 22 delete person1.name; 23 console.log(person1.name); //"Sleipnir" 24 console.log(person1.hasOwnProperty("name")); //false

从代码结果可以总结出来,hasOwnProperty()检测的是实例属性,如果是属于实例的,返回true,否则返回false

in操作符:

function Person(){
}
Person.prototype.name="Sleipnir";
Person.prototype.age=23;
Person.prototype.job="Software";
Person.prototype.sayname=function(){
alert(this.name); }; var person1=new Person(); var person2=new Person(); console.log(person1.hasOwnProperty("name")); //false console.log("name" in person1); //true  person1.name="xiaoxiao"; console.log(person1.name); //"xiaoxiao" console.log(person1.hasOwnProperty("name")); //true console.log("name" in person1); //true  console.log(person2.name); //"Sleipnir" console.log(person2.hasOwnProperty("name")); //false console.log("name" in person2); //true delete person1.name; console.log(person1.name); //"Sleipnir" console.log(person1.hasOwnProperty("name")); //false console.log("name" in person1); //true

通过互相比较,我们能看出来,直接使用in操作符是,无论属性是实例自己的还是原型的,都会返回true

我们来定义一个函数,组合使用hasOwnProperty()和in操作符:

function hasPrototypeProperty(object,name){
                                                     return !object.hasOwnProperty(name)&&(name in object); 
                                                                }

 1 function Person(){2 }3 Person.prototype.name="Sleipnir";4 Person.prototype.age=23;5 Person.prototype.job="Software";6 Person.prototype.sayname=function(){ 7 alert(this.name); 8 }; 9 function hasPrototypeProperty(object,name){ 10 return !object.hasOwnProperty(name)&&(name in object); 11  } 12 var person=new Person(); 13 console.log(hasPrototypeProperty(person,"name")); //true 14 15 person.name="xiaoxiao"; 16 console.log(hasPrototypeProperty(person,"name")); //false

3.2  更简单的原型语法

使用Person.prototype一个个定义属性太繁琐,于是我们可以用对象字面量给原型定义属性:

 1 function Person(){2 }3 Person.prototype={4        constructor:Person,5        name:"Sleipnir",6        age:23, 7 job:"Software", 8 sayname: function(){ 9 alert(this.name); 10  } 11 };

这里要注意,constructor要设置为Person,不然就会切断原型与实例之间的关系了

3.3 原型模式的问题

原型模式省略了为构造函数传递初始化参数这一环节,结果所有实例在默认情况下都取得了相同的属性值。

原型模式的问题在于其共享的本质,我们来看一个由于这个原因而引起问题的栗子:

function Person(){}Person.prototype={constructor:Person,name:"Sleipnir",age:23,job:"Software",friends:["zhangsan","lisi"], sayname: function(){ alert(this.name); } }; var person1=new Person(); var person2=new Person(); person1.friends.push("van"); console.log(person1.friends); //"zhangsan,lisi,van" console.log(person2.friends); //"zhangsan,lisi,van" console.log(person1.friends===person2.friends); //true

这个例子中,friends是个引用类型的数组,由于在原型中已经定义了,所以在实例中做修改时,修改的值也会映射到其原型,然后原型又会同时将数据更新加载到其他实例中,这也体现了原型的动态性。但这种效果并不是我们想要的。

实际需求是,原型帮我们定义一部分公共属性和方法,然后实例自己也有独立的属性和方法,基于这种情况,便有了下面的模式。

4.组合使用构造函数模式和原型模式

在这种模式下,构造函数用于定义实例属性,原型模式用于定义方法和共享的属性。结果,每个实例都有了自己的一份实例属性的副本,同时也共享着对方法的引用,最大限度地节省内存,同时这种模式还支持向构造函数传递参数,可谓集两种模式之长,我们来看看用这个方式重写的上面的例子:

 1 function Person(name,age,job){2        this.name=name;3        this.age=age;4        this.job=job;5        this.friends=["zhangsan","lisi"];6 } 7 Person.prototype={ 8  constructor:Person, 9 sayname: function(){ 10 alert(this.name); 11  } 12 } 13 14 var person1=new Person("Sleipnir",23,"Software"); 15 var person2=new Person("xiaoxiao",25,"Student"); 16 17 person1.friends.push("van"); 18 console.log(person1.friends); //"zhangsan,lisi,van" 19 console.log(person2.friends); //"zhangsan,lisi" 20 console.log(person1.fgriends===person2.friends); //false 21 console.log(person1.sayname===person2.sayname); //true

这种构造模式和原型混合的模式,是目前使用最多的一种方法,可以说,这是用来定义引用类型的一种默认模式。

5.动态原型模式

这种模式是解决了在构造器中查看并初始化原型的问题,它的动态性在于,在构造器中检查一下原型中是否存在某个方法,如果不存在,就建立下,建立一次就行了,一劳永逸,这就是其方便之处,我们同样来看个例子:

 1 function Person(name,age,job){2          this.name=name;3          this.age=age;4          this.job=job;5       6          //检查原型中是否有方法7          if(typeof this.sayname!="function"){8             Person.prototype.sayname=function(){ 9 alert(this.name); 10  }; 11  } 12 }

转载于:https://www.cnblogs.com/sleipnir-0430/p/8562948.html

Javascript之 对象和原型相关推荐

  1. javascript 本地对象和内置对象_JavaScript 的面向对象

    图片来源于 DigitalOcean 1. 什么是类 在说 JavaScript 的面向对象的实现方法之前,我们先来看面向对象编程的一个核心概念--类(class).类是对拥有同样属性(propert ...

  2. javascript原型对象、原型链、构造函数

    1.原型对象(原型).原型链 先放一张在网上看到的关于原型和原型链的图,非常不错. 如果你能看懂,那就对原型链有了一定了解,如果看不懂,对照下面这几点来看图: js中所有函数都有一个prototype ...

  3. Javascript中的对象和原型(一)(转载)

    面向对象的语言(如Java)中有类的概念,而通过类可以创建任意多个具有相同属性和方法的对象.但是,JavaScript 没有类的概念,因此它的对象也与基于类的语言中的对象有所不同. 要了解面向对象,首 ...

  4. JavaScript基础09-day11【原型对象、toString()、垃圾回收、数组、数组字面量、数组方法】

    学习地址: 谷粒学院--尚硅谷 哔哩哔哩网站--尚硅谷最新版JavaScript基础全套教程完整版(140集实战教学,JS从入门到精通) JavaScript基础.高级学习笔记汇总表[尚硅谷最新版Ja ...

  5. 《JavaScript专家编程》——第1章 对象和原型 1.1鸟瞰JavaScript

    本节书摘来自异步社区<JavaScript专家编程>一书中的第1章,第1.1节,作者:[美]Mark Daggett(达格特)著,更多章节内容可以访问云栖社区"异步社区" ...

  6. 小汤学编程之JavaScript学习day04——自定义对象、原型与原型链、BOM

    一.自定义对象 1.对象的定义     2.对象的使用 二.原型与原型链 1.JS的继承结构图     2.关键点 三.BOM 1.window对象     2.document对象     3.lo ...

  7. JavaScript学习笔记之原型对象

    本文是学习<JavaScript高级程序设计>第六章的笔记. JS中,便于批量创建对象的三种模式: 1.工厂模式:用一个函数封装创建对象的细节,传入必要的参数,在函数内部new一个对象并返 ...

  8. JavaScript 原型对象和原型链

    开篇 之前对js中的原型链和原型对象有所了解,每当别人问我什么是原型链和原型对象时,我总是用很官方(其实自己不懂)的解释去描述.有一句话说的好:如果你不能把一个很复杂的东西用最简单的话语描述出来,那就 ...

  9. 深入理解Javascript中构造函数和原型对象的区别

    在 Javascript中prototype属性的详解 这篇文章中,详细介绍了构造函数的缺点以及原型(prototype),原型链(prototype chain),构造函数(constructor) ...

最新文章

  1. 0525 项目回顾7.0
  2. (21) java web的struts2框架的使用-Action实现的三种方式
  3. 京东 你访问的页面需要验证证书_SSL证书安全认证有什么原理?
  4. SpeedyCloud研发总监李孟:不要让底层细节被上层打败
  5. hdu 4530(数学)
  6. win10怎么放计算机在桌面,win10我的电脑怎么放在桌面
  7. 实现sessionfilter_session应用--采用filter和session实现简单用户权限控制
  8. mysql 登录默认实例_【MySQL案例】mysql本机登录-S失灵_mysql
  9. 20172319 实验三《查找与排序》实验报告
  10. ORA-12170: TNS: 连接超时 问题
  11. MongoDB 安全与认证
  12. python字典统计排序1_python笔记17-字典如何按value排序
  13. 两种播放m3u8链接的方法
  14. 通过xml方式根据word模板导出word
  15. 无线桥接后无法访问服务器,无线桥接后不能登录副路由器ip地址的解决方法
  16. HTML和CSS实现京东首页(html和css详解)
  17. Mac无法连接wifi,重置wifi模块
  18. 2022年数维杯国际大学生数学建模挑战赛报名通知
  19. 逃避不一定躲得过,面对不一定最难过
  20. 踢球还是搞笑?这是一届锦鲤和乌龙齐飞的亚洲杯

热门文章

  1. 线性回归python代码实现
  2. 【CyberSecurityLearning 78】DC系列之DC-9渗透测试
  3. 「 每日一练,快乐水题 」191. 位1的个数
  4. 从JDK9的Flow接口说起
  5. SpringBoot 配置绑定
  6. springboot学习笔记(六)
  7. 操作系统(三十)避免死锁
  8. C语言再学习 -- 关闭/启动
  9. 【译】Ethereum Wallet in a Trusted Execution Environment / Secure Enclave
  10. 安天移动安全发布“大脏牛”漏洞分析报告(CVE-2017-1000405)