JS_core_2.JavaScript OOP原型与继承

1 面向对象OOP

1.1 面向对象

对象:程序描述现实中一个具体事物的属性和功能的程序结构。事物的属性会成为对象的属性,事物的功能会成为对象的方法。
面向对象编程: 程序都是先将数据和功能定义在对象中,再按需使用对象的数据和功能
优点:便于大量数据和功能的维护和使用
特点:封装、继承、多态

1.2 封装

封装:创建对象集中保存一个事物的属性和功能,便于大量数据和功能的管理和使用
面向对象编程都要先封装对象,再按需使用对象的属性和功能

  1. 用{}直接量:

      var 对象名={
        属性名:属性值,
            ... : ... ,
        方法名:function(){
          ... this.成员 ...
        }
      }
    

    对象的方法要访问对象自己的属性或方法,无法直接使用,这是因为所有对象的属性和方法默认都不在作用域链当中,任何程序无权自动进入对象中获取成员(没有加任何前缀的普通变量只能在当前作用域查找)
    解决方法:不能直接在方法中使用对象名.成员名(紧耦合,每次更改需要同时改变函数内部的变量);只要对象自己的方法,要使用自己的成员,都要加this.属性名
    this指定对象自动获得正在调用方法的.前的对象名,只与调用方法时的前缀有关,而与定义方法时的前缀无关

  2. 用new创建

     //创建空对象
     var obj=new Object();
     //添加新成员
     obj.属性名=值;
     obj.方法名=function(){};
    

    本质:js中一切对象底层都是关联数组
    js中对象的特点:

    1. 对象创建完成后,依然可继续添加新成员
    2. 访问对象中不存在的属性,不报错!而是返回undefined
    3. 访问对象的成员,既可用.,又可用[“成员名”]
      选择: 如果成员名是变化的,就必须用[变量]
      如果成员名固定,就可用.简化与[""]等效

    缺点: 一次只能创建一个对象;如果反复创建多个相同结构的对象时,代码重复很多

  3. 用构造函数创建统一类型的多个对象:
    构造函数: 描述一类对象统一结构的函数,重用创建对象的代码!

    1. 定义构造函数:

         function 类型名(属性参数...){
         		  this.属性名=参数; 
         		  this.xxx = 值;
         		  this.方法名=function(){
         		    ...this.属性名 ...
         		  }
          }
      
    2. 调用构造函数反复创建多个对象:
      var obj=new 类型名(属性值,…)
      new:

      1. 创建一个新的空对象:var obj = {};
      2. 让子对象自动继承构造函数的原型对象obj.__proto__ = Base.prototype;
      3. 调用构造函数: Base.call(obj);
        通过强行赋值的方式,为新对象obj添加规定的新属性,保存属性值
      4. 返回新对象地址

    构造函数中的方法定义,会在每个子对象中重复创建函数对象,浪费内存!

1.3 继承

从其他对象获得属性和方法,父对象的成员——子对象无需重复创建,就可直接使用(代码重用,节约内存);
只要多个子对象需要相同的属性值和方法就使用原型对象(集中保存子对象共有成员的父对象)继承
通过原型实现继承:Constructor.prototype

1.3.1 原型对象

原型对象:不用创建,定义构造函数时,自动生成的构造函数.prototype
继承: new的第2步,子对象自动继承构造函数的原型对象(子对象.__ proto__=构造函数.prototype)

向原型对象中添加共有成员:
构造函数.prototype.成员=值/function(){…}

自有属性(constructor,通过对象引用添加)和共有属性(prototype原型对象继承而来): 原型对象中的属性和对象自己的属性是有区别的,对象自己的属性是可配置(用子对象修改)可枚举(遍历)的,而我们在原型中添加的属性是可枚举但不可配置(只能用原型对象修改)的;

内置对象的构造函数和原型对象:
内置对象: ES标准中规定的,浏览器已经定义好的可直接使用的对象,包括11个
String Number Boolean——包装类型
Array RegExp Date Math
Error
Function Object
global(window)
除了Math和global外,其余都是构造函数,包含2部分组成: 构造函数——创建该类型的子对象;原型对象—— 保存该类型的所有子对象共有的成员
变量先在构造函数中找,再从原型对象中找,所以自有属性会覆盖原型对象中共有属性的定义

1.3.2 原型链

原型链prototype chain,由多级父对象逐级继承形成的链式结构,包含了所有对象的属性和方法,控制属性和方法的使用顺序以及共享范围。
原型对象中的__proto__属性默认指向Object.prototype;Object处于原型链顶级,其原型对象__proto__的值为null

1.3.3 判断引用对象类型

  1. 判断原型对象:
    判断Array.prototype是不是obj的父级原型对象
    var bool=Array.prototype.isPrototypeOf(obj)

  2. 判断构造函数:
    obj.constructor === Array
    var bool = obj instanceof Array
    检查整个原型链

  3. 检测对象是否具备指定自有属性
    obj.hasOwnProperty(‘name’)

1.4 多态

