JS高级程序设计笔记(四):类和继承
来源:互联网 发布:淘宝客能赚钱吗 编辑:程序博客网 时间:2024/06/13 13:31
一些方法
Person.prototype.isPrototypeOf(实例); //返回布尔值
Object.getPrototypeOf(实例); //返回原型对象
实例.hasOwnProperty(属性名) ;返回布尔值
for in 和 in会访问实例和原型属性;for in只遍历可枚举属性
Object.keys(o)返回可枚举实例属性名组成的数组,与for in中出现的顺序一致。要遍历所有实例属性(包括不可枚举的)使用Object.getOwnPropertyNames()。
如果通过这样的方式给原型对象添加属性和方法:
Person.prototype = { XXX: XXX, ...};
那么实际上是重写了原型对象,则该原型对象中的constructor
属性不再指向构造函数Person
, 而是指向Object
。除非重新更改constructor
的值,使其指向Person
.
重写了原型,仍然是实例仍然是可以用instanceof
来检测类型的。对于原型链上的对象以及构造函数都是返回true
。
7. 如果在创建了一个实例之后再重写原型对象,原来的实例的[prototype]
属性仍然是指向之前的原型对象的(因为构造函数会为实例添加一个指向最初原型对象的属性[prototype]
),因此应当在重写原型对象之后再创建实例:
function Person() {}Person.prototype = { constructor: Person, .....};var p = new Person();
创建类
- 构造函数中创建实例属性,原型中创建共享属性和方法。
function Person(name, age) { this. name = name; this.age = age; this.hobby = ['swimming', 'tennis'];};person.prototype = { constructor: Person, sayName: function() { alert(this.name); }};
- 动态原型模式:
function Person(name, age, gender) { this.name = name; this.age = age; this.gender = gender; if (typeof this.sayName != 'function') { Person.prototype.sayName = function() {}; Person.prototype...... }
该方式的优点是将原型的添加也放到了构造函数里,封装性更好;原型的添加也只有在第一次new
对象的时候才会执行,只需检测一个要添加的原型属性是否存在即可。
需注意的是该方式不能重写原型,否则实例指向的原型仍然是原来的原型。
- 寄生构造函数
function SpecialArray() { var arr = new Array(); arr.push.apply(arr, arguments); arr.toPipeString = function() { return this.join('|'); }; return arr;}var names = new SpecialArray('lily', 'anna', 'mike');console.log(names.toPipeString());console.log(names instanceof SpecialArray); //false
传统的构造函数会默认返回生成的实例,如果在构造函数中返回一个对象,则会重写返回的对象。
返回的对象和构造函数以及构造函数关联的原型没有半毛钱关系,不能用instanceof
来确定对象的类型。最好不要使用这种方式。
- 稳妥构造函数
没有this和new,适合在需要安全性的环境中使用。并且没有公用属性。
function Person(name, age) { //定义私有变量 var o = new Object(); o.sayName = function() { alert(name); }; return o;}var obj = Person("lily", 10);
只有在o.sayName
中可以访问到name
。避免修改。
继承
继承依赖原型链,主要思想是将超类的实例赋值给子类的原型,由于实例中持有对超类原型的引用,因此可以访问到超类中的原型属性(所有属性)。
- 采用构造函数和原型组合的方式
如果仅采用原型,由于SuperType的实例属性会成为SubType的原型属性,如果SuperType中包含引用类型数据,那么子类的多个实例会共享这些引用类型属性,这是不希望看到的。
而如果仅采用构造函数的方式,则不能函数复用,因此采用两者结合的组合继承方式:
function SuperType(name, hobby) { this.name = name; this.hobby = hobby;}SuperType.prototype.getSuperValue = function() { console.log(this.name + ' ' + this.hobby);};function SubType(age, name, hobby) { SuperType.call(this, name, hobby);//父类实例属性成为子类实例属性 this.age = age;}SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function() { console.log(this.age);};
由于父类实例中有一个不可访问的属性[prototype]指向SuperType.prototype,而子类的原型对象是父类的实例,因此形成了一条原型链。
采用这种方式,超类构造函数会被调用两次,超类的实例属性在子类中会以两种形式出现,第一次是在:
SubType.prototype = new SuperType();
超类的实例属性成为子类的原型属性。第二次是:
SuperType.call(this, name, hobby);
在构造函数中,子类的同名实例属性屏蔽了原型属性。可以采用寄生组合继承来解决这个问题。
- 原型继承
不使用构造函数。新对象的原型是某个已知的对象。相当于创建了一个已知对象的副本,修改新对象,也会影响到已知对象。例如:
function create(o) { function F(){} F.prototype = o; return new F();}
在ES5中提供了更为规范的函数Object.create(o[, {}])
后面的可选参数是一个对象,包含要为新对象添加的属性。
- 寄生组合式继承
组合继承的缺点是原型那里有冗余的属性,我们希望子类的原型是这么一个对象:它不拥有超类的实例属性,但持有超类原型对象的引用。因此不能直接以超类的实例来作为子类的原型对象。
//原型继承,适用于没有自定义类型,仅想继承ofunction object(o) { function F() {} F.prototype = o; return new F();}function inheritPrototype(SubType, SuperType) { var prototype = object(SuperType.prototype); prototype.constructor = SubType; SubType.prototype = prototype;}//寄生组合继承function SuperType(name, hobby) { this.name = name; this.hobby = hobby;}SuperType.prototype.getSuperValue = function() { console.log(this.name + ' ' + this.hobby);};function SubType(age, name, hobby) { SuperType.call(this, name, hobby);//父类实例属性成为子类实例属性 this.age = age;}inheritPrototype(SubType, SuperType); SubType.prototype.getSubValue = function() { console.log(this.age);};
上述代码中,返回的F实例其原型对象就是SuperType.prototype
,再把这个没有任何实例属性的F实例作为子类的原型。即子类的原型对象中的[prototype]
属性指向一个纯粹的父类的原型对象,而不是包含冗余的父类实例属性的父类实例。同时也只调用了一次父类的构造函数。刚好符合我们的两个要求。
寄生组合继承是目前最理想的继承方式。
- JS高级程序设计笔记(四):类和继承
- js高级程序设计笔记3--继承
- js高级程序设计笔记 -- 理解继承
- 《JS高级程序设计》第6章读书笔记:继承对象(四)寄生组合式继承
- javascript高级程序设计学习(四)----------继承
- 《JavaScript高级程序设计》学习笔记(继承)
- js 高级程序设计笔记
- JS高级程序设计-笔记
- javascript高级程序设计笔记(四)
- 《JS高级程序设计》第6章读书笔记:对象继承(三)原型式继承和寄生式继承
- JS高级程序设计笔记(一)-数据类型
- JS高级程序设计笔记(六)- 函数
- javascript高级程序设计笔记(四)
- js高级程序设计(第6章----继承)
- 《JS高级程序设计》第6章读书笔记:继承对象(二)借用构造函数和组合继承
- JavaScript高级程序设计(第2版) 学习笔记:(四)js获取窗口相对于屏幕左边和上边的位置
- JavaScript高级程序设计-学习笔记5(继承)
- js高级程序设计笔记1
- Android6.0关于预置三方app卸载(一) copy到data/app下
- 项目2-程序的多文件组织
- java中存储mysql数据库时间类型【date、time、datetime、timestamp】
- 第4周项目1 建立单链表
- linux socket 缓冲区默认大小
- JS高级程序设计笔记(四):类和继承
- android Activity的启动模式 作用简析+demo详解
- 第四周 项目3 单链表应用(1)
- 第四周项目1 建立单链表
- 第四周项目1 建立单链表
- Eclipse中用Tomcat发布的Web项目,更改其部署路径
- 第四周 项目1 - 建立单链表
- 编写shader时的一些建议
- Eclipse导入SVN项目的三种方式