javascript的函数、创建对象、封装、属性和方法、继承

  一,function

  从一开始接触到js就感觉好灵活,每个人的写法都不一样,比如一个function就有N种写法

  如:function showMsg(){},var showMsg=function(){},showMsg=function(){}

  似乎没有什么区别,都是一样的嘛,真的是一样的吗,大家看看下面的例子

  

复制代码 代码如下:

  ///-----------------------------------------------------------------------------------------

  -------

  //函数定义:命名函数(声明式),匿名函数(引用式)

  //声明式,定义代码先于函数执行代码被解析

  function t1(){

  dwn("t1");

  }

  t1();

  function t1(){

  dwn("new t1");

  }

  t1();

  //引用式,在函数运行中进行动态解析

  var t1=function(){

  dwn("new new t1");

  }

  t1();

  var t1=function(){

  dwn("new new new t1");

  }

  t1();

  //以上输出:new t1,new t1,new new t1,new new new t1

  可能想着应该是输出t1,new t1,new newt1,new new new t1,结果却并不是这样,应该理解这句话:声明

  式,定义代码先于函数执行代码被解析

  如果深入一步,应该说是scope链问题,实际上前面两个方法等价于window.t1,可以理解为t1是window的

  一个公有属性,被赋了两次值,以最后一次赋值为最终值

  而后面两个方法,可以理解为是t1是个变量,第四个方法的var去掉之后的结果仍然不会改变

  然而,当第四个方法改成function t1(){}这样的声明式时,结果变成了new new new t1,new new new

  t1,new new t1,new new t1

  前面两个按照我的理解可以很好的理解为什么是这个答案,第三个也可以理解,但是最后一个输出让我比

  较纠结,希望有高手出现解答一下

  另外匿名函数还有(function(){...})()这样的写法,最后一个括号用于参数输入

  还有var t1=new function(){..}这样的声明,实际上t1已经是一个对象了

  例:

  

复制代码 代码如下:

  var t2 = new function()

  {

  var temp = 100; //私有成员

  this.temp = 200; //公有成员,这两个概念会在第三点以后展开说明

  return temp + this.temp;

  }

  alert(typeof(t2)); //object

  alert(t2.constructor()); //300

  除此之外,还有使用系统内置函数对象来构建一个函数,例:

  var t3 = new Function('var temp = 100; this.temp = 200; return temp + this.temp;'); //这个位置加不加new结果都一样,WHY

  alert(typeof(t3)); //function

  alert(t3()); //300

  二,创建对象

  首先我们理解一下面向对象编程(Object-Oriented Programming,OOP),使用OOP技术,常常要使用许多

  代码模块,每个模块都提供特定的功能,每个模块都是孤立的,甚至与其它模块完全独立

  。这种模块化编程方法提供了非常大的多样性,大大增加了代码的重用机会。可以举例进一步说明这个问

  题,假定计算机上的一个高性能应用程序是一辆一流赛车。如果使用传统的编程技巧,这辆赛车就是

  一个单元。如果要改进该车,就必须替换整个单元,把它送回厂商,让汽车专家升级它,或者购买一个新

  车。如果使用OOP技术,就只需从厂商处购买新的引擎,自己按照说明替换它,而不必用钢锯切割车体。

  不过大部分的论点是,javascript并不是直接的面向对象的语言,但是通过模拟可以做到很多面向对象语

  言才能做到的事,如继承,多态,封装,javascript都能干(没有做不到,只是想不到)

  

复制代码 代码如下:

  ///-----------------------------------------------------------------------------------------

  -------

  //以下三种构造对象的方法

  //new Object,实例化一个Object

  var a=new Object();

  a.x=1,a.y=2;

  //对象直接量

  var b={x:1,y:2};

  //定义类型

  function Point(x,y){ //类似于C#中的类

  this.x=x;

  this.y=y;

  }

  var p=new Point(1,2); //实例化类

  第一种方法通过构造基本对象直接添加属性的方法来实现,第二种和第一种差不多,可以看成是第一种方

  法的快捷表示法

  第三种方法中,可以以”类“为基础,创造多个类型相同的对象

  三,对象属性的封装(公有和私有)

  以例子来说明

  function List(){

  var m_elements=[]; //私有成员,在对象外无法访问,如果此处无var声明,则m_elements将变成全局变

  量,这样外部是可以直接访问到的,如alert(m_elements[0])

  

复制代码 代码如下:

  m_elements=Array.apply(m_elements,arguments);

  //此处模拟getter,使用时alist.length;

  //等价于getName()方式:this.length=function(){return m_elements.length;},使用时

  alist.length();

  //公有属性,可以通过"."运算符或下标来访问

  this.length={

  valueOf:function(){

  return m_elements.length;

  },

  toString:function(){

  return m_elements.length;

  }

  }

  //公有方法,此方法使用得alert(alist)相当于alert(alist.toString())

  this.toString=function(){

  return m_elements.toString();

  }

  //公有方法

  this.add=function(){

  m_elements.push.apply(m_elements,arguments);

  }

  //私有方法如下形式,这里涉及到了闭包的概念,接下来继续说明

  //var add=function()或function add()

  //{

  //m_elements.push.apply(m_elements,arguments);

  //}

  }

  var alist=new List(1,2,3);

  dwn(alist); //=alert(alist.toString()),输出1,2,3

  dwn(alist.length); //输出3

  alist.add(4,5,6);

  dwn(alist); //输出1,2,3,4,5,6

  dwn(alist.length); //输出6

  四,属性和方法的类型

  javascript里,对象的属性和方法支持4种不同的类型:private property(私有属性),dynamic public

  property(动态公有属性),static public property/prototype property(静态公有属性或原型属性),

  static property(静态属性或类属性)。私有属性对外界完全不具备访问性,可以通过内部的getter和

  setter(都是模拟);动态公有属性外界可以访问,每个对象实例持有一个副本,不会相互影响;原型

  属性每个对象实例共享唯一副本;类属性不作为实例的属性,只作为类的属性。

  以下是例子:

  

