文档章节

面向对象的JavaScript——类

小微
 小微
发布于 2012/09/21 17:21
字数 2104
阅读 313
收藏 10

在java里,我们定义类的时候用的是class关键字,但是JavaScript中class是保留字,另有用途,所以我们要采用其他的方法来定义JavaScript中的类。

定义类

利用JavaScript中的function关键字,类名首字母一般采用大写,如:

function Person(id,name,age){
    this.id = id;
    this.name = name;
    this.age = age;
}

回想java的类中会有一个与该类同名的构造函数,即使开发者自己没有写,java虚拟机也会给出一个默认的构造函数。而JavaScript这里,上面这个类本身其实也是一个构造函数。这里记住一句话: “构造函数为对象的‘类’提供一个名字,并初始化属性。”我们可以用下面的方法来使用这个类:

var per = new Person("001","Xiao Wei","12");

上面那个构造函数里的this指针(严格意义上this不是指针,只是我们把它想象为指针会容易理解一些)指向的变量即是Person类的数据成员(或者叫属性),访问权限是public。java的类中,除了有属性之外,还有方法(也就是c里的函数)。下面我们为JavaScript类添加函数。

function Person(id,name,age){
    this.id = id;
    this.name = name;
    this.age = age;
    this.getInformation = function(){
        return "编号:"+this.id+";姓名:"+this.name+";年龄:"+this.age;
    };
}

上面是一种最直接的办法来为类添加属性,然而这不是好的办法,原因在于对客观事物的描述不正确,因为属性是每个实例(也就是对象)都有一份,而方法应该不需要,因为方法是对数据进行处理,是公用的,所以,更好的解决办法采用prototype原型方式。

function Person(id,name,age){
    this.id = id;
    this.name = name;
    this.age = age;
}
Person.prototype.getInformation = function(){
    return "编号:"+this.id+";姓名:"+this.name+";年龄:"+this.age;
};

这里之所以能够采用原型方式是因为 每个JavaScript对象都包含着对它的原型对象的内部引用。其实不仅方法,属性也可以采用prototype原型方式。这里再记住一句话: “方法和其他不变属性放在原型对象中。”

类已经通过function关键字实现了,这是一种实现方式,这种方式实现起来简单、通俗易懂,其实还有一种实现方式,实现起来非常优雅,我们看下面代码:

var Class={
    create:function(){
        return function(){
            this.inital.apply(this,arguments);
        };
    }
};
/*定义Person类*/
var Person=Class.create();
Person.prototype={
    inital:function(id,name,age){
        this.id = id;
        this.name = name;
        this.age = age;
    },
    getInformation:function(){
        return "编号:"+this.id+";姓名:"+this.name+";年龄:"+this.age;
    }
};

关键代码在于:this.inital.apply(this,arguments);这样一行,Class本身是一个对象,有一个方法叫做create,此方法返回一个函数指针,其中函数体内执行this.inital.apply(this,arguments);this指向是的是当前对象,在这里就是Person,apply方法是更改initial方法的作用域,arguments是参数列表。关于apply更详细的解释可以参见我之前的学习笔记《JavaScript中call()与apply()有什么区别?》

this

关于this的详细用法我已在读书笔记《JavaScript中的this如何使用》中有介绍。这里再啰嗦几小句。

在JavaScript中,并没有严格的面向对象概念,自然也没有类的构造函数这样的概念。var o=new Obj();这样的语法,看起来似乎和Java/C++相当类似,但是它背后的执行过程是不同的。首先,解释器会new一个空的Object对象。然后将这个空的Object,作为隐藏的参数传递给function Obj()。在Obj函数中访问到的this,其实就是这个传入的空的Object 对象。这就是所谓:“this关键字关联于执行时的作用域”的含义。

如果你想把一个函数作为“构造函数”,那么就不要在函数的最后加上return语句。因为如果没有return语句,new算符返回的就是那个被操作过以后的this。一旦你通过return返回了别的东西,这个this就被废弃掉了。

匿名类

var class1 = {p1:value1,p2:value2};

这个也可以写成

var class1 = {};
class1.p1 = value1;
class1.p2 = value2;

首先所有的匿名类都是继承于Object核心对象的,var class1={} 意味着实例化了一个Object对象,它拥有Object对象的原生属性和原生方法。但是不能为匿名类添加原生方法,例如这样写是错误的:

