JavaScript面向对象编程及思想

在面向对象程序设计(组装计算机)的过程中,通常不用关心对象(例如硬盘)数据的内部实现,这种内部实现我们称为对象的封装,这些数据我们称之为对象的属性或实例字段。

一个对象(硬盘)还要提供一些接口,好使外面程序(例如主板)调用(连接),这种可以调用对象的接口我们称之为方法或成员函数。

通过对象提供的方法可以调用对象内部属性来完成指定功能,这样就把前面介绍的程序设计最重要的数据与算法结合成了一个统一体,同时隐藏了对象中数据的实现过程,实现了当前最流行的面向对象编程思想。

何为对象
对象就是一种数据结构,包含了各种命名好的数据(属性),而且还可以包含对这些数据进行操作的方法(函数),一个对象将数据与方法组织到一个灵巧的对象包中,这样就大大增强了代码的模块性和可重用性,从而使程序设计更加容易,更加轻松。

由于JavaScript是松散类型的语言,因此可以动态地增加属性到对象中,这是Java,C#这些语言等做不到的。

对象也可以称作属性的容器(包括一系列属性),每个属性都包括一个名称(name)与值(value),属性的名称可以是任意字符串.

JavaScript所有对象的数据类型是object

面向对象模式:
1、创建对象,使用一个特定接口new Object()缺点:使用同一个接口创建很多对象,会产生大量重复的代码
2、使用工厂模式,用函数来封装,以特定接口创建对象如:function createObj(name,age){

 代码如下 复制代码
 var o = new Object();
 o.name = name;
 o.age = age;
 return o;
}
var o1 = createObj("yjh",23)

优点:解决了使用一个接口创建多个相似对象产生大量重复代码的问题缺点:没有解决对象识别的问题,即o1是怎么样的一个对象类型3、构造函数模式,JavaScript没有类概念如:function CreateObj(name,age){

 代码如下 复制代码
 this.name = name;
 this.age = age;
 this.sayName = function(){
  alert("hi" + this.name); 
 }
}
var obj1 = new CreateObj("yjh1",23);
var obj2 = new CreateObj("yjh2",23);

优点:解决了实例对象类型识别的问题,obj1,obj2对象为CreateObj类型缺点:构造函数定义的属性和方法不是所有实例所共享的,各实例调用的方法是两个不同Function类型的实例(obj1.sayName != obj2.sayName)4、原型模式如:function

 代码如下 复制代码
CreateObj(){
}
CreateObj.prototype = {
 constructor: CreateObj,
 name: "yjh",
 age: 23,
 colors: ["a","b"],
 sayName: function(){
  alert(this.name); 
 }
}
var obj1 = new CreateObj();
var obj2 = new CreateObj();
alert(obj1.sayName == obj2.sayName);//true
obj1.colors.push("c");
alert(obj2.colors);//a,b,c

说明:调用obj1,obj2实例的属性和方法,首先会搜索实例自身定义的属性和方法,如果没有,由于实例的__proto__内部属性指向原型,因此会继续搜索原型中定义的属性和方法优点:原型中定义的属性和方法是所有实例对象所共享的,解决了多个函数实现同一功能的问题缺点:如果在原型中定义的属性包含的是引用类型的值,那么通过修改一个实例属性值会影响到另一个实例属性值,这正是由于原型的共享本质所导致的5、组合模式(构造函数模式与原型模式)如:function

 代码如下 复制代码
CreateObj(name,age){
 console.log(this.name);//yjhyjh
 this.name = name;
 this.age = age;
 this.colors = ["a","b"];
}
CreateObj.prototype = {
 constructor: CreateObj,
 name: "yjhyjh",
 sayName: function(){
  return this.name;
 }
}
var obj1 = new CreateObj("yjh1",23);
var obj2 = new CreateObj("yjh2",23);
alert(obj1.sayName == obj2.sayName);//true
alert(obj1.sayName());//yjh1
alert(obj2.sayName());//yjh2
obj1.colors.push("c");
alert(obj2.colors);//a,b

