读Javascript高级程序设计第三版第六章面向对象设计--创建对象

虽然Object构造函数或者对象字面量都可以用来创建单个对象,但是缺点非常明显:使用同一接口创建很多对象,会产生大量重复代码。

工厂模式

 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     var person1=CreatePerson("Jim",29,"Teaching in Colleage");
12     var person2=CreatePerson("Merry",23,"Study in Colleage");
13     console.log(person1.sayName==person2.sayName);

解决了创建多个对象的问题,但是却没有解决对象识别的问题。

构造函数模式

 1 // 构造函数模式
 2     /*
 3         必须使用 new 操作符,会经过以下几个步骤
 4         1 创建一个新对象
 5         2 将构造函数的作用域赋值给新对象
 6         3 执行构造函数中的代码(为新对象添加属性)
 7         4 返回新对象
 8     */
 9     function PersonByConstructor(name,age,job){//按照惯例构造函数以大写字母开头
10         this.name=name;
11         this.age=age;
12         this.job=job;
13         this.sayName=function(){
14             alert(this.name);
15         };
16     }
17     var person3=new PersonByConstructor("John",24,"Software Engineer");
18     var person4=new PersonByConstructor("Greg",25,"Doctor");
19 
20     console.log(person3 instanceof PersonByConstructor);//返回true
21     console.log(person4 instanceof PersonByConstructor);

创建自定义的构造函数意味着将来可以将它的实例标识为一种特定的类型。

任何函数,只要通过new 操作符来调用,那它就可以作为构造函数;否则和普通函数一样。

1 PersonByConstructor("Lily",32,"Driver");//添加到window
2     window.sayName();
3 
4     var o=new Object();
5     PersonByConstructor.call(o,"Kristen",23,"Nurse");
6     o.sayName();

构造函数模式虽然好用,但是每个方法都要在每个实例上重新创建一遍。
可以将方法移到构造函数的外部解决,但是如对象要调用很多方法,则需要定义很多全局函数,这样就没有封装性可言了。这个问题可以通过原型模式来解决。

原型模式

我们创建的每一个函数都有原型属性(prototype),这个属性是一个指向对象的指针,这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。prototype就是通过调用构造函数而创建的那个对象实例的原型对象。使用对象原型的好处是让所有对象实例共享它所包含的属性和方法。

理解原型对象

  无论什么时候,只要创建一个对象,都会根据一定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。在默认情况下所有原型都会自动获取一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针。

function PersonByPrototype(){
 
    }
    PersonByPrototype.prototype={
        name:"Jim",
        age:21,
        job:"Teaching",
        sayName:function(){
            console.log(this.name);
        }
    }
    var person5=new PersonByPrototype();
    person5.sayName();
 
    var person6= new PersonByPrototype("Mick",32,"assitant");
    person6.sayName();
    console.log(PersonByPrototype.prototype.isPrototypeOf(person5));//通过 prototype.isPrototypeOf 来确定PersonByPrototype是否是person5的原型
    console.log(Object.getPrototypeOf(person5).name);//Object.getPrototypeOf可以方便的获取对象的原型,这在利用原型实现继承的情况下是非常重要的

每当代码读取某个对象的属性时,都会指向一次搜索,目标是具有给定名字的属性。搜索首先从对象实例本身开始,如果找到则返回该属性的值,如果没找到,则搜索指针指向原型对象,在原型对象查找具有给定名字的属性。我们对原型的修改会立刻的体现出来。
当为对象实例添加新的属性时,这个属性就会屏蔽 原型对象 中的那个同名属性,换句话说,会阻止我们访问原型中的属性,但不会修改那个属性。不过使用delete可以删除实例中的属性,从而使我们能够访问原型中的属性。

console.log(person6.hasOwnProperty("name"));    //hasOwnProperty 来判断属性是实例的还是原型继承来的
    console.log(person5.hasOwnProperty("name"));

