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,一经查实,立即删除!

相关文章

treeset java_Java TreeSet pollLast()方法与示例

treeset javaTreeSet类pollLast()方法 (TreeSet Class pollLast() method) pollLast() method is available in java.util package. pollLast()方法在java.util包中可用。 pollLast() method is used to return the last highest element and then remove the element from thi…

第五章 条件、循环及其他语句

第五章 条件、循环及其他语句 再谈print和import print现在实际上是一个函数 1,打印多个参数 用逗号分隔,打印多个表达式 sep自定义分隔符,默认空格 end自定义结束字符串,默认换行 print("beyond",yanyu,23)#结果为…

两种方法将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:归一化互相关系数 公式是一样的。 参考: 模板匹配的几…

linux待机流程,Linux睡眠喚醒機制--Kernel態

一、對於休眠(suspend)的簡單介紹 在Linux中,休眠主要分三個主要的步驟: 1) 凍結用戶態進程和內核態任務2) 調用注冊的設備的suspend的回調函數, 順序是按照注冊順序3) 休眠核心設備和使CPU進入休眠態, 凍結進程是內核把進程列表中所有的進程的狀態都設置為停止,並且保存下…

strictmath_Java StrictMath log1p()方法与示例

strictmathStrictMath类log1p()方法 (StrictMath Class log1p() method) log1p() method is available in java.lang package. log1p()方法在java.lang包中可用。 log1p() method is used to return (the logarithm of the sum of the given argument and 1 like log(1d) in th…

第六章 抽象

第六章 抽象 自定义函数 要判断某个对象是否可调用,可使用内置函数callable import math x 1 y math.sqrt callable(x)#结果为:False callable(y)#结果为:True使用def(表示定义函数)语句,来定义函数 …

HTTP 状态代码

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

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

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

linux samba安装失败,用aptitude安装samba失败

版本:You are using Ubuntu 10.04 LTS- the Lucid Lynx - released in April 2010 and supported until April 2013.root下执行aptitude install sambaReading package lists... DoneBuilding dependency treeReading state information... DoneReading extended st…

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

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

strictmath_Java StrictMath log10()方法与示例

strictmathStrictMath类log10()方法 (StrictMath Class log10() method) log10() method is available in java.lang package. log10()方法在java.lang包中可用。 log10() method is used to return the logarithm of the given (base 10) of the given argument in the method…

30、深入理解计算机系统笔记,并发编程(concurrent)(2)

1、共享变量 1)线程存储模型 线程由内核自动调度,每个线程都有它自己的线程上下文(thread context),包括一个惟一的整数线程ID(Thread ID,TID),栈,栈指针,程序…

PostgreSQL在何处处理 sql查询之十三

继续: /*--------------------* grouping_planner* Perform planning steps related to grouping, aggregation, etc.* This primarily means adding top-level processing to the basic* query plan produced by query_planner.** tuple_fraction i…

【视觉项目】基于梯度的NCC模板匹配代码以及效果

文章目录流程分析工程代码【1】NCC代码【Ⅰ】sttPxGrdnt结构体【Ⅱ】sttTemplateModel模板结构体【Ⅲ】calcAccNCC计算ncc系数函数【Ⅳ】searchNcc NCC模板匹配函数【Ⅴ】searchSecondNcc 二级搜索:在某一特定点周围再以步进为1搜索【2】测试图转外轮廓【Ⅰ】孔洞填…

第七章 再谈抽象

第七章 再谈抽象 对象魔法 多态:可对不同类型的对象执行相同的操作,而这些操作就像“被施了魔法”一样能够正常运行。(即:无需知道对象的内部细节就可使用它)(无需知道对象所属的类(对象的类型)就能调用其…

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

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

java scanner_Java Scanner nextLong()方法与示例

java scanner扫描器类的nextLong()方法 (Scanner Class nextLong() method) Syntax: 句法: public long nextLong();public long nextLong(int rad);nextLong() method is available in java.util package. nextLong()方法在java.util包中可用。 nextLong() method…

技术总监和CTO的区别 浅谈CTO的作用----软件公司如何开源节流(一)

我一直在思考软件公司如何开源节流。当然,老板也在思考开源节流。当然,老板思考的开源节流在公司运营层面上,而我作为CTO,我考虑的则是在产品运营角度上来思考这个问题。否则,一个软件公司,它的生存与发展就…

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

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