class1.prototype.func1 = function(){};

你也不能尝试用new() 方法来构造一个新的与class1有相同的属性的新对象,因为它已经实例化了。以下写法也是错的:

var classB = new classA();

这是无法构造的,

准确的说,匿名类实际上是继承于Object的某一个实例,相当于C#中的静态类。你可以为它添加方法和属性。例如:

class1.func1 = function(){};

调用的时候就这样:

class1.func1();  //酷似C#中的静态类

但是你可以为Object添加原生函数,这样你的匿名类(实际上是所有的类)都有这个方法。例如:

var class1 = {};
class1.p1 = value1;
class1.p2 = value2;
Object.prototype.func1 = function(){ alert("1") };
class1.func1();

是没有问题的,但是这样一来,所有的实例化对象都有了func1()方法。实际应用中应该避免给Object类添加原生方法。

匿名函数

先说说关于Javascript的函数:可以这样说,JavaScript中一切皆是对象,function自然不例外,function可以作为函数,类,也可以当成一个被函数对象返回。

看下面的例子:

function a(){
    alert("Hello Febird!");
    this.aa = "aa";
    this.show = function(){
        alert(this.aa);
    };
    this.sayHello = function(){
        return function(){alert("hello");};
    };
}

var aaa = new a();
aaa.show();
aaa.sayHello();

其中最外面的一个function是定义了一个类 a ,它有属性aa,方法show(),sayHello();这两个都是匿名函数,而sayHello中的function便是函数作为一个返回值的例子。

实际上可以这样想,匿名函数就是一块没有命名的代码块,当把它赋值给别的变量的时候,那么那个变量就是一个函数,准确的说那是一个函数指针。

在JavaSript中,匿名函数是很有特点的东西了,也是非常有用,也是有些难以理解的。

比如在写Ajax引用的时候,如果不依靠别的JSF,自己写一个通用的Ajax话,一般这样写:

var xhr = new XMLHttpRequest(); //已经封装,可以适应不同的浏览器;
function DoAjax(){
    xhr.onreadystatechange=processFunction;
    xhr.open("GET",url,true);
    xhr.send(null);
}
function processFunction(){
    //do something with XMLHttpRequest;
    if(xhr.readState!=4||xhr.status!=200) return false;
    alert(xhr.responseText);
}

在一般的Ajax引用中,也许只要一个XMLHttpRequest对象,而且onreadystatechange的处理函数必须没有参数,有参数就出错,所以,一般经常会写一个全局变量XMLHttpRequest,再在processFunction中用到这个全局变量,但是如果我要建立几个XMLHttpRequest的并发连接怎么办呢?这个就不能用全局变量了,但是处理函数又不能有参数,怎么搞,可以这样: 

function DoAjax(){
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = processFunction(xhr);
    xhr.open("GET",url,true);
    xhr.send(null);
}
function processFunction(_xhr){
    return function(){
        //do something with XMLHttpRequest;
        if(_xhr.readState!=4||_xhr.status!=200) return false;
        alert(_xhr.responseText);
    };
}

怎么理解?虽然processFunction函数有参数,但是它返回的函数没有参数!而这两个函数之间是怎么进行的值传递呢?

这里不妨引用一句话:

“为了函数能够正确的执行,需要被函数使用的,词法作用域中的,非全局数据,存在于函数的闭包之中。”

可以这样理解:

当我们把processFunction()返回的函数,在processFunction之外使用的时候,依然要记得自己被定义时的上级作用域中的各种变量的值。这些需要被记住的值,就是“闭包”。

原生对象

原生,即prototype,它提供了扩展、改造原有对象的方法。例如我们可以为已知对象,包括JavaScript的核心对象Array,Number,Math,Object,Boolean等和自定义类添加方法或者属性。

例如:

Number.prototype.toHexString = function () {
    return this.toString(16);
};
var num = 10;
alert(num.toHexString());

输出A;

你可以为Object对象添加方法,这样,以后任意一个对象都有这个方法,因为其它对象都是从Object继承而来的。

你也可以再造现有函数

Function.prototype.toString = function () {
    return “Function Locked”;
};

 

参考资料

1. javascript实现类、继承、多态(原创)

2. JavaScript面向对象---匿名函数和匿名类,以及原生类

© 著作权归作者所有

