继承

说道这个继承,了解object-oriented的朋友都知道,大多oo语言都有两种,一种是接口继承(只继承方法签名);一种是实现继承(继承实际的方法)

奈何js中没有签名,因而只有实现继承,而且靠的是原型链实现的。下面正式的说一说js中继承那点事儿


1、原型链

关于原型链在上一节面向对象之原型提了一些,这节好好说一说。

原型链:实现继承的主要方法,利用原型让一个引用类型继承另一个引用类型的属性和方法。

回顾:构造函数,原型,实例三者的关系

每一个构造函数都有一个原型对象(Person.prototype);原型对象都包含指向构造函数的指针(constructor);每个实例都包含指向原型对象的指针(看不见的_proto_指针)

原型链是怎么来的呢?

某个构造函数的原型对象是另一个构造函数的实例;这个构造函数的原型对象就会有个(看不见的_proto_指针)指向另一个构造函数的原型对象;

那么另一个原型对象又是其他的构造函数实例又会怎么样,就这样层层递进,形成原型链;来具体看一下吧

       //第一个构造函数;有一个属性和一个原型方法
function SuperType(){
this.property=true;
} SuperType.prototype.getSuperValue=function(){
return this.property
} //第二个构造函数;目前有一个属性
function SubType(){
this.subproperty=false
} //继承了SuperType;SubType原型成了SuperType的实例;实际就是重写SubType的原型对象;给SuperType原型对象继承了
SubType.prototype=new SuperType() //现在这个构造函数有两个属性(一个本身的subproperty,一个继承的存在原型对象的property);两个方法(一个原型对象的getSubValue,一个原型对象的原型对象的getSuperValue)
SubType.prototype.getSubValue=function(){
return this.subproperty
} var instance=new SubType() //创建第二个构造函数的实例 console.log(instance.getSuperValue()) //true 先查找instance这个实例有没有此方法;显然没有,再查找SubType原型对象有没有此方法;也没有,再查找SubType原型对象的原型对象;显然是存在的

注意:instance的constructor现在指向的是SuperType这个构造函数;因为原来的SubType.prototype被重写了,其内部的constructor也就随着SubType.prototype的原型对象的constructor指向构造函数SuperType;至于原型搜索机制是怎么样运行的,请仔细看上面的代码,相信你是可以的

1.1完整的原型

在原型那节已经提了些,还是再说一下。完整的原型包括Object。

所有函数的默认原型都是Object的实例;每个默认原型都有个_proto_指针指向Object.prototype;因此自定义类型都继承如toString,valueOf的方法

而Object.prototype的_proto_指针指向null来结束原型链。以Person构造函数为例,看看完整的原型链图

1.2原型和实例的关系判断

第一种使用instanceof操作符:  测试实例和原型链中出现的构造函数,结果为true

第二种使用isPrototypeOf()方法: 只要是原型链中出现过的原型,都可以说是该原型链所派生的实例的原型

       console.log(instance instanceof Object)      //都为true
console.log(instance instanceof SuperType)
console.log(instance instanceof SubType) console.log(Object.prototype.isPrototypeOf(instance)) //都为true
console.log(SuperType.prototype.isPrototypeOf(instance))
console.log(SubType.prototype.isPrototypeOf(instance))

1.3谨慎定义方法

注意:给原型对象添加方法,一定放在替换原型的后面,因为放在替换原型之前是找不到了,原型会被重写的;

注意:在通过原型链继承时,不能使用对象字面量创建原型方法,因为也会重写原型链;

       function SuperType(){
this.property=true;
} SuperType.prototype.getSuperValue=function(){
return this.property
} function SubType(){
this.subproperty=false
} //继承SuperType
SubType.prototype=new SuperType() //使用字面量添加新方法,导致上一行无效 因为现在的原型替换了Object实例而非SuperType的实例,关系中断
SubType.prototype={
getSubValue:function(){
return this.subproperty;
},
somOtherMethod:function(){
return false
}
}; var instance=new SubType()
console.log(instance.getSuperValue()) //error

1.4原型链的问题

1、包含引用类型值的原型:当实例是另一函数的原型时,引用类型值就会变成原型上的属性,就会被另一函数的实例所共享。

       function SuperType(){
this.colors=["yellow","red","olive"]
} function SubType(){
} SubType.prototype=new SuperType() //color实际上就是原型上的了 var instance1=new SubType()
instance1.colors.push("purple")
var instance2=new SubType() console.log(instance1.colors==instance2.colors) //true

2、创建子类型实例时,不能向超类型的构造函数传递参数(没有办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数)

2、借助构造函数

为了解决原型中包含引用类型值带来的问题,利用构造函数来解决

