【JS】JavaScript中对象的创建与原型模式



ECMAScript中没有类的概念,因此它的对象与基于类的语言中的对象有所不同。

1.理解对象

创建对象最简单的方式是创建一个Object实例,再为它添加属性和方法,如下:

<span style="font-family:SimSun;font-size:12px;">var people = new Object();
people.age = 20;
people.sayAge = function(){
    alert(this.age);
};</span>

以对象字面量的方式创建对象:

<span style="font-family:SimSun;font-size:12px;">var people = {
    age:20,
    sayAge:function(){
        alert(this.age);
    }
};</span>

2.创建对象

工厂模式:

<span style="font-family:SimSun;font-size:12px;">function createPeople(name, age){
    var o = new Object();
    o.name = name;
    o.age = age;
    o.sayAge = function(){
        alert(this.age);
    };
    return o;
}

var p1 = createPeople("AA", 19);
var p2 = createPeople("BB", 19);</span>

使用工厂模式创建对象的不足在于无法知道一个对象的类型,没解决对象识别的问题(对象都Object类型,这里只是人为给该方法命名为createPeople,使创建出的对象”看似“people),因此引出下一种模式;

构造函数模式:

<span style="font-family:SimSun;font-size:12px;">function People(name, age){
    this.name = name;
    this.age = age;
    this.sayAge = function(){
        alert(this.age);
    };
}

var p3 = new People("CC", 21);
var p4 = new People("DD", 22);</span>

使用该模式创建People的实例,要使用new操作符,调用构造函数实际上经过以下四步骤:

  1. 创建新对象;
  2. 将构造函数作用域赋给新对象(因此this指向了这个新对象);
  3. 为新对象添加属性(执行构造函数中的代码);
  4. 返回新对象

使用该模式没有显式创建对象、直接将属性和方法赋给this对象、没return语句。

上面p3、p4都保存有People的一个不同的实例,p3、p4都有一个constructor(构造函数)属性,指向People,如下:

<span style="font-family:SimSun;font-size:12px;">alert(p3.constructor == People);  // true</span>

下面使用instanceof操作符检测对象类型,如下:

<span style="font-family:SimSun;font-size:12px;">alert(p3 instanceof People);  // true
</span>

创建自定义的构造函数可以将它的实例标识为一种特定的类型,解决了工厂模式解决不了的对象识别问题。

对于任何函数,能通过new操作符调用的,就可以作为构造函数;不能有通过new操作符调用的,就跟普通函数一样。

<span style="font-family:SimSun;font-size:12px;">// 作为普通函数调用
People("EE", 23);  // 添加到了window
window.sayAge();  // "EE"

// 在另一个对象的作用域中调用(另一个对象作为调用者)
var o = new Object();
People.call(o, "FF", 24);
o.sayAge();  // 24</span>

上面第一个例子中,因为在全局作用域中调用一个函数时,this对象是指向Global对象的,在浏览器中即是window对象,因此可以通过window对象调用sayAge()方法。

虽然使用构造函数创建对象解决了对象类型识别问题,但这种模式不足就在于每个方法都要在每个实例上重新创建一遍。因为在ECMAScript中的函数也是对象,每定义一个函数也是实例化了一个对象,因此p3、p4中的sayAge()方法并不是同一个Function实例,上面使用构造函数创建对象的模式在逻辑上其实与以下的是等价的。

<span style="font-family:SimSun;font-size:12px;">function People(name, age){
    this.name = name;
    this.age = age;
    this.sayAge = new Function("alert(this.age)");
}
alert(p3.sayAge == p4.sayAge );  // false</span>

使用这种方式,不同实例上的同名函数是不相等的,这时可以用下面的方式解决这问题:

<span style="font-family:SimSun;font-size:12px;">function People(name, age){
    this.name = name;
    this.age = age;
    this.sayAge = sayAge;
}

function sayAge(){
    alert(this.age);
};

alert(p3.sayAge == p4.sayAge );  // 使用这种方式会返回true</span>

这样把sayAge()函数的定义定义在构造函数外部,在构造函数里将sayAge属性等于全局的sayAge()函数,这样sayAge属性包含的是一个指向sayAge()函数的指针,因此p3、p4就共享了这个全局的sayAge()函数;那问题又来了,如果对象需要定义很多方法时,那么将要定义很多个全局函数,在此引出下一种模式;

原型模式:

先看一个例子:

<span style="font-family:SimSun;font-size:12px;">function People(){}