原型与 IN 操作符
  在单独使用时,in 操作符会在通过对象能够访问给定属性时返回 true,无论该属性存在于实例中还是原型中。只要 in 操作符返回 true, 而 hasOwnProperty返回false,就可以确定属性是原型中的属性。

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

  在使用 for-in 循环时,返回的是通过对象访问的、可枚举的属性,其中包括实例和原型对象中的属性。

for (var prop in person5)
        console.log(prop);    //返回  name,age ,job, sayName

原型的动态性
  实例中的指针仅指向原型,而不是构造函数。

  如果把原型改为另外一个对象,相当于切断了构造函数和最初原先对象之间的联系。

原生对象的问题

  1 省略了为构造函数传参这一环节

  2 原型中所有属性都是被所有实例共享的。

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

  构造函数模式用于定义实例属性,原型模式来定义方法和共享的属性。

function PersonByConstructorAndPrototype(name,age,job){
            this.name=name;
        this.age=age;
        this.job=job;
        this.friends=[];
    }
    PersonByConstructorAndPrototype.prototype={
        constructor:PersonByConstructorAndPrototype,
        sayName:function(){console.log(this.name);}
    }

    var person7=new PersonByConstructorAndPrototype("LiLei",12,"Student");
    var person8 = new PersonByConstructorAndPrototype("LiuLei",13,"Student")
    person7.friends.push("LiuLei");
    person8.friends.push("LiLei");
    console.log(person7.friends.toString());
    console.log(person8.friends);

动态原型模式

  有其他 OO 语言经验的开发人员在看到独立的构造函数和原型时,很可能会感到非常困惑。动态原型模式正是致力于解决这个问题的一个方案,它把所有信息都封装在了构造函数中,而通过在构造函数中初始化原型(仅在必要的情况下),又保持了同时使用构造函数和原型的优点。换句话说,可以通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型。

寄生构造函数模式

  如果上面几种方式都不适用,可以适用寄生模式。该模式的核心思想是创建一个函数,该函数的作用仅仅是封装创建对象的代码。

//寄生构造函数模式
    function PersonByParasitic(name,age,job){
        var o=new Object();
        o.name=name;
        o.age=age;
        o.job=job;
        o.sayName=function(){
            console.log(this.name);
        };
        return o;
    }

    var friend=new PersonByParasitic("Nicholas",20,"Software Engineer");
    friend.sayName();
    var friend2=new PersonByParasitic("Nick",32,"");
    friend2.sayName();

这个模式可以在特殊情况下为对象创建构造函数。

稳妥构造函数模式

  稳妥对象指的是没有公共属性,而其方法也不引用this对象。

  

时间: 2024-10-24 19:02:10

读Javascript高级程序设计第三版第六章面向对象设计--创建对象的相关文章

《JavaScript高级程序设计 第三版》 前2章 Javascript简介与HTML 读书笔记

第一章:Javascript简介 1.JavaScript诞生于1995年,当时,它的主要目的是处理以前由服务器端语言(如Perl)负责的一些输入验证操作.现在,JavaScript是一种专为与网页交互而设计的脚本语言. 注:Netscape(网景)公司研发,Java是sun公司研发,原名为LiveScript,为了搭上媒体热炒的Java的顺风车,更名为JavaScript 2.微软推出JSript的和网景的JavaScript相竞争,最后微软胜利.ECMA指定了规定并重新命名为ECMAScri

Javascript高级程序设计读书笔记(第六章)

第6章  面向对象的程序设计 6.2 创建对象 创建某个类的实例,必须使用new操作符调用构造函数会经历以下四个步骤: 创建一个新对象: 将构造函数的作用域赋给新对象: 执行构造函数中的代码: 返回新对象. 构造函数的问题:每个方法都要在每个实例上重新创建一遍: 理解原型对象: 无论何时,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象.默认情况下,所有原型对象都会自动获得一个constructor属性,这个属性包含一个指向proto

21.1 XMLHttpRequest 对象【JavaScript高级程序设计第三版】