在子类型构造函数的内部调用超类型构造函数(函数是特定环境中执行代码的对象,可以通过apply或call调用)

       function SuperType(){
this.color=["yellow","red","olive"]
} function SubType(){
//继承了SuperType
SuperType.call(this)
} var instance1=new SubType()
instance1.color.push("purple")
var instance2=new SubType() console.log(instance1.color) //["yellow","red","olive","purple"]
console.log(instance2.color) //["yellow","red","olive"] //传递参数
function SuperType(name){
this.name=name
}
function SubType(){
SuperType.call(this,"double")
this.age=12
} var instance1=new SubType()
console.log(instance1.name) //double
console.log(instance1.age) //

问题:仅仅借鉴构造函数,那么避免不了构造函数的问题,方法都在构造函数定义了,函数无法复用

3、组合继承(常用的还是组合,和原型与构造结合一样)

       function SuperType(name){
this.name=name;
this.color=["yellow","red","olive"];
} SuperType.prototype.sayName=function(){
console.log(this.name);
} function SubType(name,age){
//继承属性,创建属性副本
SuperType.call(this,name);
this.age=age;
} //继承属性和方法,只是原型中属性被后来的函数调用生成的属性副本遮盖
SubType.prototype=new SuperType(); alert(SubType.prototype.constructor) //指向的是SuperType SubType.prototype.constructor=SubType; //将constructor回归到SubType构造函数身上
SubType.prototype.sayAge=function(){
console.log(this.age)
} var instance1=new SubType("double",23)
instance1.color.push("pink")
console.log(instance1.color) //["yellow","red","olive","pink"]
instance1.sayName() //double
instance1.sayAge() // var instance2=new SubType("single",34)
console.log(instance2.color) //["yellow","red","olive"]
instance2.sayName() //single
instance2.sayAge() //

还有其他的继承,花点时间写一下

1、原型式继承

克罗克福德写的;借助原型可以基于已有的对象创建新对象,同时不必创建自定义类型

       function object(o){           //本质上object()函数对其中对象的浅复制
function F(){} //创建一个新的构造函数
F.prototype=o //构造函数原型为传入的对象
return new F() //返回构造函数的实例
} var person={
name:"double",
friends:["tom","jack","mike"]
} var person1=object(person) //事实上为原型共享
person1.name="grey"
person1.friends.push("single") console.log(person1.friends) //["tom", "jack", "mike", "single"] var person2=object(person)
person2.name="red"
console.log(person2.friends) //["tom", "jack", "mike", "single"]

ES5为了规范原型式的继承,有个Object.create()来方便,IE9以上可以;只是想一个对象和另一个对象保持类似的情况,完全可以这种方法

       var person={
name:"double",
friends:["tom","jack","mike"]
} var person1=Object.create(person)
person1.name="single"
person1.friends.push("singles") var person2=Object.create(person) console.log(person1.friends==person2.friends) //true //Object.create()接受两个参数,一个为作为新对象原型的对象,一个为新对象定义额外属性对象
var person={
name:"double",
friends:["tom","jack","mike"]
} var person1=Object.create(person,{
name:{
value:"single" //每个属性都是通过自己描述符定义的
}
})

2、寄生式继承

思路和原型式继承一脉相承,创建一个用于封装继承过程的函数,内部通过方式增强对象,返回对象;主要考虑对象时使用

function object(o){
function F(){}
F.prototype=o
return new F()
} function createPerson(original){
var clone=object(original) //继承原型
clone.sayName=function(){
alert("name")
}
return clone
} var person={
name:"double",
friends:["single","tom","jack"]
} var person1=createPerson(person)
person1.sayName() //name 引用类型值还是共享的

3、寄生组合继承

组合继承是继承中常常用到的,但是会调用两次超类型构造函数;寄生组合继承就是为了解决这个问题的

    function object(o){
function F(){}
F.prototype=o
return new F()
} function inheritPrototype(subType,superType){
var prototype=object(superType) //创建对象 (superType实例)
prototype.constructor=subType //增强对象
subType.prototype=prototype //指定对象 (原型赋予实例)
} function SuperType(name,sex){
this.name=name
this.sex=sex
this.colors=["red"]
} SuperType.prototype.sayName=function(){
alert(this.name)
} function SubType(name,sex,age){
SuperType.call(this,name,sex)
this.age=age
} inheritPrototype(SubType,SuperType) //目前subType.prototype什么都没有
SubType.prototype.sayAge=function(){ //为subType.prototype添加个方法
alert(this.age)
} var person1=new SubType("double","man",34)
console.log(person1.name) //SuperType 这是个Bug
console.log(person1.sex) //man
console.log(person1.colors) //["red"]
person1.sayAge() //

到此,差不多结束啦。

各位客官们,关注一波呗

