JavaScript继承和封装

2019-05-15  本文已影响0人  JJesson

Javascript继承


第2章 写的都是看到的---面向对象编程

2.1 两种编程风格 ----面向过程与面向对象

面向过程:直接实现功能,面向结果和过程,参数为实现特定功能的必须参数

面向对象:将对象进行特征分析,将方法和方法进行封装,主要思想有:封装,继承,多态(但js里考虑的比较少),并且类Class的概念在ES6的时候才出现,之前都是通过一些js的特性进行相关概念的模拟

2.2 包装明星 ---- 封装

2.2.1 创建一个类

在ES6之前创建一个类:首先声明一个函数保存在一个对象里,然后按照编程习惯这个代表类的变量名首字母大写,然后再这个函数的内部通过this(函数内部自带的一个变量,用于指向当前这个对象)变量,添加属性或者方法来实现对类属性或方法的添加

给类添加属性的时候有两种方法,一种是直接用this指向进行属性的赋值,一种是通过prototype属性进行赋值,这两种方式的区别:

使用this进行属性添加的时候所有的实例化对象都会创建并且具有这个属性的实际内容

但是使用prototype属性进行属性添加,其实所有的实例化对象本身是不包含该属性的,只是将该属性添加到该对象的原型属性上,但是由于js的原型链的原理,所有的原型对象也可以调用和使用该属性

实例化对象的时候可以通过new 关键字进行创建,具体的创建流程是这样 ,先通过new 关键字创建一个空对象,然后将函数中的this 指向该空对象,然后再将所有的this的属性和方法复制到该空对象,实现实例化过程

contructor: 构造函数 当创建一个函数或者对象时,都会为其创建一个原型对象,prototype ,在prototype对象中又会像函数中创建this一样创建一个constructor属性,这个constructor属性就是拥有整个原型对象的函数和对象,指向构造函数本身,例如Book prototype中的constructor就是指向Book类对象

2.2.2 这些都是我的 ---- 属性与方法的封装

私有属性:对象的属性不能被访问者看到;只能在函数内部使用。好处就是安全,就类似闭包中的函数一样,减少污染

公有属性(共有属性):当我们定义一个对象后,使用对象的人在实例化之后可以访问到对象内部的属性;

私有方法:对象的方法不能被访问者看到;只能在函数内部使用。好处就是安全,就类似闭包中的函数一样,减少污染

构造方法: 通过方法修改实例中属性或者私有属性的方法

实现方法: 由于Js中定义的变量都只会存在最近的函数作用域或者最近的全局作用域,所以可以通过函数包裹作用域,实现方法和属性的私有化

例如:

    var Book = function(id,name,price){
        var num  = 1 ;//定义私有属性,var定义的变量在最近的函数左右用
        funciton CheckId(){
            //跟用var声明变量是一样的,其实也是创建了一个变量,只是用函数进行赋值,也是一个私有方法
        }
        this.getName = function(){
            //this 会将该方法传递到所有的实例对象中,所以是一个共有方法
        }
        this.setNum = function(number){
            num = number // 通过函数内部的一个函数操作私有变量,这种方法叫做构造器
        }
    }
    //类静态公有属性(实例获取不到该属性,想获取该属性只能通过类调用)
    Book.isChenese = true ; 
    //类静态公有函数(公有方法)
    Book.resetTime = function(){

    }
    //
    Book.prototype = {
        //公有属性  因为写在prototype所有的实例都可以调用和获取到该属性
        isJsBook  = true
        //同理,这样定义的方法为公有方法
        display:function(){

        }
    }

你们看不到我 ---- 闭包实现

经常将类的静态变量通过闭包来实现

实现原理:闭包是有权访问另一个函数作用域中变量的函数,即在一个函数内部创建另一个函数,我们将这个闭包作为创建对象的构造函数

这样他既是闭包又是可实例化对象的函数,即可以访问到类函数作用域中的变量

例如:

    //一个立即执行函数,返回值是一个构造函数
    var Book = (function(){
        //立即执行函数中间创建了变量,该变量实例对象无法修改,也只能通过闭包方法获取,所以该变量叫  静态私有变量
        var bookNum = 0;
        //静态私有函数
        function checkBook(name){}
        //创建类的构造方法
        function _book(newId,newName,newPrice){
            //类里面的正常的私有变量
            var name,price;
            //私有方法
            function checkId(id){

            }
            //公有方法
            this.getName = function(){}
            this.id = newId
            this.copy = function(){}
        }
        //原型方法
        _book.prototype = {
            //原型静态方法
            isJsbook = true
            display:function(){}
        }
        return _book
    })()

2.2.4 找位检查长 ---- 创建对象的安全模式

由于怕在创建对象的时,忘记使用new关键字,则会返回一个undefined对象

