我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,它的用途是包含可以有特定类型的所有实例共享的属性和方法。

prototype就是通过构造函数而创建的那个对象的原型对象。使用原型的好处就是可以让所有对象实例共享它所包含的属性和方法 。

function Person() {
}
Person.prototype.name = "zxj";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function () {alert(this.name);
}
var person1 = new Person();
person1.sayName(); //zxj
var person2 = new Person();
person2.sayName(); //zxj

1、理解原型对象

    无论什么时候,只要创建了一个新函数,ECMAScript就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针。就拿前面的例子,Person.prototype.constructor指向Person。而通过这个构造函数,我们还可以继续为原型对象添加其他属性和方法。
    创建了自定义指针之后,其原型对象默认只会取得constructor属性;至于其它方法,都会从Object对象继承而来。当调用构造函数创建一个新实例之后,该实例的内部将包括一个指针(内部属性),指向构造函数的原型对象。ECMA-262第5版中管这个叫[[Prototype]]。
    要明确一点的就是,这个连接存在于实例和构造函数的原型对象之间,而不是存在于实例和构造函数之间。
    以前面使用的Person构造函数和Person.prototype创建实例的代码为例,如下图:
    上图展示了Person构造函数、Person的原型属性以及Person现有的两个实例之间的关系。在此,Person.prototype指向了原型对象,而Person.prototype.constructor又指回了Person。原型对象中除了包含constructor属性之外,还包括后来添加的其他属性。Person的每一个实例——person1和person2都包含一个内部属性,该属性仅仅指向Person.prototype。换句话说,它们与构造函数没有直接的联系。此外,要格外注意的是,虽然这两个实例都不包含属性和方法,但我们却可以调用person1.sayName()。这是通过查找对象属性的过程来实现的。
    虽然我们无法访问到[[Prototype]],但可以通过isPrototypeOf()方法来确定对象之间是否存在这种关系。从本质上讲,如果[[Prototype]]指向调用isPrototypeOf()方法的对象(Person.prototype),那么这个方法就会返回true。
alert(Person.prorotype.isPrototypeOf(person1)); //true
alert(Person.prorotype.isPrototypeOf(person2)); //true

    每当代码要读取某个对象的属性时,都会进行一次搜索,搜索目标是具有给定名称的属性。搜索当然先从对象实例的本身开始,如果找到了,就可以返回该值了;如果找不到,则会去指针所指向的原型对象中去查找,在原型对象中找到了,就可以顺利返回该值。而这正是多个对象实例共享原型所保存的属性和方法的基本原理。

    虽然可以通过对象实例访问到保存在原型中的值,但不能通过对象实例重写原型中的值。根据查找原理,如果找到了实例中的值,就不会再去查找原型对象中的值。,代码如下所示:
function Person() {
}
Person.prototype.name = "zxj";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.sayName = function () {alert(this.name);
}
var person1 = new Person();
var person2 = new Person();
person1.name = "Greg";
alert(person1.name); //Greg 来自实例
alert(person2.name); //zxj  来自原型
delete person1.name; //删除实例中的name属性
alert(person1.name); //zxj 来自原型

 使用hasOwnPeoperty()方法可以检测一个属性是否存在于实例中,还是存在原型中,这个方法(它是从Object继承来的)只在给定属性存在域对象实例中时,才返回true。

function Person() {
}
Person.prototype.name = "zxj";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.sayName = function () {alert(this.name);
}
var person1 = new Person();
var person2 = new Person();
alert(person1.hasOwnProperty("name"));  //false
person1.name = "Greg";
alert(person1.name); //Greg 来自实例
alert(person1.hasOwnProperty("name")); //true
alert(person2.name); //zxj  来自原型
alert(person2.hasOwnProperty("name")); //false
delete person1.name;
alert(person1.name); //zxj 来自原型
alert(person1.hasOwnProperty("name")); //false

2、原型与in操作符

    有两种方式使用in操作符:一、单独使用;二、for-in中使用。
    功能:会在通过对象能够访问给定属性时返回true,无论是在对象实例中或是原型中。
function Person() {
}
Person.prototype.name = "zxj";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.sayName = function () {alert(this.name);
}
var person1 = new Person();
var person2 = new Person();alert(person1.hasOwnProperty("name"));  //false
alert("name" in person1);  //true

person1.name = "Greg";alert(person1.name); //Greg 来自实例
alert(person1.hasOwnProperty("name")); //true
alert("name" in person1);  //true

alert(person2.name); //zxj  来自原型
alert(person2.hasOwnProperty("name")); //false
alert("name" in person2);  //truedelete person1.name;
alert(person1.name); //zxj 来自原型
alert(person1.hasOwnProperty("name")); //false
alert("name" in person1);  //true