js面向对象之继承那点事儿根本就不是事的更多相关文章

  1. 捋一捋js面向对象的继承问题

    说到面向对象这个破玩意,曾经一度我都处于很懵逼的状态,那么面向对象究竟是什么呢?其实说白了,所谓面向对象,就是基于类这个概念,来实现封装.继承和多态的一种编程思想罢了.今天我们就来说一下这其中继承的问 ...

  2. JS面向对象组件 -- 继承的其他方式(类式继承、原型继承)

    继承的其他形式: •类式继承:利用构造函数(类)继承的方式 •原型继承:借助原型来实现对象继承对象   类 : JS是没有类的概念的 , 把JS中的构造函数看做的类 要做属性和方法继承的时候,要分开继 ...

  3. JS——面向对象、继承

    创建对象的方式: 1)单体 <!DOCTYPE html> <html lang="en"> <head> <meta charset=& ...

  4. JS 面向对象之继承 -- 原型链

    ECMAScript只支持实现继承,其实现继承主要是靠原型链来实现. 原型链的基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法. 简单回顾下构造函数.原型和实例的关系: 每个构造函数都有 ...

  5. js面向对象之继承-原型继承

    //animal 父类 超类 var Animal = function(name) { this.name = name; this.sayhello = function() { alert(&q ...

  6. JS 面向对象之继承---多种组合继承

    1. 组合继承:又叫伪经典继承,是指将原型链和借用构造函数技术组合在一块的一种继承方式. 下面来看一个例子: function SuperType(name) { this.name = name; ...

  7. Js面向对象构造函数继承

    构造函数继承 <!-- 创建构造函数 --> function Animal(){ this.species= '动物'; } function Dog(name,color){ this ...

  8. JS面向对象(封装,继承)

    在六月份找工作中,被问的最多的问题就是: js面向对象,继承,封装,原型链这些,你了解多少? 额,,,我怎么回答呢, 只能说,了解一些,不多不少,哈哈哈哈,当然,这是玩笑话. 不过之前学过java,来 ...

  9. JS面向对象笔记二

    菜单导航,<JS面向对象笔记一>,  参考书籍:阮一峰之<JavaScript标准参考教程> 一.构造函数和new命令 二.this关键字 三.构造函数和new命令 四.构造函 ...

随机推荐

  1. Percona XtraBackup 核心文档

    1. 介绍 1.1 MySQL 备份工具特性对比 Features Percona XtraBackup MySQL Enterprise backup License GPL Proprietary ...

  2. windows第四层负载均衡--基于NLB负载均衡

    上面有一篇文章说windows第七层负载均衡,这次讲讲第四层负载均衡 TCP/IP协议族,第七层是应用层,第四层是传输层.第四层负载均衡主要通过IP进行转化. 一些优秀的第四层负载均衡软件,速度可以接 ...

  3. 学Java必看,不看的人都后悔了

    什么是Java? Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承.指针等概念,因此Java语言具有功能强大和简单易用两个特征.Java语言作为静态面向 ...

  4. 【原】IOS兼容性之APP内fixed定位头部跳动

    兼容现象: 在App的webview里边,我们有时候会在页面里写自定义头部,会使用到fixed定位,我们想要的效果是,页面无论怎么滑动,这个自定义的头部始终是固定在顶部的,但是在ios 11以上的版本 ...

  5. Git基础命令使用(个人总结)

    个人在开发中整理常用的git命令,相信很多人会需要到的. 全局配置信息: git config --global user.name "Your name" git config ...

  6. WPF: WpfWindowToolkit 一个窗口操作库的介绍

    在 XAML 应用的开发过程中,使用MVVM 框架能够极大地提高软件的可测试性.可维护性.MVVM的核心思想是关注点分离,使得业务逻辑从 View 中分离出来到 ViewModel 以及 Model ...

  7. cs231n spring 2017 lecture15 Efficient Methods and Hardware for Deep Learning 听课笔记

    1. 深度学习面临的问题: 1)模型越来越大,很难在移动端部署,也很难网络更新. 2)训练时间越来越长,限制了研究人员的产量. 3)耗能太多,硬件成本昂贵. 解决的方法:联合设计算法和硬件. 计算硬件 ...

  8. [bzoj1999]树网的核

    从下午坑到网上..noip的数据太弱,若干的地方写挂结果还随便过= = 最坑的就是网上有些题解没考虑周全... 第一步是找直径,用两次bfs(或者dfs,Linux下系统栈挺大的..)解决.找出其中一 ...

  9. ECharts 环形饼图 动态获取json数据

    ECharts  环形饼图 动态获取json数据 效果图如下: 一.html部分 <div id="secondPieChart" style="width:100 ...

  10. python列表三

    >>> list1 = [123]>>> list2 =[234]>>> list1 > list2False>>> li ...