IE5 是第一款引入XHR 对象的浏览器.在IE5 中,XHR 对象是通过MSXML 库中的一个ActiveX对象实现的.因此,在IE 中可能会遇到三种不同版本的XHR 对象,即MSXML2.XMLHttp.MSXML2.XMLHttp.3.0 和MXSML2.XMLHttp.6.0.要使用MSXML 库中的XHR 对象,需要像第18章讨论创建XML 文档时一样,编写一个函数,例如: //适用于IE7 之前的版本 function createXHR() { if (typeof argumen

[已读]JavaScript高级程序设计(第3版)

从去年开始看,因为太长,总是没有办法一口气把它看完,再加上它与第二版大部分一致,读起来兴致会更缺一点. 与第二版相比,它最大的改变就是增加了很多html5的内容,譬如:Object对象的一些新东西,数据属性.访问器属性及相应的一些方法;比如它对跨域常见方法的比较和总结:postMessage,IE8的XDR,升级的XHR,jsonp跨域原理,单向的图片ping;又比如对数据推送的一些介绍,长轮询和http流是什么样子,单向的SSE和双向的Web Socket的详细介绍和比较. PS,关于SSE,

2.1 <script>元素【JavaScript高级程序设计第三版】

向 HTML 页面中插入 JavaScript 的主要方法,就是使用<script>元素.这个元素由 Netscape 创造并在 Netscape Navigator 2 中首先实现.后来,这个元素被加入到正式的 HTML 规范中. HTML 4.01 为<script>定义了下列 6 个属性. async:可选.表示应该立即下载脚本,但不应妨碍页面中的其他操作,比如下载其他资源或等待加载其他脚本.只对外部脚本文件有效. charset:可选.表示通过 src 属性指定的代码的字符

javascript高级程序设计第三版 读书笔记

第三章   基本概念 1.在JavaScript中是区分大小写的,第一个字符是字母 _ 或者$,其他字符可以试数字 字母 _ 或者$,命名格式要求是驼峰式书写(第一个字母小写,剩下的每个有意义的单词开头大写  比如fontSize) 2.单行注释//  块级注释为/*多行 内容*/ 不得使用关键字和保留字 3.在JavaScript中变量是松散型的   可以为任何一种类型 4.用var操作符定义的变量成为定义该变量的作用域中的局部变量. function test(){ var i = 'hi'

20.2 解析与序列化【JavaScript高级程序设计第三版】

JSON 之所以流行,拥有与JavaScript 类似的语法并不是全部原因.更重要的一个原因是,可以把JSON 数据结构解析为有用的JavaScript 对象.与XML 数据结构要解析成DOM 文档而且从中提取数据极为麻烦相比,JSON 可以解析为JavaScript 对象的优势极其明显.就以上一节中包含一组图书的JSON数据结构为例,在解析为JavaScript 对象后,只需要下面一行简单的代码就可以取得第三本书的书名: books[2].title 当然,这里是假设把解析JSON 数据结构后

模拟事件【JavaScript高级程序设计第三版】

事件,就是网页中某个特别值得关注的瞬间.事件经常由用户操作或通过其他浏览器功能来触发.但很少有人知道,也可以使用JavaScript 在任意时刻来触发特定的事件,而此时的事件就如同浏览器创建的事件一样.也就是说,这些事件该冒泡还会冒泡,而且照样能够导致浏览器执行已经指定的处理它们的事件处理程序.在测试Web 应用程序,模拟触发事件是一种极其有用的技术.DOM2 级规范为此规定了模拟特定事件的方式,IE9.Opera.Firefox.Chrome 和Safari 都支持这种方式.IE 有它自己模拟

4.1 基本类型和引用类型的值【JavaScript高级程序设计第三版】

ECMAScript 变量可能包含两种不同数据类型的值:基本类型值和引用类型值.基本类型值指的是简单的数据段,而引用类型值指那些可能由多个值构成的对象. 在将一个值赋给变量时,解析器必须确定这个值是基本类型值还是引用类型值.第3 章讨论了5 种基本数据类型:Undefined.Null.Boolean.Number 和String.这5 种基本数据类型是按值访问的,因为可以操作保存在变量中的实际的值. 引用类型的值是保存在内存中的对象.与其他语言不同,JavaScript 不允许直接访问内存中的