alert(person1.hasOwnProperty("qqqq")); //false
alert("qqqq" in person1);  //false

    同时使用hasOwnProperty()和in操作符可以判断出该属性到底是存在对象实例中还是存在与原型中。

    使用for-in循环时,返回的是所有能够通过对象访问的,可枚举(enumerated)属性,其中即包括存在与实例中的属性,也包括存在与原型中的属性。根据规定,开发人员定义的属性都是可枚举的——IE8及更早版本除。
var o = {toString: function () {return "My Object";}
}
for (var prop in o) {if (prop == "toString") {alert("Found toString"); //在IE中不会显示(IE9(未测试)和IE10(已测试)可用)
    }
}

3、更简单的原型语法

    我们可以用一个包含属性和方法的对象字面量重写整个原型对象。
function Person(){
}Person.peototype={name:"zxj",age:29,job:"Software Engineer",sayName:function(){alert(this.name);}
};

    结果是与先前的相同,但有一个是不同的:contrcutor属性不再指向Person了。我们曾经介绍过,没创建一个函数,就会同时创建它的prototype对象,这个对象也会自动获得constructor属性。而我们这样写,本质上是完全重写了默认的prototype对象,因此constructor属性也就变成了新对象的constructor属性(指向Object构造函数),不再指向Person函数。

    当然我可以将它特意设置成适当的值:
function Person() {
}Person.prototype = {constructor: Person,name: "zxj",age: 29,sayName: function () {alert(this.name);}
};

    以上代码特意包含了一个constructor属性,并将它的值设置为Person,从而确保了通过该属性能够访问到适当的值。

    注意,以这种方式重设constructor属性会导致它的[[Enumerable]]特性被设置为true。默认情况下,constructor属性是不可枚举的。因此如果你使用兼容ECMASCript5的JavaScript引擎,可以试一试Object.definePropety()。
function Person() {
}Person.prototype = {constructor: Person,name: "zxj",age: 29,sayName: function () {alert(this.name);}
};Object.defineProperty(Person.prototype,"constructor",{enumerable:false,value:Person
});

4、原型的动态性

    由于在原型中查找值的过程是一次搜索,因此我们对原型对象所做的任何修改都能够立即从实例上反映出来——即使是先创建了实例后修改原型也照样可以,如下所示:
function Person() {
}var friend = new Person();Person.prototype.sayHi = function () {alert("hi");
}friend.sayHi(); //"hi"

    尽管可以随时为原型添加属性和方法,但如果我们重写了整个原型对象,那么情况就不一样了。我们知道,调用构造函数时会为实例添加一个指向最初原型的[[Prototype]]指针,而把原型修改为另一个对象就等于切断了构造函数与最初原型之间的联系。一定要记住:实例中的指针仅仅指向原型,而不是构造函数。如下例子:

function Person() {
}var friend = new Person();Person.prototype = {constructor: Person,name: "zxj",age: 29,job: "Software Engineer",sayName: function () {alert(this.name);}
};friend.sayName();   //error 找不到该方法

5、原生对象的原型

    原型模式的重要性不仅体现在创建自定义类型方面,就连所有原生的引用类型,都是采用这种模式创建的。所有引用类型(Object、Array、String,等等)都在构造函数的原型上定义了方法。例如,在Array.prototype中可以找到sort()方法,而在String.prototype中可以找到substring()方法,如下所示:
alert(typeof Array.prototype.sort); //"function"
alert(typeof String.prototype.substring); //"function"

    通过原生对象的原型,不仅可以取得所有默认方法的引用,而且也可以定义新方法。如下所示:

String.prototype.startWith = function (text) {return this.indexOf(text) == 0;
}var msg = "Hello World";alert(msg.startWith("Hello")); //true

6、原型对象的问题

    原型模式的缺点。首先,它省略了为构造函数传递初始化参数这一环节,结果所有的实例在默认情况下都取得了相同的值。原型模式最大的问题是由其共享的本性所导致的。
Person.prototype = {constructor: Person,name: "zxj",age: 29,job: "Software Engineer",friends: ["saly", "geil"],sayName: function () {alert(this.name);}
};var person1 = new Person();
var person2 = new Person();person1.friends.push("van");alert(person1.friends);  //"saly","geil","van"
alert(person2.friends);  //"saly","geil","van"

alert(person1.friends === person2.friends)  //问题出来了,person1结交了新朋友意味着person2也必须结交这个朋友

    我们可以看到,当一个对象想获取独有的操作时,原型模式的共享就是最大的阻碍。

 

转载于:https://www.cnblogs.com/zxj159/archive/2013/05/20/3089513.html

