js面向对象和继承的碎碎念
2019独角兽企业重金招聘Python工程师标准>>>
一、prototype属性的特点:
1、定义在prototype中的方法是"实例方法",必须是new出来的实例,才能调用prototype中的方法,相同的方法可以被不同的实例调用,互不干扰:
function Human (name) {this.name = name; } Human.prototype = {sayHi:function () {console.log('Hi! I\'m '+this.name+'.');} }var tom = new Human('Tom'); tom.sayHi();// Hi! I'm Tom. var perter = new Human('Perter'); perter.sayHi();// Hi! I'm Perter.可以做个小调整,将打招呼的的方法sayHi()移到构造函数中,一实例化就执行,不必单独调用:
function Human (name) {this.name = name;this.sayHi() } Human.prototype = {sayHi:function () {console.log('Hi! I\'m '+this.name+'.');} }var tom = new Human('Tom');// Hi! I'm Tom. var perter = new Human('Perter');// Hi! I'm Perter.自问:但是,这样跟在window全局作用域写个sayHi方法,传不同值调用有何区别?:
function sayHi (name) {console.log("Hi! I'm "+name+'.'); } sayHi('Tom');// Hi! I'm Tom. sayHi('Perter');// Hi! I'm Perter.自答:全局的function对象适合封装简单的小块逻辑,如果是较为复杂的逻辑,都写在一个function对象中会使逻辑更趋复杂,难以维护,一般会拆分成多个function,以便于管理和维护:
function Human (name) {this.name = name;this.sayHi() } Human.prototype = {sayHi:function () {console.log('Hi! I\'m '+this.name+'.');},work:function () {console.log("For live,I must work.");},eat:function () {console.log("For live,I must eat.");}sex:function () {console.log("Sex is an instinct");} }上例的Human类比开始多定义了3个方法,prototype实现的是,对已经拆分的逻辑模块(sayHi,work,eat,sex),再封装后的复用,这是一个简单的function对象难以做到的。
2、其他属性定义的方法都是"静态方法",只能在统一不变的"类"的层级调用。
3、独立的json对象也是封装逻辑的很好容器,但因为是引用类型,在相同的文档上下文中,复用会出现后者参数覆盖前者的情况。
二、使用prototype的注意事项:
1、如果使用xx.prototype={}的方式封装逻辑,会影响"constructor"值,所以使用后注意重置"constructor"为当前的类。方便以后判断一个实例的构造类是谁。
function Human (name) {this.name = name; } var a = new Human('Peter'); console.log(a.constructor);上面代码的输出结果是:
我们得到了实例a的构造类是Human!
但是使用xx.prototype={}的方式封装逻辑后:
function Human (name) {this.name = name; } Human.prototype = {sayHi:function () {console.log("Hi! I'm "+this.name+'.');} } var a = new Human('Peter'); console.log(a.constructor);输出结果是:
我们已经不再知道实例a的构造类是谁?所以,应该重置"constructor"为当前的类:
function Human (name) {this.name = name; } Human.prototype = {sayHi:function () {console.log("Hi! I'm "+this.name+'.');} } Human.prototype.constructor = Human; var a = new Human('Peter'); console.log(a.constructor);输出结果又是:
2、在构建继承父类的子类时,子类会调用父类的构造函数,生成实例,再赋值给子类的prototype。也就是子类还没调用,子类继承自父类的构造方法已经执行了。
// 定义"人"类 function Human () {alert("hello world!"); } Human.prototype = {sayHi:function () {console.log("hello world!");} } Human.prototype.constructor = Human;// 定义"女人"类 function Woman (name) {} // "女人"类继承"人"类 Woman.prototype = new Human(); // 定义"女人"类的特有方法 Woman.prototype.bear = function () {console.log("I can bear baies."); } Woman.prototype.constructor = Woman;上述代码只是定义了一个Human类,然后Woman子类继承Human父类,还没有实例化,就执行了
alert("hello world!");不仅不合理,当父类构造函数含有破坏性代码,或者要依赖特定状态时,还会引发其他错误。
所以,通常我们需要单独封装一个实现继承的方法,而不是仅仅把子类的prototype赋值为父类的实例。
三、继承方法的封装步骤:
3.1.构造一个新类,该类具有一个空的构造函数,将该类的prototype赋值为父类的prototype,然后将该类的实例赋值给子类的prototype:
function inheritance(){}; inheritance.prototype = superClass.prototype; subClass.prototype = new inheritance();构造函数为空了,子类再来继承时,也就不会出现还没实例化,代码已经开始执行的情况了。
3.2.重置恢复子类的constructor:
subClass.prototype.constructor = subClass;便于对实例追根溯源。
3.3.子类自定义属性(baseConstructor)保存对父类的构造函数的引用:
subClass.baseConstructor = superClass;构造函数只是在过度的“inheritance”类置空了,它的引用仍然会通过子类的自定义属性(baseConstructor)保留,方便在需要的时候调用。
3.4.如果父类自定义属性(__super__)包含对祖父类的引用,那么这个属性要赋值给父类prototype下的同名属性:
if(superClass.__super__){superClass.prototype.__super__ = superClass.__super__; }只要保存在prototype属性中的方法才能被后代继承,这样做方便子类对祖先类的方法的重写。
3.5.子类自定义属性(__super__)保存对父类的prototype的引用:
方便子类对父类的方法的重写。
完整的代码:
// 实现继承的方法 function extend (subClass,superClass) {// 创建一个新的类,该类具有一个空的构造函数,并具有父类的成员function inheritance(){};inheritance.prototype = superClass.prototype;// 将子类的prototype属性设置为不带构造函数的父类新实例subClass.prototype = new inheritance();subClass.prototype.constructor = subClass;// 重置恢复constructor属性subClass.baseConstructor = superClass;// 用自定义属性保存父级的构造函数// 允许多重继承if(superClass.__super__){superClass.prototype.__super__ = superClass.__super__;}subClass.__super__ = superClass.prototype;// 保持对父类原型的引用,方便子类重写 },
转载于:https://my.oschina.net/710409599/blog/618884
与50位技术专家面对面20年技术见证,附赠技术全景图总结
以上是生活随笔为你收集整理的js面向对象和继承的碎碎念的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: golang(5):编写WebSocke
- 下一篇: 收集Cocos2d提供的字体