People.prototype.age = 20;
People.prototype.sayAge = function(){
    alert(this.age);
};

var p5 = new People();
p5.sayAge();  // 20

var p6 = new People();
p6.sayAge();  // 20

alert(p5.sayAge == p6.sayAge );  // true</span>

使用原型对象的好处在于可以让所有实例共享它所包含的属性和方法,不必在构造函数中定义对象的信息,而是将这些信息添加到原型对象上,这里的构造函数是空函数,属性和方法被加到People的prototype属性上,但是使用构造函数创建对象时,对象还是具有这些属性和方法,与传统构造函数不同的是,新对象的属性和方法是由所有实例共享的。要理解原型模式原理,还要先理解原型对象的性质。

每当创建一个函数时,都会为该函数创建一个prototype属性,这个属性指向函数的原型对象(原型对象的作用是包含可以由特定类型的所有实例共享的属性和方法);而所有原型对象都会自动获得一个构造函数属性(constructor),这个属性包含一个指向prototype属性所在函数的指针。前面的例子就是People.prototype.constructor指向People。

当调用构造函数创建一个新实例时,该实例 内部将包含一个指针,指向构造函数的原型对象,这个指针一般称为[[Prototype]]。这个连接存在于实例与构造函数的原型对象之间,而不是实例与构造函数间。

使用对象实例无法访问原型对象,例如p5.prototype是访问不到原型对象的,也没有方式可以访问[[Prototype]],不过一些浏览器在每个对象上都支持一个__proto__属性,通过该属性可以访问到原型对象(p5.__proto__)。

当访问对象的某一属性时,都会搜索给定名字的属性,首先搜索对象实例本身,如果找到给定名字的属性,则返回;若没找到,再继续搜索指针指向的原型对象,若找到,则返回。这也是多个对象实例共享原型保存的属性和方法的原理。如果人为给实例添加一个与原型中某一属性同名的属性,那么实例中的该属性将屏蔽原型中的那个属性,但原型的被屏蔽的属性的值还是不变的。

使用hasOwnProperty()方法可以用于检测一个属性是存在于实例中还是原型中,只有在属性存在于实例中时,该方法才返回true。hasOwnProperty()方法常被用于for-in循环中过滤原型中的属性,因为for-in循环默认返回的是所有能通过对象访问的、可枚举的(enumerated)属性,既包括实例中的属性也包括原型中的(这也是for-in循环与传统的for循环的区别)。

接下来使用对象字面量的方式重写上面的原型对象,如下:

<span style="font-family:SimSun;font-size:12px;">function People(){}

People.prototype = {
    age:20,
    sayAge:function(){
        alert(this.age);
    }
};</span>

上面将People.prototype设置为等于一个以对象字面量形式创建的新对象,结果也是相同的,唯一例外的是constructor属性不再指向People。因为这里使用{}创建原型对象,每次使用{}都会创建一个新对象,在这里{}就是对象(Object),new Object就相当于{},因为此时constructor属性指向的是Object。

<span style="font-family:SimSun;font-size:12px;">var people = new People();
alert(people.constructor == People);  // false
alert(people.constructor == Object);  // true</span>

使用构造函数与原型模式:

由前面的介绍可知,使用原型模式下所有实例的属性都是共享的,对其中一个实例的进行修改都会影响到其他实例的属性,但有时我们并不希望实例所有的属性都一样,这时就可以使用组合构造函数与原型模式一起使用,构造函数用于定义实例属性,而原型模式用于定义方法和共享属性。

<span style="font-family:SimSun;font-size:12px;">function People(name, age){
    this.name = name;
    this.age = age;
}
People.prototype = {
    sayAge:function(){
        alert(this.age);
    }
};

var p1 = new People('AA', 20);
p1.name = 'CC';  // 修改p1的name属性
var p2 = new People('BB', 21);

alert(p1.name);
alert(p2.name);
alert(p1.name == p2.name);  // false
alert(p1.sayAge == p2.sayAge);  // true</span>

Author:顾故

Sign:别输给曾经的自己

时间: 2024-10-27 11:34:04

【JS】JavaScript中对象的创建与原型模式的相关文章

javascript中对象的创建

本文是根据李炎恢老师的相关视频和讲义整理成的,如果感兴趣可以亲自去听李炎恢老师的相关课程.讲的很详细.下面是链接:javascript视频教程 1.一般方法: 创建一个对象,然后给这个对象新建属性和方法.  var box = new Object();                   //创建一个Object 对象      box.name = 'Lee';                           //创建一个name 属性并赋值      box.age = 100;   

创建型—原型模式

1.原型模式意图: 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 2.场景描述: 原型模式,利用实例自身的克隆功能来得到与原实例相同的新的实例. 设想西游记中的一个场景,唐僧师徒四人(白龙马除外),不过,该唐僧是个多事.喜欢使唤徒弟的唐僧.每当有事时,唐僧便会使唤他的三个徒弟去做.但是三个徒弟尽职尽责,为了保护师傅,必须留在唐僧身边.此时,多亏了三个徒弟能够千变万化,且都可通过毛发来变作自身(八戒.沙僧不知是否具有此功能?暂时看做有吧).当唐僧需要洗衣.化斋.喝水.探路.借宿

JavaScript中的数组创建

JavaScript中的数组创建 数组是一个包含了对象或原始类型的有序集合.很难想象一个不使用数组的程序会是什么样. 以下是几种操作数组的方式: 初始化数组并设置初始值 通过索引访问数组元素 添加新元素 删除现有元素 本文涵盖了数组的初始化以及设置初始值的操作.在JavaScript中要做到这一点的基本方法是使用数组字面量,例如[1, 5, 8]或是数组构造器new Array (1, 5, 8). 除了手动枚举之外,JavaScript还提供了更有趣更直接的数组创建方式.让我一起看看在Java

私人定制javascript中对象小知识点(Only For Me)

废话不多讲,先上笑话,然后再,.看懂这个的说明你的节操已经不再了. 晚饭后去理发店理发...割了吧...老板问我怎么剪,我悠悠的来一句往帅了剪...高潮往往令人想不到....旁边一在焗油烫头发的大妈说到 别这样为难老板,人家赚点钱不容易...首先如果你是高手那么请出门右转,如果你是菜鸟那么恭喜你,go on吧 全局对象在javascript程序中任何位置,都可以拈来就用的这种东西,是全局对象的属性.那么属性所在的对象也就是全局对象了.当javascript解释器启动时(或者任何Web浏览器加载新

JavaScript中对象类型的转换小结

对象到字符串和对象到数字类型的转换涉及到两个重要的方法,最终的转换结果会受到这两个方法返回结果的影响,这两个方法就是toString和valueOf.所有的对象都会从Object对象中继承到这两个方法.toString方法 用于返回对象的字符串表示(但是其实也可以不返回字符串).对于默认从Object继承而来的toString方法并不会返回太多有意义的内容.而valueOf方法目的是返回一个可以表示对象的原始类型值,但是由于对象的复杂性,大多数情况下根本不可能用一个原始类型值来表示,所以默认的v

JavaScript中对象属性的添加和删除

<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> <script type="text/javascript"> /* * 用.为对象添加属性 用关键字delete删除属性 用[]添加属性 和.的不同 r.name==r["name"]; r.

JVM中对象的创建过程

JVM中对象的创建过程如以下流程图中所示: 对其主要步骤进行详细阐述: 为新生对象分配内存: 内存的分配方式: 指针碰撞:假设Java堆中内存是绝对规整的,所有用过的内存放在一边,空闲的内存在另一边,中间防着一个指针作为分界的指示器,那么当分配内存时仅需移动指针即可. 空闲列表:维护一个列表,记录那些内存可用,分配时找出一块足够大的空间进行划分,并更新列表记录. 选择:内配方式的选择依赖于内存大小是否规整,内存大小的规整,依赖于垃圾收集器是否带有压缩整理功能. 并发情况下保证线程安全: 方法一:

Greenplum中定义数据库对象之创建与管理模式

创建与管理模式 概述:DB内组织对象的一种逻辑结构.一个DB内能够有多个模式.在未指定模式时默认放置在public中.能够通过"\dn"方式查看数据库中现有模式. testdw=# \dn List of schemas Name        |  Owner --------------------+--------- gp_toolkit         | gpadmin information_schema | gpadmin pg_aoseg           | gpa

「JavaScript里的面向对象」— 5.原型模式

本文原文来源:<Object-Oriented JavaScript>By Stoyan Stefanov 本文翻译来源:赤石俊哉 原创翻译 版权申明: 如果您是原文的原作者并且不希望此文被公开,可以联系作者删除.本文翻译由 赤石俊哉 翻译整理,您可以用于学习目的,但是禁止转载. 第五章 原型模式(Prototype) 在这一章节中你将会学习使用"函数(function)"对象中的prototype属性.在JavaScript的学习过程中,理解prototype的工作原理是