以多种不同的形式运行函数或方法

  1. 重载overload
    相同函数名,不同参数列表的多个函数,可根据传入参数的不同自动选择对应函数调用。
    减少API数量以减轻调用者的负担
    js不允许重载,因为不允许同名函数同时存在,可以通过判断arguments.length统计参数个数来选择执行

    arguments是类数组对象(可以使用下标、length、遍历,但不能使用数组API),是函数自动创建的接收所有传入参数的对象。
  2. 重写override
    子对象中定义和父对象API同名成员

2 补充

2.1 静态方法static

静态方法:无需创建子对象,可以使用构造函数直接调用的方法(部分方法不确定使用的对象类型)
在函数外部使用构造函数名.静态方法名=function(){}定义,通过构造函数名.静态方法名()调用。
实例成员,只能由实例化的对象来访问;静态成员,只能由构造函数本身来访问

2.2 自定义继承

  1. 仅修改一个实例对象的父对象
    child.__ proto__=father.prototype
    Object.setPrototypeOf(child,father)
  2. 修改所有子对象的父对象
    构造函数.prototype=new Father实质是修改构造函数的prototype属性,必须在创建子对象之前
  3. 两种类型之间的继承
    当多个类型之间拥有部分相同的属性结构和方法定义
    1. 定义抽象父类型
      在构造函数中定义相同的部分属性;
      在原型对象中定义相同的方法
    2. 子类型继承父类型
      子类型原型对象继承父类型原型对象:
      child.prototype.__ proto__=parent.prototype
      子类型构造函数借用父类型构造函数
      在子类型构造函数中调用父类型函数:parent.call(正确的对象this,参数,…)
      不使用call调用会导致父类型构造函数的所有属性泄漏到全局,因为不使用new和.调用的函数this默认指向window

对基本继承理解的代码
使用child.prototype.__proto__=father.prototype只继承原型对象,无法使用父对象的实例;而使用child.prototype=new father(),子对象继承父对象的完整实例

<!-- 1.创建对象 -->
<!-- 2.构造函数 -->
<!-- 3.构造函数+原型 -->
<!-- <script>
    function Duck(name, word){
        this.name=name;
        this.word=word;
    }
    Duck.prototype.say=function(){
        console.log(this.name+" say: "+this.word);
    }
    var duck=new Duck("jerry","Hi");
    duck.say();
</script> -->

<!-- 4.寄生构造 -->
<!-- <script>
    function Duck1(name,word){
        var obj=new Object();
        obj.name=name;
        obj.word=word;
        obj.say=function(){
            console.log(this.name+" say: "+this.word);
        }
        return obj;
    }
    var duck1=new Duck1("jecy","you");
    duck1.say();
</script> -->

<!-- 原型继承 -->
<script>
    var Fn = function() {
        this.property = true;
    }
    Fn.prototype.getFnProperty = function() {
        console.log( this.property );
    };
    var SubFn = function() {
        // this.property=2;
        this.subProperty = false;
    };
    //SubFn继承了Fn的实例
    // SubFn.prototype = new Fn();
    SubFn.prototype.__proto__=Fn.prototype;
    //为实例添加额外的实例方法;
    SubFn.prototype.getSubProperty = function(){
        console.log(this.subProperty);
    };
    var subFn = new SubFn();
    subFn.getFnProperty(); //输出了true
    subFn.getSubProperty(); //输出了false
    SubFn.prototype.constructor = SubFn;
    console.log(subFn.subProperty,subFn.property);
    /*现在subFn的constructor 是
        function () {
        this.property = true;
        };
        所以要修正SubFn.prototype.constructor = SubFn
        */
</script>

<!-- 对象继承Object.create() -->
<!-- <script> 
        var Fn = function() {
            this.property = true;
        }
        Fn.prototype.getFnProperty = function() {
            console.log( this.property );
        };
        var SubFn = function() {
            this.subProperty = false;
        };
        //SubFn继承了Fn的实例
        SubFn.prototype = new Fn();
        //为实例添加额外的实例方法;
        SubFn.prototype.getSubProperty = function(){
            console.log(this.subProperty);
        };
        var subFn = new SubFn();
        subFn.getFnProperty(); //输出了true
        subFn.getSubProperty(); //输出了false
        SubFn.prototype.constructor = SubFn;
        console.log(subFn.subProperty,subFn.property);
        /*现在subFn的constructor 是
            function () {
            this.property = true;
            };
            所以要修正SubFn.prototype.constructor = SubFn
            */

        var newSubFn=Object.create(subFn, {
            getFnProperty:{
                value:1
            }
        })
        console.log(newSubFn.getFnProperty);
</script> -->

<!-- class -->
<script>
    class A {
        constructor(a,b){
            this.a=a;
            this.b=b;
        }
        sum(){
            return this.a+this.b;
        }
        static len(){
            return "000";
        }
    }
    // var err=new A(1,2);
    // console.log(err.sum(),A.len());

    class B extends A{
        constructor(a,b,c){
            super(a,b);
            this.c=c;
        }
        sumAll(){
            return this.a+this.b+this.c;
        }
    }
    var err=new B(1,2,3);
    console.log(err.sum(),err.sumAll());
</script> 

链接: js继承和继承基础
https://www.cnblogs.com/diligenceday/p/4246515.html

猜你喜欢

转载自blog.csdn.net/qq_33392141/article/details/85718059