说明:把所有实例不需要共享的属性定义在构造函数中,把需要共享的属性,方法定义在原型中,互补构造函数模式和原型模式的优缺点,原型是所有实例化对象的原型对象,实例与原型之间是通过实例内部属性__proto__连接到原型,所有实例共享原型中的属性和方法,而原型内部又包含一个constructor属性(指向构造函数),由于作用域链的关系,构造函数中的this对象也指向了原型对象,因此在构造函数中定义的this.name,this.age等属性和方法会覆盖原型中定义的属性和方法6、继承(实现继承,原型链)就是把一个构造函数的原型作为另一个构造函数的实例化对象,那么这个实例化原型对象就会继承另一个构造函数的原型属性和方法,这就是所谓的原型链如:

 代码如下 复制代码
function Fun1(){
 this.name = ["yjh1","yjh2"];
}
Fun1.prototype = {
 constructor: Fun1,
 sayName: function(){
  alert(this.name) 
 }
}
function Fun2(){}
Fun2.prototype = new Fun1();
var fun2 = new Fun2();
fun2.sayName();//yjh1,yjh2
fun2.name.push("yjh3"); //fun2.name = ["yjh1","yjh2","yjh3"];
var fun3 = new Fun2();
alert(fun3.name);//yjh1,yjh2,yjh3

缺点:来自包含引用类型值的原型,原型中定义的属性,方法是所有实例所共享的,Fun2.prototype原型对象是Fun1类型的一个实例,因此原型对象中有包含引用类型值的name属性,当实例化多个Fun2类型对象时,所有的实例对象都共享这个原型name属性,通过修改实例中的name属性值,会直接修改原型中定义的name属性值7、组合继承(继承与借用构造函数)如:

 代码如下 复制代码
function Fun1(){
 this.name = ["yjh1","yjh2"];
}
Fun1.prototype = {
 constructor: Fun1,
 sayName: function(){
  alert(this.name) 
 }
}
function Fun2(){
 Fun1.call(this); 
}
Fun2.prototype = new Fun1();
var fun2 = new Fun2();
fun2.sayName();//yjh1,yjh2
fun2.name.push("yjh3"); //fun2.name = ["yjh1","yjh2","yjh3"];
var fun3 = new Fun2();
alert(fun2.name);//yjh1,yjh2,yjh3
alert(fun3.name);//yjh1,yjh2

说明:由于构造函数中定义的属性,方法不是所有实例共享的,而且会覆盖原型中定义的属性与方法,所以它不会由于实例化原型对象(Fun2.prototype)中包含了引用类型值的属性而

Javascript是一种基于对象(object-based)的语言,你遇到的所有东西几乎都是对象。但是,它又不是一种真正的面向对象编程(OOP)语言,因为它的语法中没有class(类)。

那么,如果我们要把"属性"(property)和"方法"(method),封装成一个对象,甚至要从原型对象生成一个实例对象,我们应该怎么做呢?

1. 生成对象的原始模式

假定我们把猫看成一个对象,它有"名字"和"颜色"两个属性。

 代码如下 复制代码

  var Cat = {

    name : '',

    color : ''

  }

现在,我们需要根据这个原型对象,生成两个实例对象。

 代码如下 复制代码

  var cat1 = {}; // 创建一个空对象

    cat1.name = "大毛"; // 按照原型对象的属性赋值

    cat1.color = "黄色";

  var cat2 = {};

    cat2.name = "二毛";

    cat2.color = "黑色";

好了,这就是最简单的封装了。但是,这样的写法有两个缺点,一是如果多生成几个实例,写起来就非常麻烦;二是实例与原型之间,没有任何办法,可以看出有什么联系。

2. 原始模式的改进

我们可以写一个函数,解决代码重复的问题。

 

 代码如下 复制代码

 function Cat(name,color){

    return {

      name:name,

      color:color

    }

  }

然后生成实例对象,就等于是在调用函数:

 

 代码如下 复制代码

 var cat1 = Cat("大毛","黄色");

  var cat2 = Cat("二毛","黑色");

这种方法的问题依然是,cat1和cat2之间没有内在的联系,不能反映出它们是同一个原型对象的实例。

3. 构造函数模式

为了解决从原型对象生成实例的问题,Javascript提供了一个构造函数(Constructor)模式。

所谓"构造函数",其实就是一个普通函数,但是内部使用了this变量。对构造函数使用new运算符,就能生成实例,并且this变量会绑定在实例对象上。

比如,猫的原型对象现在可以这样写,

 代码如下 复制代码

  function Cat(name,color){

    this.name=name;

    this.color=color;

  }