小微
粉丝 117
博文 78
码字总数 81696
作品 0
海淀
程序员
私信 提问
加载中

评论(3)

乔康007
乔康007

引用来自“小微”的评论

引用来自“乔康007”的评论

嗯,写的挺好,对new和this的理解很深刻~~

谢谢你的夸奖!不过文章内容不是我的原创,是我看了上面参考资料里提到的两篇文章后总结的学习笔记^_^

那不重要,重要的是你理解了~~
小微
小微 博主

引用来自“乔康007”的评论

嗯,写的挺好,对new和this的理解很深刻~~

谢谢你的夸奖!不过文章内容不是我的原创,是我看了上面参考资料里提到的两篇文章后总结的学习笔记^_^
乔康007
乔康007
嗯,写的挺好,对new和this的理解很深刻~~
是时候谈谈JavaScript面向对象了!(我们什么时候更需要它)

前端技术尤其是JavaScript,经常被后端觉得不是正经编程语言,大多数是因为JavaScript是基于函数的语言,因此潜在上使得它在使用和发展上有所局限。 我记得在90年代末和21世纪初,JavaScrip...

Little heaven
05/14
0
0
【译】TypeScript简介

JavaScript曾是作为客户端语言引入的。Nodejs的到来让JavaScript成为服务器端语言的新星。然而,随着JS代码的增长,它变得更加混乱,较难去维护和重用代码。除此之外,它没有采用面向对象,强...

ling20
09/03
0
0
每个JavaScript工程师都应懂的33个概念

摘要: 基础很重要啊! 原文:33 concepts every JavaScript developer should know 译文:每个 JavaScript 工程师都应懂的33个概念 作者:stephentian Fundebug经授权转载,版权归原作者所有...

Fundebug
2018/10/30
0
0
JavaScript开发者应懂的33个概念

简介 这个项目是为了帮助开发者掌握 JavaScript 概念而创立的。它不是必备,但在未来学习(JavaScript)中,可以作为一篇指南。 本篇文章是参照 @leonardomso 创立,英文版项目地址在这里。 ...

大灰狼的小绵羊哥哥
2018/10/22
0
0
作为一名JS开发人员,是什么使我夜不能寐

作者:Justen Robertson 翻译:疯狂的技术宅 原文:www.toptal.com/javascript/… 未经允许严禁转载 JavaScript 是一种奇怪的语言。虽然受到 Smalltalk 的启发,但它用了类似 C 的语法。它结...

前端先锋
07/08
0
0

没有更多内容

加载失败,请刷新页面

加载更多

【0918】正则介绍_grep

【0918】正则介绍_grep 9.1 正则介绍_grep上 9.2 grep中 9.3 grep下 一、正则介绍 正则是一串有规律的字符串,它使用单个字符串来描述或匹配一系列符合某个语法规则的字符串。 二、grep工具 ...

飞翔的竹蜻蜓
5分钟前
0
0
为什么要在网站中应用CDN加速?

1. 网页加载速度更快 在网站中使用CDN技术最直接的一个好处就是它可以加快网页的加载速度。首先,CDN加速的内容分发是基于服务器缓存的,由于CDN中缓存了不少数据,它能够给用户提供更快的页...

云漫网络Ruan
43分钟前
7
0
亚玛芬体育(Amer Sports)和信必优正式启动合作开发Movesense创新

亚玛芬体育和信必优正式启动合作开发Movesense创新,作为亚玛芬体育的完美技术搭档,信必优利用Movesense传感器技术为第三方开发移动应用和服务。 Movesense基于传感器技术和开放的API,测量...

symbiochina88
54分钟前
4
0
创龙TI AM437x ARM Cortex-A9 + Xilinx Spartan-6 FPGA核心板规格书

SOM-TL437xF是一款广州创龙基于TI AM437x ARM Cortex-A9 + Xilinx Spartan-6 FPGA芯片设计的核心板,采用沉金无铅工艺的10层板设计,适用于高速数据采集和处理系统、汽车导航、工业自动化等领...

Tronlong创龙
54分钟前
4
0
好程序员Java学习路线分享MyBatis之线程优化

  好程序员Java学习路线分享MyBatis之线程优化,我们的项目存在大量用户同时访问的情况,那么就会出现大量线程并发访问数据库,这样会带来线程同步问题,本章我们将讨论MyBatis的线程同步问...

好程序员官方
今天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部