JavaScript经验整理

      接触Web开发也已经有一段时间了,对javascript的认识也比以前有了更加深入的认识了,所以觉得应该整理一下。

一、JavaScript不支持函数(方法)的重载,用一个例子证明如下:
function add(number)
{
    alert('hello');
}

function add(number1, number2)
{
    alert('world');
}
此时调用add(1),会弹出'world',而不是'hello',由此现象说明写在文档后面的方法会覆盖掉前面的方法,所以有一个我认为暂时是正确的原则,就是如果方法名相同,则以后面对方法为主。

二、JavaScript中函数(方法)就是一个对象,这里就不解释了,看一个例子比较直观
function add(number)
{
    alert('hello');
}
是等价于下面这种写法的
var add = new Function("number","alert('hello')");
以此类推
function add(number1, number2)
{
    alert('world');
}
等价于
var add = new Function("number1","number2","alert('world')");
这里一和二都是一样的,只是写法不同,底层是一样的,所以可以把JavaScript的不支持重载的现象按照面向对象的概念来理解:首先add对象指向第一个Function对象,而后引用的对象改变了,指向了第二个新的Function对象了。示意图如下

 所以可以得出一个这样的结论:JavaScript中的每个函数function都是new出来的Function对象。
  
三、如果在调用某个JavaScript函数的时候,没有给函数中的参数传值,则参数为undefined,例子如下:
function add(number)
{
    alert(number);
    alert(number + 30);
}
当在触发处调用add()的时候,显然没有传入参数值,此时先弹出undefined(未定义),
然后弹出NaN(不是一个数Not A Number)

四、arguments是每个函数的内置对象,应用这个内置对象可以模拟面向对象的重载功能,例如
function sum()
{
    if(1 == arguments.length)
    {
        alert(arguments[0]);
    }
    else if(2 == arguments.length)
    {
        alert(arguments[0] + arguments[1]);
    }
    else if(3 == arguments.length)
    {
        alert(arguments[0] + arguments[1] + arguments[2]);
    }
}
调用如下:
sum(1);    =>    1
sum(1, 2);    =>    3
sum(1, 2, 3);    =>    6

五、由上面的例子又可以得到,JavaScript的函数就是Function的对象,函数都有一个length属性,表示函数接收参数的个数。证明如下:
function add() {.....}
function minus(num) {....}
function multiply(num1, num2) {.....}
调用如下:
alert(add.length)    =>    0
alert(minus.length)    =>    1
alert(multiply.length)    =>    2

六、JavaScript中有五种原始值,可以理解为原生数据类型(这里仅仅是值JavaScript的,不同于3GL语言),它们分别是:Undifined,Null,Boolean,Number,String
(1)Undefined数据类型的值只有一个:undefined,这里要注意一点就是,对于JavaScript中的变量,如果只声明,而没有赋值,则该变量的值为undefined。
例如:var s;    alert(s);   此时弹出的是undefined

(2)Null数据类型的值只有一个:null

(3)Boolean数据类型的值有两个:true和false

(4)任何整数或者浮点数都是Number数据类型中的值,另外typeof(..)函数会返回参数的类型名称,例如:
var s;   alert(typeof(s));     弹出undefined
var a = 1;    alert(typeof(a));    弹出number
var b = 'hello';    alert(typeof(b));    弹出string     双引号表示原始类型在栈里
而var c = new String("hello");    alert(typeof(c));    弹出object,是在堆中的对象了

