当前位置: 代码迷 >> JavaScript >> js种与继承
  详细解决方案

js种与继承

热度:133   发布时间:2012-11-22 00:16:41.0
js类与继承

JavaScript 类

一、javascript也作为一种面向对象的语言,它也可以有类,有对象,可以用new来创建类实例,用实例调用它的方法。只是javascript中类形式与java以及其他面向对象语言有所差异而已。

二、类举例

function className()
{
?this.a = 8;
?this.setA = function (parame)
?{
??this.a = parame;
?};
}

function testClass()
{
?var cls = new className();
?document.write(cls.a);
?cls.setA(9);
?document.write(cls.a);
}

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

深入理解javascript类,参考一下

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

三、this关键字

this属性表示当前对象,如果在全局作用范围内使用this,则指代当前页面对象window; 如果在函数中使用this,则this指代什么是根据运行时此函数在什么对象上被调用。 我们还可以使用apply和call两个全局方法来改变函数中this的具体指向。

先看一个在全局作用范围内使用this的例子:

<?script?type="text/javascript">?

????console.log(this?===?window);??//?true ?

????console.log(window.alert?===?this.alert);??//?true ?

????console.log(this.parseInt("021",?10));??//?10 ?

<?/script>

?

函数中的this属性是在运行时决定的,而不是函数定义时,如下:

//?定义一个全局函数 ?

function?foo()?{ ?

????console.log(this.fruit); ?

} ?

//?定义一个全局变量,等价于window.fruit?=?"apple"; ?

var?fruit?=?"apple"; ?

//?此时函数foo中this指向window对象 ?

//?这种调用方式和window.foo();是完全等价的 ?

foo();??//?"apple" ?

?

//?自定义一个对象,并将此对象的属性foo指向全局函数foo ?

var?pack?=?{ ?

????fruit:?"orange", ?

????foo:?foo ?

};

//?此时函数foo中this指向window.pack对象 ?

pack.foo();?//?"orange"??

全局函数apply和call可以用来改变函数中this属性的指向,如下:

//?定义一个全局函数 ?

?function?foo()?{ ?

?????console.log(this.fruit); ?

?} ?

? ?

?//?定义一个全局变量 ?

?var?fruit?=?"apple";??

?//?自定义一个对象 ?

?var?pack?=?{ ?

?????fruit:?"orange"?

?}; ?

? ?

?//?等价于window.foo(); ?

?foo.apply(window);??//?"apple" ?

?//?此时foo中的this?===?pack ?

?foo.apply(pack);????//?"orange" ?

注:apply和call两个函数的作用相同,唯一的区别是两个函数的参数定义不同。

?

因为在JavaScript中函数也是对象,所以我们可以看到如下有趣的例子:

?

//?定义一个全局函数 ?

function?foo()?{ ?

????if?(this?===?window)?{ ?

????????console.log("this?is?window."); ?

????} ?

} ?

?

//?函数foo也是对象,所以可以定义foo的属性boo为一函数

foo.boo?=?function()?{ ?

????if?(this?===?foo)?{ ?

????????console.log("this?is?foo."); ?

????}?else?if?(this?===?window)?{ ?

????????console.log("this?is?window."); ?

????} ?

}; ?

//?等价于window.foo(); ?

foo();??//?this?is?window. ?

?

//?可以看到函数中this的指向调用函数的对象 ?

foo.boo();??//?this?is?foo. ?

?

//?使用apply改变函数中this的指向 ?

foo.boo.apply(window);??//?this?is?window.

?

?

四、prototype属性本质上还是一个JavaScript对象。并且每个函数都有一个默认的prototype属性。如果这个函数被用在创建自定义对象的场景中,我们称这个函数为构造函数。比如下面一个简单的场景:

prototype用在创建自定义对象中

//?构造函数 ?

function?Person(name)?{ ?

????this.name?=?name; ?

}??

?

//?定义Person的原型,原型中的属性可以被自定义对象引用

Person.prototype?=?{ ?

????getName:?function()?{ ?

????????return?this.name; ?

????} ?

} ?

var?zhang?=?new?Person("ZhangSan"); ?

console.log(zhang.getName());???//?"ZhangSan"?

作为类比,我们考虑下JavaScript中的数据类型 - 字符串(String)、数字(Number)、数组(Array)、对象(Object)、日期(Date)等。 我们有理由相信,在JavaScript内部这些类型都是作为构造函数来实现的,比如:

?

//?定义数组的构造函数,作为JavaScript的一种预定义类型

function?Array()?{ ?

????//?... ?

} ?

?

//?初始化数组的实例 ?

var?arr1?=?new?Array(1,?56,?34,?12); ?

//?但是,我们更倾向于如下的语法定义: ?

var?arr2?=?[1,?56,?34,?12];?

?

?

?

同时对数组操作的很多方法(比如concat、join、push)应该也是在prototype属性中定义的。