复制代码 代码如下:

  ///-----------------------------------------------------------------------------------------

  -------

  //动态公有类型,静态公有类型(原型属性)

  function myClass(){

  var p=100; //private property

  this.x=10; //dynamic public property

  }

  myClass.prototype.y=20; //static public property or prototype property,动态为myClass的原型添

  加了属性,将作用于所有实例化了的对象,注意这里用到了prototype,这是一个非常有用的东东

  //要想成为高级javascript阶段,prototype和闭包必须得理解和适当应用

  myClass.z=30; //static property

  var a=new myClass();

  dwn(a.p) //undefined

  dwn(a.x) //10

  dwn(a.y) //20

  a.x=20;

  a.y=40;

  dwn(a.x); //20

  dwn(a.y); //40

  delete(a.x); //删除对象a的属性x

  delete(a.y); //删除对象a的属性y

  dwn(a.x); //undefined

  dwn(a.y); //20 静态公有属性y被删除后还原为原型属性y

  dwn(a.z); //undefined 类属性无法通过对象访问

  dwn(myClass.z);

  五,原型(prototype)

  这里只讲部分,prototype和闭包都不是几句话都能讲清楚的,如果这里可以给你一些启蒙,则万幸矣

  习语”照猫画虎“,这里的猫就是原型,虎是类型,可以表示成:虎.prototype=某只猫 or

  虎.prototype=new 猫()

  因为原型属性每个对象实例共享唯一副本,所以当实例中的一个调整了一个原型属性的值时,所有实例调

  用这个属性时都将发生变化,这点需要注意

  以下是原型关系的类型链:

  

复制代码 代码如下:

  function ClassA(){

  }

  ClassA.prototype=new Object();

  function ClassB(){

  }

  ClassB.prototype=new ClassA();

  function ClassC(){

  }

  ClassC.prototype=new ClassB();

  var obj=new ClassC();

  dwn(obj instanceof ClassC); //true

  dwn(obj instanceof ClassB); //true

  dwn(obj instanceof ClassA); //true

  dwn(obj instanceof Object); //true

  带默认值的Point对象:

  function Point2(x,y){

  if (x) this.x=x;

  if (y) this.y=y;

  }

  //设定Point2对象的x,y默认值为0

  Point2.prototype.x=0;

  Point2.prototype.y=0;

  //p1是一个默认(0,0)的对象

  var p1=new Point2(); //可以写成var p1=new Point2也不会出错,WHY

  //p2赋值

  var p2=new Point2(1,2);

  dwn(p1.x+","+p1.y); //0,0

  dwn(p2.x+","+p2.y); //1,2

  delete对象的属性后,原型属性将回到初始化的状态:

  function ClassD(){

  this.a=100;

  this.b=200;

  this.c=300

  }

  ClassD.prototype=new ClassD(); //将ClassD原有的属性设为原型,包括其值

  ClassD.prototype.reset=function(){ //将非原型属性删除

  for (var each in this) {

  delete this[each];

  }

  }

  var d=new ClassD();

  dwn(d.a); //100

  d.a*=2;

  d.b*=2;

  d.c*=2;

  dwn(d.a); //200

  dwn(d.b); //400

  dwn(d.c); //600

  d.reset(); //删掉非原型属性,所有回来原型

  dwn(d.a); //100

  dwn(d.b); //200

  dwn(d.c); //300

  六,继承

  如果两个类都是同一个实例的类型,那么它们之间存在着某种关系,我们把同一个实例的类型之间的泛化

  关系称为继承。C#和JAVA中都有这个,具体的理解就不说了。

  在javascript中,并不直接从方法上支持继承,但是就像前面说的,可以模拟啊

  方法可以归纳为四种:构造继承法,原型继承法,实例继承法和拷贝继承法。融会贯通之后,还有混合继

  续法,这是什么法,就是前面四种挑几种混着来~

  以下例子来源于王者归来,其中涉及到了apply,call和一些Array的用法,有兴趣的可以自己在园子里搜索

  一下

  1,构造继续法例子:

  

