JavaScript中的面向对象(二)
来源:互联网 发布:电视直播软件v7.3.4 编辑:程序博客网 时间:2024/06/15 08:43
javascript基于原型的面向对象编程
上一篇面向对象编程(一)讲了很多关于面向对象的概念、原型、原型链的内容,都是为最后的面向对象实现做铺垫,
如果不明白原型链的实现机制,基于原型的对象继承将会很难理解。
封装
先使用构造函数声明一个类,在构造函数中给this添加本地属性,
并实例化一个对象,这种方式可以为对象声明一个公共的本地属性:
function Animal(name) { this.name = name; this.sleep = function() { console.log(this.name + ' sleep'); };}var a1 = new Animal('不高兴');a1.sleep();
类名为Animal,使用大写字母开头,是编程的一种命名约定。
使用prototype也可以实现:
function Animal(name) { this.name = name;}Animal.prototype.sleep = function() { console.log(this.name + ' sleep');};var a1 = new Animal('不高兴');a1.sleep();
但是,两种声明公共属性/方法的方式是有区别的,使用hasOwnProperty()方法可以用来判断某一个属性到底是本地属性,还是继承自prototype对象的属性。
继承
function Animal() { console.log('Animal init');}Animal.prototype.sleep = function() { console.log('Animal sleep');};var a1 = new Animal(); // Animal inita1.sleep(); // Animal sleep
上面这段代码声明了一个Animal类,我现在想声明一个Cat类并继承它。
function Cat() { console.log('Cat init')}Cat.prototype = Animal.prototype;Cat.prototype.sleep = function() { console.log('Cat sleep')}var c2 = new Cat();c2.sleep(); // Cat sleepa1.sleep(); // Cat sleep上面这么写很显然是有问题的,这么写会使Cat.prototype与Animal.prototype引用相同对象。修改Cat.prototype的属性值会影响到Animal.prototype。
修改上面的代码
//声明一个Animal类function Animal() { console.log('Animal init');}Animal.prototype.sleep = function() { console.log('Animal sleep');};//声明一个Cat类function Cat() { console.log('Cat init')}Cat.prototype = new Animal();var c2 = new Cat(); //打印了Animal init Cat initc2.sleep(); // Cat sleep似乎也有问题,在给Cat.prototype属性赋值时,会实例化Animal。我们只是希望继承Animal.prototype,并不希望执行Animal的构造函数。
继续修改上面的代码
//声明一个Animal类function Animal() { console.log('Animal init');}Animal.prototype.sleep = function() { console.log('Animal sleep');};var a1 = new Animal();//声明一个Cat类function Cat() { console.log('Cat init')}利用一个空对象作为中介,实现对Animal的原型继承var Empty = function () {};Empty.prototype = Animal.prototype;Cat.prototype = new Empty();Cat.prototype.constructor = Cat;Cat.prototype.sleep = function() { console.log('Cat sleep');};var c3 = new Cat(); // Cat initc2.sleep(); // Cat sleepa1.sleep(); // Animal sleep
终于达到了想要的效果,现在来来重新组织下Animal与Cat的原型链:
将继承的方法封装成一个公共函数:
var Empty = function() {};function chain(object) { Empty.prototype = object; var result = new Empty(); Empty.prototype = null; return result;}
写一个小案例
var Empty = function() {};function chain(object) { Empty.prototype = object; var result = new Empty(); Empty.prototype = null; return result;}function Animal(name,color) { this.name = name; this.color = color;}Animal.prototype.sleep = function() { console.log(this.name + '在睡觉');}var a1 = new Animal('胖达','黑白');a1.sleep(); // 胖达 在睡觉function Cat() { //通过调用父类的构造函数实现初始化 Animal.apply(this,arguments);}Cat.prototype = chain(Animal.prototype);Cat.prototype.constructor = Cat;Cat.prototype.run = function() { console.log(this.name + "在跑步")};Cat.prototype.kind = "猫科";var c2 = new Cat('tom','黑色');c2.run(); // tom 在跑步c2.sleep(); // tom 在睡觉console.log(c2.kind); // 猫科console.log(c2.name); // 胖达function PersinCat() { Cat.apply(this,arguments);}PersinCat.prototype = chain(Cat.prototype);PersinCat.prototype.constructor = PersinCat;PersinCat.prototype.name = 'persian cat'; // 在原型中声明name属性var p3 = new PersinCat('肥仔','灰色');console.log(p3.name); //肥仔console.log(p3.__proto__.name);// persian cat,本地name属性赋值之后,并不会覆盖prototype中name属性的值
下面通过一个更详细的原型链图,来描述这个例子中本地属性与prototype属性之间的关系:
通过这个图,大家应该也看明白了,a1、c2、p3中的是本地属性,其他的都是prototype属性,从例子的运行结果可以知道,对本地属性赋值,并不会覆盖prototype属性。
在使用this访问对象的属性或方法时,是先从本地属性中查找,如果未到,那么它会向上遍历原型链,直到找到给定名称的属性为止,
当到达原型链的顶部(也就是Object.prototype)仍然没有找到指定的属性,就会返回undefined。
chain()函数也可以使用Object.create()函数替代,可以简单的理解成Object.create()完成的工作与chain()一样。
这样可以对上面例子的代码再优化,将类继承封装成一个独立函数:
var Empty = function() {};function chain(object) { Empty.prototype = object; var result = new Empty(); Empty.prototype = null; return result;}function extend(SubClass, SuperClass, overrides) { var subProto, name; SuperClass = SuperClass || Object; SubClass.prototype = chain(SuperClass.prototype); subProto = SubClass.prototype; subProto.constructor = SubClass; if (overrides) { for (name in overrides) { if (overrides.hasOwnProperty(name)) { subProto[name] = overrides[name]; } } }}
小案例代码重构
function Animal(name, color) { this.name = name; this.color = color;}extend(Animal, Object, { sleep: function() { console.log(this.name + ' 在睡觉'); }});var a1 = new Animal('胖达','黑白');a1.sleep(); // 胖达 在睡觉function Cat() { Animal.apply(this, arguments);}extend(Cat, Animal, { kind: "猫科", run: function() { console.log(this.name + ' 在跑步'); }});var c2 = new Cat('tom','黑色');c2.run(); // tom 在跑步c2.sleep(); // tom 在睡觉console.log(c2.kind); // 猫科console.log(c2.name); // 胖达function PersianCat() { Cat.apply(this, arguments);}extend(PersianCat, Cat, { name: 'persian cat',});var p3 = new PersinCat('肥仔','灰色');console.log(p3.name); //肥仔console.log(p3.__proto__.name);
- JavaScript中的面向对象(二)
- JavaScript(二)-- 面向对象
- javascript 面向对象二
- javascript面向对象(二)
- JavaScript 中的面向对象
- Javascript中的面向对象!
- Javascript中的面向对象
- JavaScript中的面向对象
- JavaScript中的面向对象
- JavaScript中的面向对象
- javascript中的面向对象
- Javascript中的面向对象
- Javascript中的面向对象
- javascript中的面向对象
- JavaScript中的面向对象
- JavaScript中的面向对象
- javascript面向对象编程(二)
- Javascript面向对象编程(二):继承
- 字符数组
- 非UI线程的线程池的封装
- Android runtime机制(一)init进程
- C++类静态成员与类静态成员函数详解
- CODEVS 1025 选菜
- JavaScript中的面向对象(二)
- 替换空格
- 操作系统的 (program)loader(程序加载器)
- 10分钟理解代理——JDK动态代理
- MySQL基础知识
- webrtc之signal机制
- [勇者闯LeetCode] 125. Valid Palindrome
- 算法系列—计算阶乘
- 基础头文件