解决原理:如果是不适用关键字调用,则会当成一个普通函数进行构造函数的调用,this指向的是掉用他的函数上下文,也就是一般都是window或者什么,所以可以在赋值之前,判断这个this是否为构造函数的实例,如果是,则表示是通过new 语法创建,如果不是,则表示出错

解决代码:

    var Book = function(){
        //赋值之前判断this是否为类的实例对象
        if(this instanceof Book){
            //如果是的话直接进行赋值
            this.title = title;
        }
        //如果不是的话,则重新实例化一个对象
        else{
            return new Book()
        }
    }

2.3传宗接代 --- 继承

js实现继承的几种方式

function SuperClass(){
    this.SuperObj = {}
}
SuperClass.prototype.SuperMethod = function(){}

function SubClass(){
    
}
SubClass.prototype.SubMethod = function(){
    
}

SubClass.prototype = new SuperClass()
//将子类的prototype属性设置为父类的实例

//实例化方法
var Sub = new SubClass()

缺点:1:由于子类实现的继承是靠其原型prototype对父类的实例化实现的,因此在创建父类的时候,是无法想父类传递参数的
    2:由于子类通过其原型prototype对父类实例化继承了父类,所以说父类中的共有属性要是引用类型,就会在子类中呗所有实例公用
    ★3:由于子类的构造函数是直接在window下创建的,所以子类的构造函数并不继承自父类的构造函数 SubClass instanceof SuperClass === false
 function SuperClass(){
    this.SuperObj = {}
}
SuperClass.prototype.SuperMethod = function(){}

function SubClass(opt){
   SuperClass.call(this,opt)
   //继承父类
}

//实例化方法
var Sub = new SubClass()
call方法可以改变函数的执行上下文,因此在子类中调用父类的构造函数,就相当于以子类的this调用了父类的构造函数,但是最后会返回到子类的创建的对象中,实现了子类调用父类构造函数的操作,叫做构造函数继承
由于这种类型的继承没有设计原型prototype,所以父类的原型方法自然不会子类继承★ SubperClass.prototype = xxx无法被继承
而如果想要被子类继承就必须要放在构造函数中,这样创建出来的每个实例都会单独拥有一份而不会公用,造成资源浪费
 function SuperClass(){
    this.SuperObj = {}
}
SuperClass.prototype.SuperMethod = function(){}

function SubClass(opt){
   SuperClass.call(this,opt)
   //继承父类
}

SubClass.prototype = new SuperClass()
//在构造函数继承中再次把原型属性赋值到子类的原型属性上

//实例化方法
var Sub = new SubClass()

既不会实例属性影响类属性的引用特性,同时在子类构造函数中执行父类的构造函数能都传递参数,看起来两全其美近乎完美
但是实际上还是有缺点:
★ 在构造函数继承时执行了一遍父类的构造函数,而在实现子类原型的类式继承时又调用了一边父类构造函数,因此父类构造函数执行了两遍

直接上代码

function inheritObject(o){
    function F(){}
    F.prototype = o
    return new F()
}

//测试用例

var book = {
    name:'js book',
    alikeBook:["css","htmlbook"]
}
var newBook = inheritObject(book)
//实现继承

newBook.name = 'new book'
//修改子类属性
缺点:实际上就是对类式继承的一个封装,实际上还是原型的引用,所以还是有类式继承的缺点
function inheritObject(o){
    function F(){}
    F.prototype = o
    return new F()
}
function inheritPrototype(subClass,superClass){
    var p = inheritObject(superClass.prototype)
    //复制一份原型副本保存在变量中
    p.constructor = subClass
    //修正因为重写子类原型导致子类的construtor属性被修改
    subClass.prototype = p;
}
原理:直接将父类的原型复制到子类,但是同时将父类的原型中的构造函数改成子类的构造函数,变为一个子类的对象实现继承,一般和其他的构造函数式继承混合使用

2.4 多继承

多继承:一个子类同时拥有两个父类或者说一个派生类同时有两个基类,这样的关系称作多继承

但是在JavaScript中继承是依赖于原型链实现的,只有一个原型链,所以理论上是不能继承多个父类的,但是JavaScript是灵活的,可以将两个父类的所有原型属性合并,即实现了多继承

实现:遍历两个对象的所有属性,将不重复的属性进行合并复制,但是这样是浅拷贝,如果要执行深拷贝的话需要对所有引用类型继续调用mix方法

2.5 多种调用方式 ---多态

多态:在面向对象编程中就是同一个方法的多种调用方式,在js中也是可以实现的,只是要对传入的参数做判断以实现多种调用方式

实现: 调用方法时对参数进行判定,执行不同的代码块

                                                                                                          修改时间:2019/05/15
上一篇下一篇

猜你喜欢

热点阅读