复制代码 代码如下:

  //定义一个Collection类型

  function Collection(size)

  {

  this.size = function(){return size}; //公有方法,可以被继承

  }

  Collection.prototype.isEmpty = function(){ //静态方法,不能被继承

  return this.size() == 0;

  }

  //定义一个ArrayList类型,它"继承"Collection类型

  function ArrayList()

  {

  var m_elements = []; //私有成员,不能被继承

  m_elements = Array.apply(m_elements, arguments);

  //ArrayList类型继承Collection

  this.base = Collection;

  this.base.call(this, m_elements.length);

  this.add = function()

  {

  return m_elements.push.apply(m_elements, arguments);

  }

  this.toArray = function()

  {

  return m_elements;

  }

  }

  ArrayList.prototype.toString = function()

  {

  return this.toArray().toString();

  }

  //定义一个SortedList类型,它继承ArrayList类型

  function SortedList()

  {

  //SortedList类型继承ArrayList

  this.base = ArrayList;

  this.base.apply(this, arguments);

  this.sort = function()

  {

  var arr = this.toArray();

  arr.sort.apply(arr, arguments);

  }

  }

  //构造一个ArrayList

  var a = new ArrayList(1,2,3);

  dwn(a);

  dwn(a.size()); //a从Collection继承了size()方法

  dwn(a.isEmpty); //但是a没有继承到isEmpty()方法

  //构造一个SortedList

  var b = new SortedList(3,1,2);

  b.add(4,0); //b 从ArrayList继承了add()方法

  dwn(b.toArray()); //b 从ArrayList继承了toArray()方法

  b.sort(); //b 自己实现的sort()方法

  dwn(b.toArray());

  dwn(b);

  dwn(b.size()); //b从Collection继承了size()方法

  2,原型继承法例子:

  

复制代码 代码如下:

  //定义一个Point类型

  function Point(dimension)

  {

  this.dimension = dimension;

  }

  //定义一个Point2D类型,"继承"Point类型

  function Point2D(x, y)

  {

  this.x = x;

  this.y = y;

  }

  Point2D.prototype.distance = function()

  {

  return Math.sqrt(this.x * this.x + this.y * this.y);

  }

  Point2D.prototype = new Point(2); //Point2D继承了Point

  //定义一个Point3D类型,也继承Point类型

  function Point3D(x, y, z)

  {

  this.x = x;

  this.y = y;

  this.z = z;

  }

  Point3D.prototype = new Point(3); //Point3D也继承了Point

  //构造一个Point2D对象

  var p1 = new Point2D(0,0);

  //构造一个Point3D对象

  var p2 = new Point3D(0,1,2);

  dwn(p1.dimension);

  dwn(p2.dimension);

  dwn(p1 instanceof Point2D); //p1 是一个 Point2D

  dwn(p1 instanceof Point); //p1 也是一个 Point

  dwn(p2 instanceof Point); //p2 是一个Point

  以上两种方法是最常用的

  3,实例继承法例子:

  在说此法例子之前,说说构造继承法的局限,如下:

  

复制代码 代码如下:

  function MyDate()

  {

  this.base = Date;

  this.base.apply(this, arguments);

  }

  var date = new MyDate();

  alert(date.toGMTString); //undefined,date并没有继承到Date类型,所以没有toGMTString方法

  核心对象的某些方法不能被构造继承,原因是核心对象并不像我们自定义的一般对象那样在构造函数里进

  行赋值或初始化操作

  换成原型继承法呢?,如下:

  

复制代码 代码如下:

  function MyDate(){}

  MyDate.prototype=new Date();

  var date=new MyDate();

  alert(date.toGMTString); //'[object]'不是日期对象,仍然没有继承到Date类型!

  现在,换成实例继承法:

  

复制代码 代码如下:

  function MyDate()

  {

  var instance = new Date(); //instance是一个新创建的日期对象

  instance.printDate = function(){

  document.write("<p> "+instance.toLocaleString()+"</p> ");

  } //对instance扩展printDate()方法

  return instance; //将instance作为构造函数的返回值返回

  }

  var myDate = new MyDate();

  dwn(myDate.toGMTString()); //这回成功输出了正确的时间字符串,看来myDate已经是一个Date的实例

  了,继承成功

  myDate.printDate(); //如果没有return instance,将不能以下标访问,因为是私有对象的方法

  4,拷贝继承法例子:

  

复制代码 代码如下:

  Function.prototype.extends = function(obj)

  {

  for(var each in obj)

  {

  this.prototype[each] = obj[each];

  //对对象的属性进行一对一的复制,但是它又慢又容易引起问题

  //所以这种“继承”方式一般不推荐使用

  }

  }

  var Point2D = function(){

  //……

  }

  Point2D.extends(new Point())

  {

  //……

  }

  这种继承法似乎是用得很少的。

  5,混合继承例子:

  

复制代码 代码如下:

  function Point2D(x, y)

  {

  this.x = x;

  this.y = y;

  }

  function ColorPoint2D(x, y, c)

  {

  Point2D.call(this, x, y); //这里是构造继承,调用了父类的构造函数

  //从前面的例子看过来,这里等价于

  //this.base=Point2D;

  //this.base.call(this,x,y);

  this.color = c;

  }

  ColorPoint2D.prototype = new Point2D(); //这里用了原型继承,让ColorPoint2D以Point2D对象为原型