JavaScript中继承原理与用法实例入门(2)

构造函数的属性和行为已经成功实现了继承,接下来我们要实现原型中的属性和行为的继承。既然Zhangsan类需要和Person类原型中同样的属性和行为,那么能否将Person类的原型直接传给Zhangsan类的原型,如下代码:
[代码6]

// 定义Person类 function Person (name){ this.name = name; this.type = "黄"; } Person.prototype={ say : function(){ console.info("我是一个"+ this.type +"种人,我的名字叫" + this.name); } } // 定义Zhangsan类 function Zhangsan (name){ Person.call(this,name); } Zhangsan.prototype = Person.prototype; // 实例化Zhangsan对象 var zs = new Zhangsan("张三"); // 我是一个黄种人,我的名字叫张三 zs.say();

通过Person类的原型传给Zhangsan类的原型,Zhangsan类成功获得了say行为,但事情并不像想象中的那么简单,如果我们要给Zhangsan类添加run行为呢?如下代码:
[代码7:添加run行为]

// 定义Person类 function Person (name){ this.name = name; this.type = "黄"; } Person.prototype={ say : function(){ console.info("我是一个"+ this.type +"种人,我的名字叫" + this.name); } } // 定义Zhangsan类 function Zhangsan (name){ Person.call(this,name); } Zhangsan.prototype = Person.prototype; Zhangsan.prototype.run = function(){ console.info("我100米短跑只要10秒!"); } // 实例化Zhangsan对象 var zs = new Zhangsan("张三"); zs.say(); // 我是一个黄种人,我的名字叫张三 zs.run(); //我100米短跑只要10秒! var zs2 = new Person("张三2"); zs2.run(); //我100米短跑只要10秒!

我们只想给Zhangsan类添加run行为,为什么Person类也获得了run行为了呢?这涉及传值和传址的两个问题----在JavaScript中,赋值语句会用传值和传地址两种不同的方式进行赋值,如果是数值型、不尔型、字符型等基本数据类型,在进行赋值时会将数据直接赋值一份,将赋值的那一份数据进行赋值,也就是通常所说的传值;如果是数组、hash对象等复杂数据类型,在进行赋值时会直接用内存地址赋值,而不是将数据赋值一份,这就是传址赋值,就是传数据的映射地址。
[代码8:传值与传址]

var a=10; // 基本数据类型 var b=a; // 将变量a保存的值赋值一份,传给变量b,b和a各保存一份数据 var c=[1,2,3]; // 复杂数据类型 var d=c; // 将变量c指向的数据内存地址传给变量d,c和d指向同一份数据 b++; d.push(4); console.info(a); // 10 console.info(b); // 11 变量b保存的数据更改不会影响到变量a console.info(c); // 1,2,3,4 变量c和d指向同一份数据,数据更改会相互影响 console.info(d); // 1,2,3,4

在原生JavaScript中,选择传值还是传地址是根据数据类型来自动判断的,但传地址有时候会给我们带来意想不到的麻烦,所以我们需要对复杂数据类型的赋值进行控制,让复杂数据类型也可以进行传值。

最简单的做法是遍历数组或者Hash对象,将数组或者Hash对象这种复杂的数据拆分成一个个简单数据,然后分别赋值,如下面代码:
[代码9:对复杂数据类型进行传值]

var a = [1, 2, 3] ,b = {name:'张三',sex:'男',tel:'1383838438'}; var c = [] ,d = {}; for(var p in a){ c[p] = a[p]; } for(var p in b){ d[p] = b[p]; } c.push('4'); d.email = 'ibing@outlook.com'; console.info(a); // [1, 2, 3] console.info(c); // [1, 2, 3, "4"] console.info(b.email); // undefined console.info(d.email); // ibing@outlook.com

值得一提的是,对于数组的传值还可以使用数组类的slice或者concat方法实现,如下面代码:
[代码10:数组传值的简单方法]

var a = [1, 2, 3]; var b = a.slice(), c = a.concat(); b.pop(); c.push(4); console.info(a); // [1, 2, 3] console.info(b); // [1, 2] console.info(c); // [1, 2, 3, 4]

prototype本质上也是一个hash对象,所以直接用它赋值时会进行传址,这也是为什么[代码7:添加润行为]中,zs2居然会run的原因。我们可以用for in来遍历prototype,从而实现prototype的传值。但因为prototype和function(用做类的function)的关系,我们还有另外一种方法实现prototype的传值----new SomeFunction(),如下面代码:
[代码11]

// 定义Person类 function Person (name){ this.name = name; this.type = "黄"; } Person.prototype={ say : function(){ console.info("我是一个"+ this.type +"种人,我的名字叫" + this.name); } } // 定义Zhangsan类 function Zhangsan (name){ Person.call(this,name); } Zhangsan.prototype = new Person(); Zhangsan.prototype.constructor = Person; Zhangsan.prototype.run = function(){ console.info("我100米短跑只要10秒!"); } // 实例化Zhangsan对象 var zs = new Zhangsan("张三"); zs.say(); // 我是一个黄种人,我的名字叫张三 zs.run(); // 我100米短跑只要10秒! var zs2 = new Person("张三2"); zs2.run(); // TypeError: zs2.run is not a function

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/a7b74ea102610f1739ae9f98662998b3.html