前言
1. 本文默认阅读者已有面向对象的开发思想,最好是使用过c++、java,本人Java不太熟悉,所以例子都是用C++来写的。
2. 本人不是专业网站开发人员,接触javascript一年多,自己也编写调试了一些代码,本文完全根据自己经验所写,只希望和朋友们分享。文章难免出错,希望大家指出,以便及时改正。
3. 代码测试环境:google chrome
正文:
好,都说得差不多了,那么怎么面向对象呢?刚学JS的时候,一个函数就是一个函数。比如
function a()
{
alert("呵呵哒");
}
a();
定义完直接调用即可,很简单。但是,继续学,又看到了这样的代码:
function man( name )
{
this.name = name;
this.sayName = function()
{
alert(this.Name);
}
}
于是乎晕了,哪来的this?又没有定义对象哪来的this?于是乎在网上搜索答案,最后知道了原来JS也可以面向对象。然后网上说用function声明的可以是函数,也可以是对象。这就让人有点乱了。本来脚本语言一般都是弱类型的语言,这个已经让人有点不习惯了,又来了一个既可以是函数又可以是对象的,头都大了。时隔半年后的今天,我在写程序C++的时候,顺便将JS的面向对象又想了一遍。貌似是这样的:
function a()
{
alert( this );
}
a();
结果是很简单啦,因为Window是最高层的对象,那么所谓的全局函数就是Window对象的成员函数,所谓的全局变量就是Window的成员变量啦!说到这,可以看出来Js中的对象是层层嵌套的,也就是C++中的内部类,外部类的关系啦!
答案是:可以作为纯对象,又可以作为纯函数,又可以同时作为对象和函数,具体返回值看你的调用方法。推荐一篇文章的参考链接:http://www.cnblogs.com/andyliu007/archive/2012/07/27/2795415.html。但是推荐归推荐,我对于这篇文章中的观点并不是完全赞同。下面是一段文章的截图:
根据作者的意思,我们可以认为:如果一个函数有返回值,那么以new的方式使用该函数时,得到的返回值与函数的返回值的类型有关,且当函数返回值是基本类型时,得到的返回值为一个object的对象;当函数返回值为一个引用类型的对象时,那么这个对象就是由这个对象的原型决定,至于是什么,并不清楚。那么请看以下代码:
function Test1()
{
this.id = 1;
return 1000;
}
var myTest = new Test1();
alert( myTest.id ); // 可访问!
alert( typeof myTest );
结果是
第一张图片的结果说明了返回的对象并不是函数返回值的prototype,而是一个Test1的对象。
如果返回一个对象呢?
function Test1()
{
this.id = 1;
return new String("我是返回值");
}
var myTest = new Test1();
alert( myTest.id ); // 无法访问
alert( myTest.legth );
alert( typeof myTest );
说明返回的是一个实质上是String类型而typeof是object的对象,你也可以显式地将new的返回值强制类型转换成String,也一切正常。
那么有没有可能是因为String是内置的类型才可以访问?返回普通的对象也是那样吗?请看下例
function Test1()
{
this.id = 1;
return new Test2();
}
function Test2()
{
this.id = 2;
}
var myTest = new Test1();
alert( myTest.id );
alert( typeof myTest );
结果:
说明:
如果函数返回值是原始类型时,没用,new返回的还是这个函数的对象。
如果函数的返回值是对象时,那么new返回的就是这个对象。
不过你如果没有强制类型转换的话,那么typedef出来的类型将是object。
还有需要注意,原始类型的string和引用类型的String( 首字母大写 )是不一样的,一个是值,一个是类,类可以有很多属性和方法,原始类型没有。
那么既然谈到了this.声明的变量,那么它和不用this.声明的变量有什么区别呢?先看一段C++示例代码:
A.h文件
class A()
{
public:
A();
~A();
public:
int name ;
}
A.cpp文件
A::A()
{
this.name = 0; // 成员变量,和对象同生命周期
int name1 = 1; // 函数的局部变量,函数执行完,内存就会被其他内容覆盖
}
A::~A()
{
}
JS代码
function A()
{
this.name = 0; // 成员变量,会随对象一直存在
name1 = 1; // 局部变量,会随对象一直存在(为什么这么说?测试出来的)
}
可以看出:1) 看成它对应C++构造函数内的局部变量(很多文章都称之为局部变量,如 http://www.jb51.net/article/24101.htm)。这么想的话,那么就有:JS局部变量和C++中的局部变量不同,它和成员变量的生命周期一样。
2) 这里我们它想成它对应C++中用private修饰的变量(私有变量):外部不能通过对象访问,它的生命周期也和对象一样,正好。
我比较偏向 2),因为看成是构造函数的局部变量的话,那么一个类的构造函数是访问不了的,因为JS里根据变量的函数作用域可知,里面的函数可以访问外面函数的变量的,这样才能实现闭包,二者矛盾。所以1)的类比没有2)确切。
总结一下
函数里带this的变量相当于C++中public修饰的变量。
函数里不带this的变量相当于C++中private修饰的变量。
那么问题来了,有时候我们要访问变量name1啊,怎么办呢?不能通过"对象.变量名"的形式,因为它不是对象的成员变量。怎么办呢?
方法一:C++中是通过成员函数来读写私有变量的:
A.h文件
class A()
{
public:
A();
~A();
public:
int name ;
private:
int name1;
public:
getName();
}
A.cpp文件
A::A()
{
this.name = 0; // 成员变量,和对象同生命周期
this.name1 = 1;
}
A::~A()
{
}
A::getName()
{
return this.name1;
}
function A()
{
this.name = 0;
name1 = 1;
this.getName1 = function(){ return name1; }
}
gN = new A();
alert( gN.getName1() );
方法二:
将函数返回出来
function A()
{
this.name = 0;
name1 = 1;
getName1 = function(){ return name1; }
return getName1;
}
gN = new A();
alert( gN() );
结果
这种方法涉及到两次返回,不容易理解,但是这种方法在JS里比较有名,叫做闭包。不过我个人推荐用成员函数来返回局部变量(也可以叫做私有成员变量)。因为将一个函数返回出来保存在了"全局变量中,这导致对象始终在内存中"( 引用自 http://www.jb51.net/article/24101.htm )。通过成员函数方法返回的也一样,也是始终存在于内存。function A()
{
this.name = 1;
}
a = new A();
这个过程发生了什么呢?(还是按C++的过程来模拟、类比,如有错误请指教哈)
function parent( name )
{
this.name = name;
this.sayName = function()
{
alert(this.name);
}
}
function child( name )
{
parent.call(this,name);
alert(this.sayName); // 相当于执行了一次this.name = "parent";this.sayName = function(){alert(this.name);}
}
c = new child("child");
c.sayName();
结果:
function Parent(age)
{
this.name = ['mike','jack','smith']; // 这里面只添加属性
this.age = age;
}
Parent.prototype.run = function () // 方法(成员函数)在这里(原型上)添加
{
return this.name + ' are both' + this.age;
};
function Child(age)
{
Parent.call(this,age); // call继承属性
}
Child.prototype = new Parent(); // 原型链继承方法
var test = new Child(21); // 写new Parent(21)也行
alert(test.run()); // mike,jack,smith are both21
即只用call来继承属性,用原型来继承方法。也就是假如现在有一个父类A,如果想让它被继承,那么最好将它的属性定义在构造函数里,将它的方法定义在它的prototype里,然后用call实现属性继承,用prototype直接继承A,从而继承方法。
Child.prototype = new Parent(); // 原型链继承方法
function A( name,age )
{
this.name = name;
this.age = age;
}
A.prototype.sayNameAge = function()
{
alert(this.name+" "+this.age);
}
function B( name,age )
{
A.call( this,name,age );
}
B.prototype = A.prototype;
B.constructor = B; // 定位回来,都则就指向A,不知道为啥,求大神指教
b = new B( "我是B",2 );
b.sayNameAge();