我们现在就可以生成实例对象了。

 代码如下 复制代码

  var cat1 = new Cat("大毛","黄色");

  var cat2 = new Cat("二毛","黑色");

  alert(cat1.name); // 大毛

  alert(cat1.color); // 黄色

这时cat1和cat2会自动含有一个constructor属性,指向它们的构造函数。

 代码如下 复制代码

  alert(cat1.constructor == Cat); //true

  alert(cat2.constructor == Cat); //true

Javascript还提供了一个instanceof运算符,验证原型对象与实例对象之间的关系。

 代码如下 复制代码

  alert(cat1 instanceof Cat); //true

  alert(cat2 instanceof Cat); //true

4. 构造函数模式的问题

构造函数方法很好用,但是存在一个浪费内存的问题。

请看,我们现在为Cat对象添加一个不变的属性"type"(种类),再添加一个方法eat(吃老鼠)。那么,原型对象Cat就变成了下面这样:

 代码如下 复制代码

  function Cat(name,color){

    this.name = name;

    this.color = color;

    this.type = "猫科动物";

    this.eat = function(){alert("吃老鼠");};

  }

还是采用同样的方法,生成实例:

 代码如下 复制代码

  var cat1 = new Cat("大毛","黄色");

  var cat2 = new Cat ("二毛","黑色");

  alert(cat1.type); // 猫科动物

  cat1.eat(); // 吃老鼠

表面上好像没什么问题,但是实际上这样做,有一个很大的弊端。那就是对于每一个实例对象,type属性和eat()方法都是一模一样的内容,每一次生成一个实例,都必须为重复的内容,多占用一些内存。这样既不环保,也缺乏效率。

 代码如下 复制代码

  alert(cat1.eat == cat2.eat); //false

能不能让type属性和eat()方法在内存中只生成一次,然后所有实例都指向那个内存地址呢?回答是可以的。

5. Prototype模式

Javascript规定,每一个构造函数都有一个prototype属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。

这意味着,我们可以把那些不变的属性和方法,直接定义在prototype对象上。

 代码如下 复制代码

  function Cat(name,color){

    this.name = name;

    this.color = color;

  }

  Cat.prototype.type = "猫科动物";

  Cat.prototype.eat = function(){alert("吃老鼠")};

然后,生成实例。

 代码如下 复制代码

  var cat1 = new Cat("大毛","黄色");

  var cat2 = new Cat("二毛","黑色");

  alert(cat1.type); // 猫科动物

  cat1.eat(); // 吃老鼠

这时所有实例的type属性和eat()方法,其实都是同一个内存地址,指向prototype对象,因此就提高了运行效率。

 

 代码如下 复制代码
 alert(cat1.eat == cat2.eat); //true

6. Prototype模式的验证方法

6.1 isPrototypeOf()

这个方法用来判断,某个proptotype对象和某个实例之间的关系。

 代码如下 复制代码

  alert(Cat.prototype.isPrototypeOf(cat1)); //true

  alert(Cat.prototype.isPrototypeOf(cat2)); //true

6.2 hasOwnProperty()

每个实例对象都有一个hasOwnProperty()方法,用来判断某一个属性到底是本地属性,还是继承自prototype对象的属性。

 代码如下 复制代码

  alert(cat1.hasOwnProperty("name")); // true

  alert(cat1.hasOwnProperty("type")); // false

6.3 in运算符

in运算符可以用来判断,某个实例是否含有某个属性,不管是不是本地属性。

 

 代码如下 复制代码

 alert("name" in cat1); // true

  alert("type" in cat1); // true

in运算符还可以用来遍历某个对象的所有属性。

 代码如下 复制代码

  for(var prop in cat1) { alert("cat1["+prop+"]="+cat1[prop]); }

时间: 2024-11-05 19:33:58

JavaScript面向对象编程及思想的相关文章

Javascript面向对象编程(二):构造函数的继承

Javascript面向对象编程(二):构造函数的继承 作者: 阮一峰 日期: 2010年5月23日 这个系列的第一部分,主要介绍了如何"封装"数据和方法,以及如何从原型对象生成实例. 今天要介绍的是,对象之间的"继承"的五种方法. 比如,现在有一个"动物"对象的构造函数. function Animal(){ this.species = "动物"; } 还有一个"猫"对象的构造函数. function