实际上,JavaScript所有的固有数据类型都具有只读的prototype属性(这是可以理解的:因为如果修改了这些类型的prototype属性,则哪些预定义的方法就消失了), 但是我们可以向其中添加自己的扩展方法。

?

//?向JavaScript固有类型Array扩展一个获取最小值的方法 ?

Array.prototype.min?=?function()?{ ?

????var?min?=?this[0]; ?

????for?(var?i?=?1;?i?<??this.length;?i++)?{ ?

????????if?(this[i]?<??min)?{ ?

????????????min?=?this[i]; ?

????????} ?

????} ?

????return?min; ?

}; ?

?

//?在任意Array的实例上调用min方法 ?

console.log([1,?56,?34,?12].min());??//?1?

?

注意:这里有一个陷阱,向Array的原型中添加扩展方法后,当使用for-in循环数组时,这个扩展方法也会被循环出来。

下面的代码说明这一点(假设已经向Array的原型中扩展了min方法):

var?arr?=?[1,?56,?34,?12]; ?

var?total?=?0; ?

for?(var?i?in?arr)?{ ?

????total?+=?parseInt(arr[i],?10); ?

} ?

console.log(total);???//?NaN?

解决方法也很简单:

var?arr?=?[1,?56,?34,?12]; ?

var?total?=?0; ?

for?(var?i?in?arr)?{ ?

????if?(arr.hasOwnProperty(i))?{ ?

????????total?+=?parseInt(arr[i],?10); ?

????} ?

} ?

console.log(total);???//?103?

?

五、parseInt的使用

①parseInt(string,radix) 函数可解析一个字符串,并返回一个整数。其中radix非必须,表示要解析的数字的基数。该值介于 2 ~ 36 之间。如果省略该参数或其值为 0,则数字将以 10 为基础来解析。如果它以 “0x” 或 “0X” 开头,将以 16 为基数。如果该参数小于 2 或者大于 36,则 parseInt() 将返回 NaN。当参数 radix 的值为 0,或没有设置该参数时,parseInt() 会根据 string 来判断数字的基数。举例,如果 string 以 "0x" 开头,parseInt() 会把 string 的其余部分解析为十六进制的整数。如果 string 以 0 开头,那么 ECMAScript v3 允许 parseInt() 的一个实现把其后的字符解析为八进制或十六进制的数字。如果 string 以 1 ~ 9 的数字开头,parseInt() 将把它解析为十进制的整数。

②实例说明

parseInt("11"); //返回 11
parseInt("11",10); //返回 11 (10+1)
parseInt("11",2); //返回 3 (2+1)
parseInt("11",8); //返回?9 (8+1)
parseInt("11",16); //返回?17 (16+1)
parseInt("010");???????????????//返回10或8

parseInt("010"); //未定:返回 16 或 8

六、类继承

说到Javascript类继承,就必然离不开原型链,但只通过原型链实现的继承有着不少缺陷。

无参数类继承的问题

先看一段示例代码,实现B继承于A:

?

function?A()?{} ?

A.prototype.a1?=?function()?{}; ?

function?B()?{} ?

B.prototype?=?new?A(); ?

B.prototype.b1?=?function()?{}; ?

var?b?=?new?B(); ?

alert(b.constructor?==?A);?//?true ?

alert(b.constructor?==?B);?//?false??

这段代码的主要问 题是:

1、需要实例化A作为B的原型,此时就执行了A的构造函数。但按照面向对象的规则,实例化B之前,B及其父类A的构造函数都不应该执行。

2、更改了B的prototype,导致b.constructor不是B而是A。

有参类继承的问题

假设A和B都有两个字符串参数s1和s2,A中计算了两段字符串的总长度,B直接以s1、s2为参数调用A:

function?A(s1,?s2)? ?

{ ?

this.totalLength?=?s1.length?+?s2.length; ?

} ?

A.prototype.a1?=?function()? ?

{ ?

}; ?

function?B(s1,?s2)? ?

{ ?

} ?

B.prototype?=?new?A(); ?

B.prototype.b1?=?function()? ?

{ ?

}; ?

new?B(“ab”,?“123″);??

可以看到,这段代码中根本没有办法把s1和s2传到A,而又因为实例化A作为B的原型时没有 参数,所以出现了异常:s1?is?undefined?

?

解决方案

s1 和s2的作用域只在B内,要把它们传到A,就只能在B中操作,借助函数的apply方法就可以实现之:

function?B(s1,?s2)? ?

{ ?

A.apply(this,?arguments); ?

alert(this.totalLength); ?

}

?

?

?

?

?

?

部分出处:http://developer.51cto.com/art/200907/134911.htm

http://developer.51cto.com/art/201105/264432.htm

http://developer.51cto.com/art/200907/134909.htm

?

?

?

  相关解决方案