javascript继承学习系列之三:对象伪装(Object Masquerading)
2011-07-06 13:27
711 查看
基本思想很简单:在子类的构造器上调用超类构造器。更确切的说是在子类构造器执行上下文来中执行超类构造器(父类构造器中的
this指向子类实例),js提供了apply()和call()函数,它们可以实现这种调用。基本代码如下:
function BaseClass()
{
this.colors=["red","blue"];
}
function ChildClass()
{
//inherit
BaseClass.call(this);
}
var instance1 = new ChildClass();
var instance2 = new ChildClass();
instance2.colors.push("green");
var instance3 = new ChildClass();
alert(instance1.colors);//red,blue
alert(instance2.colors);//red,blue,green
alert(instance3.colors);//red,blue
结果正是我们所预期的,即使对某个实例继承的属性进行了修改,对其他实例也没影响。代码很简单,核心的是子类构造器里的call()的执行,与它具有相同功效的是apply(),所以这里有必要对这两个神奇的函数进行简单说明。
js中每个function都有这call()和apply()方法。call()函数的功能是在指定的对象上执行函数,它的第一个参数就是指定的对象。它还可以有多个参数,除第一个之外的参数都是调用指定函数所需要的参数,例如:
function fun(x,y)
{
this.pointX = x;
this.pointY = y;
}
var dot = {};
fun.call(dot,10,20);
alert(dot.pointX);//10
alert(dot.pointY);//20
其中代码fun.call(dot,10,20)使得dot具有了fun定义的属性。它的意思就是在dot对象上运行fun(),在运行时,fun中的this指向dot,同时向fun()传递了两个参数10和20,分别传给了fun的形参x和y。
而apply()功用上与call()相同,但是调用形式上有些不同,将上个示例中将call()改成apply(),如下:
function(x,y)
{
this.pointX = x;
this.pointY = y;
}
var dot={};
fun.apply(dot,[10,20]);
alert(dot.pointX);//10
alert(dot.pointY);//20
区
别在于第一个实参(dot)之后的参数,fun()调用所需的实参以数组的形式传递给形参了。在这个示例中apply()的优点似乎不够明显,设想一下这
样的场景:用户调用一个函数,在执行这个函数时,同时要运行另外一个函数,但必须是同一个上下文中(this指向的是同一个对象),而用户传给第一个函数
的参数是不确定的,第一个函数传给第二个函数的参数也是将是不确定的,这种情况下用apply()就很合适了。如:
function fun2(x,y,z)
{
x=x||0;
y=y||0;
z=z||0;
var list = [];
list.push(x);
list.push(y);
list.push(z);
alert(list);
}
function fun1()
{
fun2.apply(this,arguments);
}
fun1(10);//10,0,0
fun1(10,20);//10,20,0
fun1(10,20,30);//10,20,30
可见,如果函数参数传递时是固定的,用call()或apply()都可以,如果参数不是固定的,就要用apply()。
构造器伪装存在一个问题,子类没有办法继承到父类原型上的属性和方法,它只能继承到在父类构造器里定义的属性和方法。而很多时候我们定义类时,为了避免每次new时重复定义方法,会将方法定义到它的原型上,而不是构造器里。考虑以下代码:
function BaseClass(n,g)
{
this.name = n;
this.gender = g;
this.getName = function(){return this.name;};
}
BaseClass.prototype.getGender = function(){return this.gender;};
function ChildClass(n,g,a)
{
BaseClass.call(this,n,g);
this.age = a;
}
ChildClass.prototype.getAge=function(){return this.age;};
var instance = new ChildClass("sds","M",30);
alert(instance.getName());//sds
alert(instance.getAge());//30
alert(instance.getGender());//error,The getGender() don't inherit from BaseClass;
预
想的是getGender()返回"M",要命的是报了个致命错误。这个示例给我的们展示了,仅靠构造器伪装方式,继承是不可靠的。这里还要说明一点,对
于getName(),由于它定义在父类构造器里,就意味这每次创建父类对象或子类对象时,它都会重新被创建,而这种重复工作,大多数时候是我们要极力避
免的。很多时候,我们将属性定义在构造器里,而将方法定义在原型对象上。这在OO里是讲得通的,因为每个对象拥有不同的数据(属性),但是拥有相同的行为
(方法)。好比我们人类自己,每个人(实例对象)都有自己区别于其他人的特征:姓名、年龄、性别等,但却拥有相同的行为:说话()、行走()等。
this指向子类实例),js提供了apply()和call()函数,它们可以实现这种调用。基本代码如下:
function BaseClass()
{
this.colors=["red","blue"];
}
function ChildClass()
{
//inherit
BaseClass.call(this);
}
var instance1 = new ChildClass();
var instance2 = new ChildClass();
instance2.colors.push("green");
var instance3 = new ChildClass();
alert(instance1.colors);//red,blue
alert(instance2.colors);//red,blue,green
alert(instance3.colors);//red,blue
结果正是我们所预期的,即使对某个实例继承的属性进行了修改,对其他实例也没影响。代码很简单,核心的是子类构造器里的call()的执行,与它具有相同功效的是apply(),所以这里有必要对这两个神奇的函数进行简单说明。
js中每个function都有这call()和apply()方法。call()函数的功能是在指定的对象上执行函数,它的第一个参数就是指定的对象。它还可以有多个参数,除第一个之外的参数都是调用指定函数所需要的参数,例如:
function fun(x,y)
{
this.pointX = x;
this.pointY = y;
}
var dot = {};
fun.call(dot,10,20);
alert(dot.pointX);//10
alert(dot.pointY);//20
其中代码fun.call(dot,10,20)使得dot具有了fun定义的属性。它的意思就是在dot对象上运行fun(),在运行时,fun中的this指向dot,同时向fun()传递了两个参数10和20,分别传给了fun的形参x和y。
而apply()功用上与call()相同,但是调用形式上有些不同,将上个示例中将call()改成apply(),如下:
function(x,y)
{
this.pointX = x;
this.pointY = y;
}
var dot={};
fun.apply(dot,[10,20]);
alert(dot.pointX);//10
alert(dot.pointY);//20
区
别在于第一个实参(dot)之后的参数,fun()调用所需的实参以数组的形式传递给形参了。在这个示例中apply()的优点似乎不够明显,设想一下这
样的场景:用户调用一个函数,在执行这个函数时,同时要运行另外一个函数,但必须是同一个上下文中(this指向的是同一个对象),而用户传给第一个函数
的参数是不确定的,第一个函数传给第二个函数的参数也是将是不确定的,这种情况下用apply()就很合适了。如:
function fun2(x,y,z)
{
x=x||0;
y=y||0;
z=z||0;
var list = [];
list.push(x);
list.push(y);
list.push(z);
alert(list);
}
function fun1()
{
fun2.apply(this,arguments);
}
fun1(10);//10,0,0
fun1(10,20);//10,20,0
fun1(10,20,30);//10,20,30
可见,如果函数参数传递时是固定的,用call()或apply()都可以,如果参数不是固定的,就要用apply()。
构造器伪装存在一个问题,子类没有办法继承到父类原型上的属性和方法,它只能继承到在父类构造器里定义的属性和方法。而很多时候我们定义类时,为了避免每次new时重复定义方法,会将方法定义到它的原型上,而不是构造器里。考虑以下代码:
function BaseClass(n,g)
{
this.name = n;
this.gender = g;
this.getName = function(){return this.name;};
}
BaseClass.prototype.getGender = function(){return this.gender;};
function ChildClass(n,g,a)
{
BaseClass.call(this,n,g);
this.age = a;
}
ChildClass.prototype.getAge=function(){return this.age;};
var instance = new ChildClass("sds","M",30);
alert(instance.getName());//sds
alert(instance.getAge());//30
alert(instance.getGender());//error,The getGender() don't inherit from BaseClass;
预
想的是getGender()返回"M",要命的是报了个致命错误。这个示例给我的们展示了,仅靠构造器伪装方式,继承是不可靠的。这里还要说明一点,对
于getName(),由于它定义在父类构造器里,就意味这每次创建父类对象或子类对象时,它都会重新被创建,而这种重复工作,大多数时候是我们要极力避
免的。很多时候,我们将属性定义在构造器里,而将方法定义在原型对象上。这在OO里是讲得通的,因为每个对象拥有不同的数据(属性),但是拥有相同的行为
(方法)。好比我们人类自己,每个人(实例对象)都有自己区别于其他人的特征:姓名、年龄、性别等,但却拥有相同的行为:说话()、行走()等。
相关文章推荐
- Javascript学习笔记(Object 对象与继承)
- Object-C 学习笔记(八)---面相对象(继承)
- Scala学习教程笔记二之函数式编程、Object对象、伴生对象、继承、Trait、
- javascript系列学习----对象相关概念理解
- Ext学习系列一(JavaScript 对象设计)
- 【JavaScript】【学习】对象的创建和继承
- javascript中的Function对象和Object对象的区别以及对象继承
- javascript面向对象编程的学习---对象继承
- 【Javascript学习笔记】由JavaScript中call()方法引发的对面向对象继承机制call的思考
- JavaScript学习系列之深入原型链与继承的实现
- JavaScript 学习_4_原型_对象_继承
- javascript面向对象编程的学习---对象继承
- Javascript学习6 - 类、对象、继承
- Javascript学习笔记--Object对象
- Sharepoint学习笔记 –架构系列—Sharepoint的服务器端对象模型(Server Object Model) 1.物理对象层次结构
- JavaScript 学习笔记 - 对象和继承
- Javascript对象学习2 - 继承
- javascript继承学习系列之一:初识JS的OOP
- [JavaScript]Object(对象)学习