Javascript面向对象编程(三):非构造函数的继承

Javascript面向对象编程(三):非构造函数的继承 作者: 阮一峰 日期: 2010年5月24日 这个系列的第一部分介绍了"封装",第二部分介绍了使用构造函数实现"继承". 今天是最后一个部分,介绍不使用构造函数实现"继承". 一.什么是"非构造函数"的继承? 比如,现在有一个对象,叫做"中国人". var Chinese = { nation:'中国' }; 还有一个对象,叫做"医生&qu

js面向对象 编程: JavaScript 面向对象编程,严格过程的标准化编程法,目前为止最好的 JS 生成对象代码结构

市面上流行了很多 JavaScript 面向对象的编程方法,其中不少都有好些问题.这里总结最正确的 JavaScript 面向对象编程模式.对于类 Special 继承自类 Common 继承自类 Super 的情况,一个 Special 对象的创建,详细说来,应该经历以下步骤.1 确定继承关系1.1 读取 Special 的父类,发现是 Common1.2 读取 Common 的父类,发现是 Super1.3 读取 Super 的父类,发现没有了(隐形父类 Object)2 加载类结构(如果没

深入剖析JavaScript面向对象编程_javascript技巧

二. Javascript 面向对象编程:构造函数的继承 本节主要介绍,如何生成一个"继承"多个对象的实例. 比如,现在有一个"动物"对象的构造函数, function Animal(){ this.species = "动物"; } 还有一个"猫"对象的构造函数, function Cat(name,color){ this.name = name; this.color = color; } 怎样才能使"猫&qu

Javascript 面向对象编程

Javascript 面向对象编程 2012年1月9日陈皓 发表评论阅读评论 49,161 人阅读     Javascript是一个类C的语言,他的面向对象的东西相对于C++/Java比较奇怪,但是其的确相当的强大,在 Todd 同学的"对象的消息模型"一文中我们已经可以看到一些端倪了.这两天有个前同事总在问我Javascript面向对象的东西,所以,索性写篇文章让他看去吧,这里这篇文章主要想从一个整体的角度来说明一下Javascript的面向对象的编程.(成文比较仓促,应该有不准确

Javascript 面向对象编程(一):封装

Javascript 面向对象编程(一):封装  作者: 阮一峰 日期: 2010年5月17日 学习Javascript,最难的地方是什么? 我觉得,Object(对象)最难.因为Javascript的Object模型很独特,和其他语言都不一样,初学者不容易掌握. 下面就是我的学习笔记,希望对大家学习这个部分有所帮助.我主要参考了以下两本书籍: <面向对象的Javascript>(Object-Oriented JavaScript) <Javascript高级程序设计(第二版)>

《JavaScript面向对象编程指南》——1.6 OOP概述

1.6 OOP概述 JavaScript面向对象编程指南 如果您在面向对象程序设计方面是一个新手,或者您不能确定自己是否真的理解了上面这些概念,请不必太担心.以后我们还会通过一些代码来为您具体分析它们.尽管这些概念说起来好像很复杂.很高级,但一旦进入真正的实践,事情往往就要简单得多. 话虽如此,但还是先让我们再来复习一下这些概念吧(见表1-1).

JavaScript面向对象编程[转]

JavaScript面向对象编程 命名空间 命名空间是一个容器,它允许开发人员在一个独特的,特定于应用程序的名称下捆绑所有的功能. 在JavaScript中,命名空间只是另一个包含方法,属性,对象的对象. 需要认识到非常重要的一点,与其他面向对象编程语言中的普通对象和命名空间相比,它们在语言层面上没有区别. 创造的JavaScript命名空间背后的想法很简单:一个全局对象被创建,所有的变量,方法和功能成为该对象的属性.使用命名空间也最大程度地减少应用程序的名称冲突的可能性. 我们来创建一个全局变

JavaScript 面向对象编程(2) 定义类_js面向对象

本文承接上一篇JavaScript面向对象编程(1) 基础. 上篇说过,JavaScript没有类的概念,需要通过函数来实现类的定义.先通过一个例子说明: 复制代码 代码如下: function myClass() { var id = 1; var name = "johnson"; //properties this.ID = id; this.Name = name; //method this.showMessage = function() { alert("ID: