Javascript之创建对象(原型模式)

    我们创建的每个函数都有一个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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/378354.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

两种方法将Android NDK samples中hello-neon改成C++

一、第一种方法:1.修改helloneon.c 中代码 a.将 char* str; 改为 char str[512] {0}; b.将 asprintf(&str, "FIR Filter benchmark:\nC version : %g ms\n", time_c); 改为 sprintf(str, "FIR Filter benchmark:\nC ve…

【视觉项目】【day6】8.26关于matchTemplate()以及NCC的思考整理

NCC与matchTemplate()函数中match_method TM_CCOEFF_NORMED是否一样? 先看公式: TM_CCOEFF_NORMED NCCTM_CCOEFF_NORMED:归一化的相关性系数匹配方法 NCC:normalized cross correlation:归一化互相关系数 公式是一样的。 参考: 模板匹配的几…

HTTP 状态代码

如果向您的服务器发出了某项请求要求显示您网站上的某个网页(例如,当用户通过浏览器访问您的网页或在 Googlebot 抓取该网页时),那么,您的服务器会返回 HTTP 状态代码以响应该请求。 此状态代码提供了有关请求状态的信…

TensorFlow的可训练变量和自动求导机制

文章目录一些概念、函数、用法TensorFlow实现一元线性回归TensorFlow实现多元线性回归一些概念、函数、用法 对象Variable 创建对象Variable: tf.Variable(initial_value,dtype)利用这个方法,默认整数为int32,浮点数为float32,…

django第二个项目--使用模板做一个站点访问计数器

上一节讲述了django和第一个项目HelloWorld,这节我们讲述如何使用模板,并做一个简单的站点访问计数器。 1、建立模板 在myblog模块文件夹(即包含__init__.py的文件夹)下面新建一个文件夹templates,用于存放HTML模板,在…

c语言math乘法,JavaScript用Math.imul()方法进行整数相乘

1. 基本概念Math.imul()方法用于计算两个32位整数的乘积,它的结果也是32位的整数。JavaScript的Number类型同时包含了整数和浮点数,它没有专门的整型和浮点型。因此,Math.imul()方法能提供类似C语言的整数相乘的功能。我们将Math.imul()方法的…

梯度下降法预测波士顿房价以及简单的模型评估

目录原理代码关于归一化的思考原理 观察数据可知属性之间差距很大,为了平衡所有的属性对模型参数的影响,首先进行归一化处理。 每一行是一个记录,每一列是个属性,所以对每一列进行归一化。 二维数组归一化:1、循环方式…

Windows Phone 内容滑动切换实现

在新闻类的APP中,有一个经常使用的场景:左右滑动屏幕来切换上一条或下一条新闻。 那么通常我们该使用哪种方式去实现呢?可以参考一下Demo的实现步骤。 1,添加Windows Phone用户自定义控件。例如: 这里我为了演示的方便…

使用鸢尾花数据集实现一元逻辑回归、多分类问题

目录鸢尾花数据集逻辑回归原理【1】从线性回归到广义线性回归【2】逻辑回归【3】损失函数【4】总结TensorFlow实现一元逻辑回归多分类问题原理独热编码多分类的模型参数损失函数CCETensorFlow实现多分类问题独热编码计算准确率计算交叉熵损失函数使用花瓣长度、花瓣宽度将三种鸢…

【神经网络计算】——神经网络实现鸢尾花分类

本blog为观看MOOC视频与网易云课堂所做的笔记 课堂链接: 人工智能实践:TensorFlow笔记 吴恩达机器学习 疑问与思考 为什么按照batch喂入数据 之前看的视频里面处理数据都是一次性将所有数据喂入,现在看的这个视频对数据进行了分组投入。这是为何&#…

c# xaml语言教程,c#学习之30分钟学会XAML

1.狂妄的WPF相对传统的Windows图形编程,需要做很多复杂的工作,引用许多不同的API。例如:WinForm(带控件表单)、GDI(2D图形)、DirectXAPI(3D图形)以及流媒体和流文档等,都需要不同的API来构建应用程序。WPF就是看着上面的操作复杂和…

.NET 小结之内存模型

.NET 小结之内存模型 为什么要解.NET 的内存模型 在.NET下的内存管理、垃圾回收其实大部分不需要我们操心,因为大部分.NET已经帮我们做了,通常情况下也不需要考虑这些。但是如果想要了解一些.NET一些稍微“底层”的原理,如:“装箱…

【电设控制与图像训练题】【激光打靶】【openmv测试代码以及效果】

9.4加入串口通讯,送出靶心坐标、激光坐标、激光所在环数、方位;加入防误判操作 博主联系方式: QQ:1540984562 QQ交流群:892023501 群里会有往届的smarters和电赛选手,群里也会不时分享一些有用的资料,有问题可以在群里多问问。 目录 规则坐标系代码总结相关openmv使用文…

MVC3中的视图文件

在MVC3中的视图部分,Razor视图引擎是与以往不同的地方之一,使用Razor的视图文件再也不是以往的ASPX文件了,是cshtml文件,在新建视图的时候也会发现增加多了几类文件 由上到下分别是 MVC 3 Layout Page:与原来Web Form的…

C语言 链表拼接 PTA,PTA实验 链表拼接 (20point(s))

本题要求实现一个合并两个有序链表的简单函数。链表结点定义如下:struct ListNode {int data;struct ListNode *next;};函数接口定义:struct ListNode *mergelists(struct ListNode *list1, struct ListNode *list2);其中list1和list2是用户传入的两个按…

【TensorFlow学习笔记:神经网络优化(6讲)】

目录【1】NN复杂度【2】指数衰减学习率【3】激活函数优秀激活函数所具有的特点常见的激活函数对于初学者的建议【4】损失函数【5】缓解过拟合——正则化【6】参数优化器【1】SGD【2】SGDM(SGD基础上增加了一阶动量)【3】Adagrade(SGD基础上增加了二阶动量)【4】RMSProp(SGD基础…

第十章 开箱即用

第十章 开箱即用 “开箱即用”(batteries included)最初是由Frank Stajano提出的,指的是Python丰富的标准库。 模块 使用import将函数从外部模块导入到程序中。 import math math.sin(0)#结果为:0.0模块就是程序 在文件夹中创…

Openmv通过串口接收数据、发送数据与stm32通信

博主联系方式: QQ:1540984562 QQ交流群:892023501 群里会有往届的smarters和电赛选手,群里也会不时分享一些有用的资料,有问题可以在群里多问问。 目录 参考接线星瞳教程openmv传送数据STM32解码程序参考 接线 星瞳教程

c语言尹宝林答案,c程序设计导引 尹宝林

《C程序设计导引》特别适合作为计算机和非计算机专业学生学习高级语言程序设计的教材,也可供计算机等级考试者和其他各类学习者使用参考。17.40定价:44.75(3.89折)/2013-05-01《大学计算机优秀教材系列:C程序设计导引》是一本讲解C程序设计的…

第十一章 文件

第十一章 文件 打开文件 当前目录中有一个名为beyond.txt的文本文件,打开该文件 调用open时,原本可以不指定模式,因为其默认值就是’r’。 import io f open(beyond.txt)文件模式 值描述‘r’读取模式(默认值)‘w…