Javascript之创建对象(原型模式)相关推荐

  1. JavaScript为什么使用原型模式而不是类模式

    导言: 作为JavaScript初学者的本菜鸡而言,刚一开始接触这门语言我就被他的原型模式给吓到了.并且在相当长的一段时间之内,我都完全不能理解或者不能接受这个模式.直到最近经过多方调查和思考才有所明 ...

  2. JavaScript之创建对象的模式

    使用Object的构造函数可以创建对象或者使用对象字面量来创建单个对象,但是这些方法有一个明显的缺点:使用相同的一个接口创建很多对象,会产生大量的重复代码. (一)工厂模式 这种模式抽象了创建具体对象 ...

  3. 设计模式(7)[JS版]-JavaScript设计模式之原型模式如何实现???

    目录 1.什么是原型模式 2 参与者 3 实例讲解 4 使用 Object.create实现原型模式 4.1 Object.create()的用法 4.2 用 Object.create实现继承 4. ...

  4. JavaScript 面向对象 (prototype 原型模式)

    一. JavaScript 设计思想 1994年,网景公司(Netscape)发布了Navigator浏览器0.9版.这是历史上第一个比较成熟的网络浏览器,轰动一时.但是,这个版本的浏览器只能用来浏览 ...

  5. JavaScript --- [学习笔记] 原型模式

    说明 接JavaScript - > [学习笔记]观察者模式 & 理解对象 & 工厂模式 & 构造函数模式 上一篇构造函数模式创建的实例,不同实例的同一个方法是不相等的, ...

  6. js创建对象之原型模式2原型与in操作符

    <!DOCTYPE html> <html><head><meta charset="UTF-8"><title>< ...

  7. [转]JavaScript构造函数及原型对象

    JavaScript中没有类的概念,所以其在对象创建方面与面向对象语言有所不同. JS中对象可以定义为"无序属性的集合".其属性可以包含基本值,对象以及函数.对象实质上就是一组没有 ...

  8. 设计模式 6 - 原型模式及spring源码案例分析

    目录 原型模式 1. 原型模式的引入 2. 原型模式的介绍和使用场景 3. 原理结构图UML图 4. 代码演示 5. 原型模式在spring中的使用 6.深拷贝的实现方式 7. 小结 原型模式 博主一 ...

  9. javascript中原型模式创建对象特点分析

    在javascript中,我们创建的每个函数都有一个prototype(原型)属性.这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法.使用原型对象的好处 ...

  10. JavaScript:创建对象(原型模式和构造函数模式)

    JavaScript:对象 一.理解对象 var person = {}Object.defineProperty(person,'name',{writable:false,value : 'Nik ...

最新文章

  1. Matlab数据的可视化 -- 柱形立体图
  2. s2sh乱码一个小处理(新手按流程走)
  3. LeetCode 1985. 找出数组中的第 K 大整数(排序)
  4. android support library github,Android Support Library 之 夜间模式
  5. java 素数乘积,求助2424379123 = 两个素数的乘积,求这两个素数?
  6. 团队工作室展示官网源码带后台-源团V1.0版本
  7. java求圆弧切线_两个圆的公切线
  8. SpringMVC 全局异常处理的简单应用
  9. 【BZOJ1057】【codevs1428】棋盘制作,悬线法
  10. 4chan 爬虫_类似4chan网站
  11. python中none算变量吗_在python中对变量判断是否为None的三种方法总结
  12. Subtext--为skin准备相关文件加载
  13. Docker镜像构建
  14. Atitit attilax总结的对于attilax重要的jsr规范,以及需要增加的jsr规范
  15. V5-SP6:iocomp-32/iocomp-64-crack-免安装
  16. 百度wz竞价开户推广营销的四大好处
  17. OpenERP-指定动作视图
  18. SSH连接时候出现 REMOTE HOST IDENTIFICATION HAS CHANGED
  19. 微信小程序 | 基于ChatGPT实现电影推荐小程序
  20. Crossplane 和 Terraform 的区别

热门文章

  1. mysql中的replication_mysql中replication的相关问题总结
  2. radmin提示授权码过期_IdentityServer4 客户端授权模式(Client Credentials)
  3. elupload获取文件名与路径_Uipath获取文件名,路径,扩展名等操作
  4. Eigen(7)Map类
  5. CentOS 6.4安装pip,CentOS安装python包管理安装工具pip的方法
  6. C++读取文本文件中以TAB作为分隔符,且中间字段有为空的情况的方法?
  7. c语言定时器作用,Go语言定时器实现原理及作用
  8. 新生必会的linux命令,jstat命令详解
  9. AUTOSAR从入门到精通100讲(十一)-AUTOSAR NVM基础知识
  10. sql server2008如何修改mac地址_如何查看本机的MAC地址和IP地址?