(5)一个函数如果不返回值,则返回'undefined',typeof(..)返回的值是字符串类型的,证明如下:
function aa() {..//里面没有return出值..}         alert(aa()); 会弹出undefined

function bb() {....;   return;  }      alert(bb);会弹出bb函数体{}中的所有源代码内容

(6)使用没有声明,并且没有赋值的变量会报错,例如,
var s;    alert(s2);  因为s2没有声明也没有赋值,所以会报错
alert(typeof(s));  弹出undefined
alert(typeof(s2));  尽管s2没有声明也没有赋值,但会弹出undefined

由此可以归纳出,typeof(..)的返回值有五个:undefined,boolean,number,string,object;

(7)null与undefined的关系:undefined实际上是从null派生而来的,证明如下,
alert(undefined == null);    会弹出true

(8)在函数体内加不加var的区别:如果在函数体的内部,变量不加var,则默认该变量为全局变量,证明如下:
function test()
{
    var s = 'hello';
}
test();
alert(typeof(s));此时会返回undefined
然而
function test()
{
    s = 'hello';
}
test();
alert(typeof(s));会弹出string
alert(s);会弹出hello
由此可知,对于函数定义中的变量来说,加var表示局部变量,不加var表示全局变量。

(9)强制类型转换有三种:Boolean(value),Number(value),String(value) [这里可以看作函数]
例如,
var s = Boolean(null);    alert(s); 会弹出false
var s = Boolean("");     alert(s);会弹出false
var s = Boolean("a");   alert(s);会弹出true
new Boolean(),new String("..."),new Number("...")都是object类型的

(10)在JavaScript中,Object是所有类的父类,类似于高级语言java
var s = new Object();
alert(s);    弹出[object Object]
alert(typeof(s)); 弹出object
alert(s.propertyIsEnumerable("prototype"));弹出false,这里是判断s中的属性是否可以枚举出来

遍历window对象中的所有属性
for(var temp in window)
{
    alert(temp);
}

delete操作和JavaScript中的晚绑定:
var o = new Object();
o.name = "scott";
alert(o.name);   //会弹出scott
delete(o.name); //删除晚绑定的name属性
alert(o.name); //会弹出undefined

七、JavaScript的面向对象的特性:
(1)JavaScript是基于ECMAScript的,构造对象时可以直接用名字,例如
var object = new Object;       <=>        var object = new Object();
var s = new String;            <=>         var s = new String();
var date = new Date;         <=>        var date = new Date();
尽管如此,还是建议加上括号,增加可读性!!!!

(2)JavaScript中的数组,长度可以动态的增加,类似于java中的ArrayList,例如
var array = new Array();        <=>      var array = [ ];
array.push(1);
array.push(2);
array.push(3);
alert(array.length); //弹出3
这里也可以用var array = [1,2,3];    alert(array.length); //弹出3

(3)数组对象有一个方法sort(),可以对数组进行升序排序,例如
var array = [3,2,1];
array.sort();
alert(array); //弹出[1,2,3]
咋一看,很爽,但是
var array = [1,3,21];
array.sort();
alert(array); //弹出[1,21,3]
原因就在于:sort()方法对数组进行排序的时候,首先将数组中的每一个元素都转化为字符串,即调用每个元素的toString()方法,然后用字符串的ASCII码进行比较! 所以'21' < '3'很正常!
另外,sort()可以接受一个排序的函数作为参数,根据参数排序,类似于java中的compare()方法
例如,
function compare(num1, num2)
{
    var temp1 = parseInt(num1);
    var temp2 = parseInt(num2);
    return temp1 - temp2;
}
var array = [3,1,21];
array.sort(compare);
alert(array);  //弹出[1,3,21]
这里还可以用匿名函数的方法,类似于java中的匿名类
array.sort(function(num1, num2) {
    var temp1 = parseInt(num1);
    var temp2 = parseInt(num2);
    return temp1 - temp2;
});

(4)对象的属性方式:[1]早绑定 [2]玩绑定
所谓早绑定,就是在对象生成之前绑定好的属性,例如,
var object = new Object();
object.name = "scott";
alert(object.name);

所谓晚绑定,就是对生成好的对象增加或减少它的属性,例如,
object.hello = function() { alert('welcome'); }
object.hello();  //此时会调用object的hello方法,弹出welcome
delete object.hello();  //删除方法
object.sayName = function() { alert(this.name); }  //这里this指的是object对象,类似于java一样
object.sayName();  //此时会弹出scott
object.sayName = function(name) {   //绑定一个传参数的方法
    this.name = name;
    alert(this.name);
}
object.sayHello('tiger');   //此时弹出tiger

=================================================================

一、用工厂方式定义对象(类),这里是参照java的设计模式的工厂模式写的,例子如下

function createObject()
{
    var object = new Object();  //这里请加上var,因为是方法体内部的局部变量
    object.username = "scott";
    object.password = "tiger";

    object.get = function()
    {
        alert("username: " + this.username + "\npassword: " + password);
    }
    return object;
}
调用如下,
var o = createObject();
o.get();  //此时会弹出username: scott
          //         password: tiger
该方法的好处是凡是学要对象了,不需要自己new一个出来,直接可以通过createObject生成对象,具体的细节都被封装起来了~ 

上面的工厂方式固然有优点,但是生成的对象属性都被默认写死成scott和tiger了,所以可以改良成如下带参数的工厂方法,例子如下,
function createObject(username, password)
{
    var object = new Object();
    object.username = username;
    object.password = password;

    object.get = function()
    {
        alert("username: " + this.username + "\npassword: " + password);
    }
    return object;
}
调用如下,
var o = createObject("scott","tiger");
o.get();
该方法的好处比起一楼的方式,就是更灵活的控制了要生成的对象。

然而对于以上两种工厂方式,方法get()定义在createObject()函数的内部,从性能上考虑,没创建一个对象,就要生成一个get的function对象,但是在高级语言java中所有的对象都共同的用同一个方法,需要注意到是在JavaScript中get方法不是如此,工厂每次生成对象的时候,都会为get再生成一次相同的对象,比较消耗内存,因此,可以将get方法的定义放在createObject之外。例如,

function get()
{
    alert("username: " + this.username + "\npassword: " + this.password);
}

function createObject()
{
    var object = new Object();
    object.username = "scott";
    object.password = "tiger";
    object.get = get;
    return object;
}
调用方法一样的,有点就是优化了性能,功能还是一样~ 


二、构造函数的方式
function Person()
{
    this.username = "scott";
    this.password = "tiger";
    this.get = function()
    {
        alert("username: " + this.username + "\npassword: " + this.password);
    }
}
用这种方法定义比较直观,与Java中生成对象很类似了,调用如下,
var person = new Person();
person.get();

注解:当调用new Person()的时候,执行流程跳到了函数中去了,在执行第一行代码之前,会生成一个对象,而this则会指向该对象,虽然没有return该对象,但是此构造函数隐式地将对象return出去了;

另外,传递参数也是类似的,内部函数的定义也可以改良从而降低对内存的消耗,将它拿到构造函数外面单独定义,所以可以改写成如下,
function get()
{
    alert("username: " + this.username + "\npassword: " + this.password);
}

function Person(username, password)
{
    this.username = username;
    this.password = password;
    this.get = get;
}

以上这种方式还是不够直观,有另一种解决方案如下,

原型方式创建类:prototype
function Person(){ }
Person.prototype.username = "scott";
Person.prototype.password = "tiger";
Person.prototype.get = function()
{ alert("username: " + this.username + "\npassword: " + this.password); }
调用方法:
var p = new Person();
p.get();

但是这里使用prototype方法生成对象,不能创建的时候初始化值,必须等对象创建出来以后,对属性修改。
var person1 = new Person();
var person2 = new Person();
person1.username = 'hello';
person1.get();   //hello tiger
person2.get();   //scott tiger

 JavaScript中的字符串也是不可变的,与java机制中一样,属于final类型的,字符串的拼接一会生成新对象

如果Person.prototype.username = new Array(); //如下面一楼贴出的图片person1和person2会共享scott
    person1.username.push("scott");
    person1.get(); //scott
    person2.get(); //scott

 混合的构造函数与原型方式,属性采用构造函数的方式定义,方法采用原型方式定义(这里比较推荐这种方式)
可以避免上面的共享属性的弊端,例如,
function Person(username,password)
{
    this.username = username;  //这里如果this.username = new Array(); 则新对象不会共享username
    this.password = password;
}
Person.prototype.get = function()
{
    alert("username: " + this.username + "\npassword: " + this.password);
}
调用方法:
var p = new Person("scott","tiger");
p.get();

另外还有一种方式:动态的原型方式
function Person()
{
    this.username = "scott";
    this.password = "tiger";
    if(typeof Person.flag == "undefined")  //或者Person.flag == undefined
    {
        Person.prototype.get = function()
        {
            alert("username: " + this.username + "\npassword: " + this.password);
        }
        Person.flag = true;
    }


下面来看看用JavaScript模拟java的继承特性
(1)继承的第一种方式:对象冒充
function Parent(username)
{
    this.username = username;
    this.hello = function()
    { alert("username: " + this.username); }
}

function Child(username,password)
{
    this.method = Parent;
    this.method(username);
    delete this.method;
    this.password = password;
    this.world = function()
    { alert("password: " + this.password); }
}

 
 


 

 根据上图,和带下划线的代码段,method属性作为桥梁引用Parent对象,调用Parent的构造函数后,删除了该属性!!
注意如果吧带下划线的代码段替换为Parent(username);是不行的,因为这样的话Parent中的hello()方法不能继承下来~

 调用上面的继承
var parent = new Parent('father');
var child = new Child('son','scott');

parent.hello(); //father
child.hello();  //son
child.world();  //scott

由此可见继承机制确实实现了!!!!

(2)继承的第二种方式:call()方法
这里先介绍call()方法:每个function,即Function对象都有call()这个方法,call(param1,param2,...),
param1表示调用call()方法的函数内部的this,param2,...对应调用call()方法的函数中的参数列表,例如,

function test(str)
{
    alert(this.name + " " + str);
}
var obj = new Object();
obj.name = 'scott';
test.call(obj,'tiger'); //这里将obj对象赋给test中的this,弹出scott tiger


了解了call()的功能以后,我们来用call()实现继承机制,例子如下:

function Parent(username)
{
    this.username = username;
    this.hello = function()
    {
        alert(this.username);
    }
}

function Child(username, password)
{
    Parent.call(this,username);
    this.password = password;
    this.world = function()
    { alert(this.password); }
}

调用方法类似,这里就省略了。。。

(3)继承的第三种方式:apply()方式
这里对比一下apply()和call()
call(parm1,parm2,...,parmN)  多个参数

apply(parm1,new Array())  <=> apply(parm1,[parm2,...,parmN])  两个参数

归纳一下,apply方法的第一个参数和call一样,第二个参数是call后面参数组成的一个数组

下面用apply()来实现继承的机制,如下:
function Parent(username)
{
    this.username = username;
    this.hello = function()
    { alert(this.username); }
}

function Child(username,password)
{
    Parent.apply(this,new Array(username));  //等价于Parent.apply(this,[username]);
    this.password = password;
    this.world = function()
    { alert(this.password); }
}

(4)继承的第四种方式:原型链方式
function Parent() { }
Parent.prototype.hello = "hello";
Parent.prototype.sayHello = function()
{ alert(this.hello); }

function Child() { }
Child.prototype = new Parent();
Child.prototype = "world";
child.prototype.sayWorld = function()
{
    alert(this.world);
}

调用一下:
var child = new Child();
child.sayHello();  //hello
child.sayWorld();  //world 


(5)继承的第五种方式:混合方式(最佳实践,推荐使用)

function Parent(hello)
{
    this.hello = hello;
}

Parent.prototype.sayHello = function()
{ alert(this.hello); }

function Child(hello,world)
{
    Parent.call(this,hello);
    this.world = world;
}

Child.prototype = new Parent();
Child.prototype.sayWorld = function()
{ alert(this.world); }

调用一下:
var child = new Child('hello','world');
child.sayHello();  //hello
child.sayWorld();  //world 


最后举个例子来应用JavaScript的继承机制的应用

function Shape(edge) //一个抽象的形状
{
    this.edge = edge;
}

Shape.prototype.getArea = function() //获取形状的面积,类似于接口中的方法,这里先返回-1
{ return -1; }

Shape.prototype.getEdge = function() //返回形状的边有多少条
{ return this.edge; }

下面定义子类,三角形
function Triangle(bottom,height) //三角形,出入参数为底边和高
{
    Shape.call(this,3);
    this.bottom = bottom;
    this.height = height;
}
Triangle.prototype = new Shape();
Triangle.prototype.getArea = function() //返回三角形的面积,覆盖父类的方法
{ return 0.5 * this.bottom * this.height; }

调用三角形的对象和方法:
var triangle = new Triangle(10,4);
alert(triangle.getEdge()); //3
alert(triangle.getArea()); //20

下面再定义一个四边形
function Rectangle(bottom, height)
{
    Shape.call(this,4);
    this.bottom = bottom;
    this.height = height;
}
Rectangle.prototype = new Shape();
Rectangle.prototype.getArea = function()
{ return this.bottom * this.height; }

调用四边形的对象和方法
var rectangle = new Rectangle(10,20);
alert(rectangle.getEdge());  //4
alert(rectangle.getArea());  //200

posted @ 2011-06-02 00:52  易独  阅读(327)  评论(0编辑  收藏  举报
乐巴儿 一个有声音的公众号
长按,识别二维码,加关注