博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JavaScript面向对象初探——封装和继承
阅读量:5135 次
发布时间:2019-06-13

本文共 3879 字,大约阅读时间需要 12 分钟。

 

1.封装类

怎么封装一个类,也就是创建自定义对象?

  • 构造函数式
1 function Car(sColor,iDoors,iMpg) { 2   this.color = sColor; 3   this.doors = iDoors; 4   this.mpg = iMpg; 5   this.showColor = function() { 6     alert(this.color); 7   }; 8 } 9 10 var oCar1 = new Car("red",4,23);11 var oCar2 = new Car("blue",3,25);

和Java模式最像。缺点是每生成实例都会创建一次showColor。

  • 原型方式
1 function Car() {} 2  3 Car.prototype.color = "blue"; 4 Car.prototype.doors = 4; 5 Car.prototype.mpg = 25; 6 Car.prototype.showColor = function() { 7   alert(this.color); 8 }; 9 10 var oCar1 = new Car();

利用了前述prototype。缺点是不灵活,无法传参数。

通过给this添加属性的方式创建成员变量都是公有的,在函数体内var的变量都是私有的。类用一个Function来声明,这个Function就是构造函数,如果变量再构造函数外声明就等同静态变量,不需要new就可以访问。

  • 构造函数和原型混合式

这种方式为了避免重复创建函数副本。

1 function Car(sColor,iDoors,iMpg) { 2   this.color = sColor; 3   this.doors = iDoors; 4   this.mpg = iMpg; 5   this.drivers = new Array("Mike","John"); 6 } 7  8 Car.prototype.showColor = function() { 9   alert(this.color);10 };11 12 var oCar1 = new Car("red",4,23);13 var oCar2 = new Car("blue",3,25);

但第12行如果不小心忘写了new,Car中的this就是window了,为了避免几个属性加载window上,可以在Car内添加this instanceof Car判断。

 

2.原型链

所有对象都拥有prototype属性,这个属性也是一个对象。给obj.prototype添加属性和方法意味着这些属性和方法挂在了原型链上,原型链下游的对象(子类)可以继承到原型链上所有的方法。也就是说,obj的prototype对象中的属性一般情况下不是给自己用的,用obj.hasOwnProperty(obj.prototype.attr)结果都为false,给自己添加属性直接obj.attr=XXX。

当一个子类查找一个属性时,首先在自己的属性中查找(注意不去找自己的prototype里的属性),然后查找父类的prototype,一直找到原型链顶端Object.prototype。这个原型链的链接是由prototype.__proto__这个私有属性实现的,子类的prototype.__proto__指向父类的prototype。所有类的基类Object的prototype.__proto__指向null,所以原型链总是有穷的。

对象实例与上面说的子类类似。

 

3.继承

先看一下call和prototype方式实现继承

1 function BMW(sColor,iDoors,iMpg,len){ 2     Car.call(this,sColor,iDoors,iMpg); //继承父类Car的属性,可以是一部分. 详见aguments对象可实现重载 3     this.len = len;  //子类自有属性 4 }; 5 BMW.prototype = new Car();  //获得父类Car的所有方法 6 BMW.prototype.showLen = function(){  //子类自有方法 7     alert(this.len); 8 }; 9 var x5 = new BMW("green",4,35,5);10 x5.showColor(); 11 x5.showLen();   //结果弹出"green","5"
  • 上面代码第2行使用了call继承父类Car的属性。

原理:call方法的用法是func.call(obj,args),使obj可以调用本不是自己的方法func,从而改变this,args是参数。与它类似的是apply方法,只是传参时参数是数组形式。

  • 代码第5行使用了prototype继承父类所有方法。

原理:对象的任何属性和方法都被传递给那个对象的所有实例。子类的prototype设置成父类的实例,就继承了父类的所有属性和方法。但子类的 prototype 属性被覆盖了,原来的方法不复存在,所以子类自有属性需要在prototype设置之后添加。

继承的顺序:

上面的代码是先使用call继承属性,同时添加自有属性,再使用prototype继承方法,最后添加自己方法。不要疑惑这个顺序会产生覆盖。如果第5行改为BMW.prototype = new Car('yellow',3,25)才会覆盖。原因是JS的aguments对象,它的特性就不在这里细说了,可以利用它实现函数重载。其实使用call并没有继承到父类的任何属性,因为父类构造函数的参数undefined,父类实例中并没有添加上前几个几个属性,drivers属性有。

call和prototype的区别:

这里将父类实例赋给子类prototype有明显限制,引用类型成员在子类中以浅拷贝的方式存在,即一个子类修改引用类型成员,其他类都会受到影响。而call方式没有这个问题,只是每次都产生副本。

显而易见,call方法可以方便的实现多继承,而原型链方法不能支持多继承。

另外,call方法继承可以获取父类的一部分属性,而prototype继承只是全盘接收。

最后一点也相当重要,instanceof可以识别prototype继承,也就是下面两行代码结果都是真。

alert(x5 instanceof BMW);alert(x5 instanceof Car);

 

另一种安全的继承:复制继承

把属性复制都新对象上,是对象的深拷贝。当然也可以区分父类自有属性和继承属性,有取舍的复制。

1 var foo = {};2 var prop;3 for(prop in props){4      if(!foo[prop]){5            foo[attr] = obj[attr];6      }7 }

 

再谈原型继承

上面的

BMW.prototype = new Car();

如果换成

BMW.prototype = Car.prototype;

会有什么不一样呢?

如果直接把prototype赋值给子类,子类中改变引用类型的属性会影响到父类。而实例是通过分配空间,构造出来的个体,不会出现这种情况。看下面的例子:

1        function People(){} 2             People.prototype.name = 'human'; 3             People.prototype.hi = function(msg){ 4                 console.log(msg); 5             } 6  7             var Student = function(){}; 8             //Student.prototype = new People(); 9             Student.prototype = People.prototype;10 11             var stu = new Student();12             stu.hi && stu.hi('stu');13             Student.prototype.hi = {};14             People.prototype.hi('people');   //不是一个函数

 

ES5支持的Object.create()方法。其特点是洁净继承,和方式2相比,没有实例化父类这一步,而是使用一个新对象,把父类的原型复制给新对象的原型。

模拟Object.create():

1 function create(proto){ //要继承的父类的原型2   function F(){};3   F.prototype = proto;   4    return new F(); 5 }6 7 var student = create(People.prototype);

 

转载于:https://www.cnblogs.com/feitan/p/5166816.html

你可能感兴趣的文章
StringBuffer的用法
查看>>
js编写时间选择框
查看>>
PHP压缩文件操作
查看>>
Java数据结构和算法(四)--链表
查看>>
JIRA
查看>>
ssl介绍以及双向认证和单向认证原理
查看>>
【BZOJ2441】【中山市选2011】小W的问题(树状数组+权值线段树)
查看>>
小技巧——直接在目录中输入cmd然后就打开cmd命令窗口
查看>>
深浅拷贝(十四)
查看>>
由级别和性格特征将程序员分类 ---看看你属于哪一种
查看>>
HDU 6370(并查集)
查看>>
BZOJ 1207(dp)
查看>>
对我来说,只有一件事情是重要的
查看>>
完整的Socket代码
查看>>
PE知识复习之PE的导入表
查看>>
POJ 3280 Cheapest Palindrome
查看>>
HDU 2076 夹角有多大(题目已修改,注意读题)
查看>>
Objective-C非正式协议与正式协议
查看>>
洛谷P3676 小清新数据结构题(动态点分治)
查看>>
SPOJ DQUERY D-query(主席树 区间不同数个数)
查看>>