本文参考张龙老师和Java1234视频教程
方法的定义
function fn1(name){
alert(name+"1");
}
function fn1(name,age){
alert(name+"2");
}
fn1("zs");
比如上面的脚本中调用fn1之后,最后调用的是第二个,这该如何理解
var fn1 = function(name){
alert(name+"1");
};
var fn1 = function(name,age){
alert(name+"2");
};
fn1("zs");
方法的定义可以理解成上面这个代码片段,刚开始fn1指向第一个,然后fn1指向第二个
然后调用fn1的时候就会 调用第二个。
原理部分:定义函数,其实就是等于定义一个对象,通过Function来定义,下面以例子来解释
var fn1 = new Function("name","alert(name+"+1+");");
var fn1 = new Function("name","age","alert(name+"+2+");");
fn1("zs");
Function 对象中可以接受的参数是不定的,但是有一个规则:最后一个参数一定是方法体
从上面可以看出来其实每个方法其实也是个Function对象,也就是说方法本身也是个对象,所以 返回一个方法是很正常的,就等于返回个对象这样
方法的特性
既然说函数是对象,对象一般都有属性和方法,比如每个方法都有一个length属性,这个属性指的是 这个函数期望接受的参数个数,说到这里要提到一个特殊的用法arguments。arguments指的是函数实际接受的参数封装成的数组。
function fn1(){
}
alert(fn1());
上面的语法片段会打印 undefined,也就是说没有返回值的情况,就会返回undefined
在这里提一下,其实undefined是从null派生出来的,也就是说如果一个var a==undefined是true,那么a肯定==null也是true。
nul和undefined的深层次区别 参考:http://developer.51cto.com/art/201401/426966.htm
对象的创建
参考W3C中JavaScript
- 最原始方法
var oCar = new Object;
oCar.color = "blue";
//也可以oCar["color"] = "blue";
oCar.showColor = function() {
alert(this.color);
};
这个是最原始的,他有一点特别不好,如果想创建一百个对象,就要写一百遍这样的代码,所以产生了工厂方式
- 工厂方式
function createCar(sColor,iDoors,iMpg) {
var oTempCar = new Object;
oTempCar.color = sColor;
oTempCar.showColor = function() {
alert(this.color);
};
return oTempCar;
}
var oCar1 = createCar("red",4,23);
var oCar2 = createCar("blue",3,25);
oCar1.showColor(); //输出 "red"
oCar2.showColor(); //输出 "blue"
这里最好的是把函数搞成共享的,把函数写到外面,然后把showColor指向那个函数即可。从功能上讲,这样解决了重复创建函数对象的问题;但是从语义上讲,该函数不太像是对象的方法。所有这些问题都引发了开发者定义的构造函数的出现。
- 构造函数方式
functon show(){
alert(this.color);
}
function Car(sColor,iDoors,iMpg) {
this.color = sColor;
this.showColor = show;
//this指向调用者
}
var oCar1 = new Car("red",4,23);
var oCar2 = new Car("blue",3,25);
在执行第一行代码前先创建一个对象,只有用 this 才能访问该对象。然后可以直接赋予 this 属性,默认返回值也是this,这种方式比起工厂方式就更像后台语言定义类了
- 原型方式
function Car() {
}
Car.prototype.color = "blue";
Car.prototype.showColor = function() {
alert(this.color);
};
var oCar1 = new Car();
var oCar2 = new Car();
在这段代码中,首先定义构造函数(Car),其中无任何代码。接下来的几行代码,通过给 Car 的 prototype 属性添加属性去定义 Car 对象的属性。调用 new Car() 时,原型的所有属性都被立即赋予要创建的对象,意味着所有 Car 实例存放的都是指向 showColor() 函数的指针。从语义上讲,所有属性看起来都属于一个对象,因此解决了前面两种方式存在的问题。
alert(oCar1 instanceof Car); //输出 "true"
非常重要的一点是,通过原型定义的字段和方法都是所有事例共享的,就类似于JAVA的静态方法
- 混合的构造函数/原型方式
function Car(sColor,iDoors,iMpg) {
this.color = sColor;
this.drivers = new Array("Mike","John");
}
Car.prototype.showColor = function() {
alert(this.color);
};
var oCar1 = new Car("red",4,23);
var oCar2 = new Car("blue",3,25);
oCar1.drivers.push("Bill");
alert(oCar1.drivers); //输出 "Mike,John,Bill"
alert(oCar2.drivers); //输出 "Mike,John"
这个就是利用上面两个综合产生的最经典方式。当然后面还有根据这个进行的扩展动态原型,但是个人认为哪个没有这种混合的看的更清晰。所以我最推荐这种
修改对象
这个我感觉W3C里面讲的太经典了。http://www.w3school.com.cn/js/pro_js_object_modifying.asp
对象的继承
对象继承分为两种,其中一个是对象冒充 一个是原型
- 对象冒充
function ClassA(sColor) {
this.color = sColor;
this.sayColor = function () {
alert(this.color);
};
}
function ClassB(sColor) {
this.newMethod = ClassA;
this.newMethod(sColor);
delete this.newMethod;
}
所有新属性和新方法都必须在删除了新方法的代码行后定义。否则,可能会覆盖超类的相关属性和方法:
其实这个只要懂this就非常好理解。 var b =new ClassB(“red”).当进入this.newMethod = ClassA后就是给b(也就是this)添加了一个新的变量,这个变量指向ClassA方法。 this.newMethod(sColor); 这句话就是调用这个方法(这也说明刚注册的方法就可以调用),调用这个方法后,在ClassA中里面的this就是classB的this,也就是b,这样到最后b就拥有了color属性和sayColor方法
但是对象冒充有一个缺点就是,ClassA中的原型 属性和方法,ClassB却继承不了
方法的call
all() 方法是与经典的对象冒充方法最相似的方法。它的第一个参数用作 this 的对象。其他参数都直接传递给函数自身。例如:
function sayColor(sPrefix,sSuffix) {
alert(sPrefix + this.color + sSuffix);
};
var obj = new Object();
obj.color = "blue";
sayColor.call(obj, "The color is ", "a very nice color indeed.");
上面这种obj就等于sayColor中的this,后面就是参数对应
function ClassA(sColor) {
this.color = sColor;
this.sayColor = function () {
alert(this.color);
};
}
function ClassB(sColor) {
ClassA.call(this, sColor);
}
var b =new ClassB();
过程
1、var b=new ClassB();
然后进入ClassB,首先隐式创建一个对象,这个对象就是this,也是返回的对象
2、ClassA.call(this, sColor);
this就是b,然后进入ClassA方法,classA方法中的this就是传入的this,也即是b,这样就完成了 继承。
apply()方法和call类似,只是传参数的时候不一样。
ClassA.call(this, sColor)==ClassA(this,new Array(sColor));
ClassA.call(this, sColor,name)==ClassA(this,new Array(sColor,name));
- 原型方式实现继承
function ClassA() {
}
ClassA.prototype.color = "blue";
ClassA.prototype.sayColor = function () {
alert(this.color);
};
function ClassB() {
}
ClassB.prototype = new ClassA();
记住标准写法是传ClassA的空的构造函数
这种方式真是太巧妙了,让ClassB.prototype等于ClassA的一个实例
这样凡是ClassB的实例都享有ClassA的静态方法和变量
因为标准是new ClassA的空的构造函数 所有这种方式只能继承ClassA的静态的变量和字段(也就是原型字段)
- 混合方式
function ClassA(sColor) {
this.color = sColor;
}
ClassA.prototype.sayColor = function () {
alert(this.color);
};
function ClassB(sColor, sName) {
ClassA.call(this, sColor);
this.name = sName;
}
ClassB.prototype = new ClassA();
ClassB.prototype.sayName = function () {
alert(this.name);
};
这样经典的继承也就产生了。