• JavaScript 面向对象编程思想(二)


    一:面向对象编程思想

    1.单例模式

    在真实项目中,为了实现模块化开发或者团队协作开发,我们经常应用单例模式(一般业务逻辑部分的代码都是依托单例模式设计规划的)

    单例模式的由来:在很久以前,JS都是值类型,没有引用数据类型,如果后面编写的代码,创建的变量或者函数名和之前一样,会把之前存储的值替换掉;真实项目中,团队协作开发,如果是这样来处理,经常会导致相互代码的冲突:‘全局变量污染’  ‘全局变量冲突’

    后来JS中诞生了对象数据类型,解决了上面出现的污染或者冲突问题,把描述同一件事务的特征或者属性,进行归纳汇总(放在一起),以此来避免全局变量之前的冲突

    我们把对象数据类型实现 ‘把描述同一件事务的属性或者特征归纳汇总在一起,以此避免全局变量冲突问题’ 的方式和思想叫做:“单例设计模式”

    1) singleton不仅仅是对象名了,在单例模式中,singleton称之为“命名空间(nameSpace)”

    把描述同一件事务的属性或者方法存放在某一个命名空间下,多个命名空间中的属性和方法是互不干扰的

    2.使用单例模式实现模块化开发

    模块化开发:在团队协作开发的时候,我们经常会把一个复杂的页面 按照具体的功能划分为几大块,然后分别去开发,这种模块划分的思想就是模块化开发思想

    真实项目中,我们可以使用单例模式(建议也使用单例模式)来实现模块化开发

    在当前的命名空间下调取其它命名空间的方法,指定好对应的命名空间名字即可,使用[nameSpace].[property]就可以操作了;调取本模块中的一些方法可以直接使用this处理即可,此方法中的this一般都是当前模块的命名空间

    2.高级单例模式

    基于JS高阶编程技巧 “惰性思想”来实现的单例模式叫高级单例模式,并且可以把一些常用的设计模式(例如:命令模式,发布订阅设计模式,promise设计模式等)融合进来,最后清晰的规划我们业务逻辑代码,方便后期二次开发和维护

    这种设计思想综合体就是高级单例模式,也是项目中最常应用的

    var searchModel = (function(){
    
        function submit(){};
        function fn(){};
    
        return {
        init:function(){
            this.submit();
            this.fn()
        }
    }
    })()        
    //searchModel.submit()

    二:面向对象(OOP)

    面向对象编程思想(面向过程编程思想:c语言是面向过程的),Java,php,c#,c++,.net(dot.net),Js,python,Ruby...这些都是面向对象编程的

    HTML和CSS是标记语言不是编程语言,没有所谓的面向对象编程;Less和Sass预编译语言,旨在把css变为编程语言(面向对象)

    对象,类,实例

    对象:编程语言中的对象是一个泛指,万物皆对象(我们所要研究学习以及使用的都是对象)

    类:对象的具体细分(按照属性或者特性细分为一些类别)

    实例:某一类中具体的事物

    【实际生活中】

    自然界中万物皆对象,我们为了认知自然界,我们会把自然界中的事物按照特征进行分类,例如:动物类,植物类,微生物类;实例:狗就是动物类的一个实例

    JS本身就是基于面向对象创造出来的语言(所以它是面向对象编程),我们想要学习JS,首先也要给其进行分类,我们拿出某一类中的实例,进行学习和呀研究

    三:JS中常用的内置类

    1.关于数据类型的

      Number:每一个数字或者NaN是它的一个实例

      String:字符串类

      Boolean:布尔类

      Null

      Undefined:浏览器屏蔽了我们操作null或者undefined这个类

      Object:对象类,每一个对象数据类型都是它的实例 ①.Array:数组类 ②.RegExp:正则类 ③.Date:日期类

      Function:函数类,每一个函数都是它的一个实例

    2.关于元素对象和元素集合的

      HTMLCollection:元素集合类(getElementsByTagName/getElementsByClassName/querySelectorAll)

      NodeList:节点集合类(getElementsByName/childNodes)

      HTMLDivElement

      HTMLElement

      Element

      Node

      EventTarget

      Object

    3.目前阶段学习面向对象对于我们的意义

    1.例如研究数组

    1)创建一个数组类的实例,研究其基础语法和结构 

    2)如果想要研究数据具备的功能方法,我们只需要看Array/Object这些类上都赋予了它什么样的方法

    问题: document.getElementById它的上下文只能是document,其它不可以?

    解答:    因为getElementById这个方法只有Document这个类才有,其它的类没有,所以只有document这个作为Document的实例才能使用这个方法

    4.基于面向对象创建数据值

    var ary = [1,3,4] //字面量创建方式  -》不严谨

    var ary = new Array(); //严谨的基于面向对象(构造函数)方式创建一个数组

    //两种创建方式在核心意义上没有差别,都是创建Array这个类的一个实例,但是在语法上是有区别的

    1.字面量创建方式传递进来什么,都是给数组每一项加入的内容

    2.构造函数创建方式new Array(10) :创建一个长度为10的数组,数组中每一项都是空;  new Array('10'):如果只传递一个实参,并且实参不是数字,相当于把当前值作为数组的第一项存储进来;new Array(10,20,30):如果传递多个实参,不是设置长度,而是把传递的内容当做数组中的每一项存储起来

    通过字面量创建出来的是一个基本类型值,但是通过构造函数方式创建出来的是对象数据类型的

    5.构造函数设计模式(constructor)

    1.使用构造函数方式,主要是为了创建类和实例的,也就是基于面向对象编程思想来实现一些需求的处理

    在JS中,当我们使用new xxx()执行函数的时候,此时的函数就不是普通的函数了,而是变为一个类,返回的结果叫做当前类的实例,我们把new xxx()执行的方式称之为 ‘构造函数设计模式’

    function fn(){
    }
    var f = new fn()  //fn是一个类,f是当前这个类的一个实例 “构造函数设计模式”
    (我们一般会把类名第一个字母大写)

    2. 普通函数执行 VS  构造函数执行

    普通函数执行

    //1.开辟一个新的私有作用域
    //2.形参赋值
    //3.变量提升
    //4.代码自上而下执行(return后面的值就是当前函数返回的结果)
    //5.栈内存释放或者不释放问题
    function fn(num){
        var total = null
        total+=num
        return total 
    }
      var f = fn(10)   // f=10

    构造函数执行

    //1.首先和普通函数一样,也需要开辟一个新的私有作用域
    //2.在私有作用域中完成类似于普通函数的操作:形参赋值以及变量提升
    //3.在代码自上而下执行之前,构造函数有属于自己比较特殊的操作:浏览器会在当前的作用域中默认创建一个对象数据类型的值,并且会让当前函数中的执行主体(this)指向这个创建的这个对象
    //4.像普通函数一样,代码自上而下执行:this.xxx = xxx 这里操作都是在给创建的这个对象增加属性名和属性值
    //5.代码执行完成后,即使函数中没有写return,在构造函数模式中:浏览器会默认的把创建的对象返回到函数的外面
    function Fn(num){
    //在构造函数模式中,方法体中出现的this是当前类的一个实例(this.xx = xx 都是在给当前实例增加一些私有的属性)
    this.num = num } var f = new Fn(10) 构造函数执行,既具备普通函数执行的一面,也同时具有自己独有的一些操作 在构造函数执行期间,浏览器默认创建的对象(也就是函数体中的this)就是当前这个类的实例。浏览器会把默认创建的实例返回,所以我没说:new Fn()执行,Fn是一个类,返回的结果是Fn这个类的一个实例

    3.深入理解构造函数执行的步骤

    1)当构造函数或者类,执行的时候不需要传递任何的实参值,此时我们是否加小括号就不重要了(不传递实参的情况下,小括号可以省略

    2)构造函数执行,同时具备了普通函数的一面,也有自己特殊的一面,但是和实例相关的,只有自己特殊的一面才相关(也就是this.xxx = xxx才相当于给当前实例增加的私有属性),函数体中出现的私有变量,和实例都没有直接的关系

    不同的实例是不同的空间地址 是不相等的 ;通过类创建出来的每一个实例都是单独的个体(单独的堆内存空间),实例和实例之间是不相等并且独立且互不影响的(市面上部分开发把这种模式叫做单例模式,这种说法是错的,JS中的这种模式叫做构造函数设计模式)

    3)在构造函数体中,通过this.xxx给实例设置的属性都是当前实例的私有属性

    4)当构造函数体中,(默认返回的是实例:对象类型值)我们自己手动设置了return,return的是一个基本数据类型值,对最后返回的实例没有任何的影响,但是如果返回是引用数据类型的值,会把默认返回的实例替换掉

    4.检测数据类型的方式

    ①:instanceof 用来检测当前实例是否隶属于某个类 instanceof解决了typepf无法识别是数组还是正则的问题 [] instanceof Array  : true

    ②:typeof  typeof [] : Object 

    ③:constructor

    ④:Object.prototype.toString().apply('''')

    5.hasOwnProperty  VS in

    in:用来检测当前这个属性是否隶属于对象(不管对象是私有还是公有的属性,主要有返回的就是true)

    hasOwnProperty:用来检测当前这个属性是否是对象的私有属性(不仅是对象的属性,而且需要是私有的才可以)

    var obj = {name:'alhh',age:'18'}
    
    'name' in obj //true
    'hasOwnProperty' in obj // true  
    //hasOwnProperty 是Object 这个内置类中提供的属性方法,只有当前对象是Object的一个实例,就可以使用这个方法
    function Fn(){
    this.num =100
    }
    var f = new Fn()
    
    //this.xxx是给这个实例添加的私有属性
    f.hasOwnProperty('num') //true

    检测一个属性是否是当前对象的公有属性

    1.是对象的一个属性

    2.不是对象的私有属性

    方法如下:
    function
    hasPublicProperty(attr,obj){ return (attr in obj) && (obj.hasOwnProperty(attr) === false) } hasPublicProperty('hasOwnProperty',{xxx:'xxx'})

    6.JS中的对象和函数汇总

    对象数据类型的值:{}包起来的普通对象;[]数组; / ^$/正则;Math数学函数;一般类的实例都是对象数据类型的;函数的prototype属性;实例的 __proto__属性

    函数数据类型的值:普通函数;所有的类(内置类和自定义类)

    7.原型和原型链

    (1).所有的函数都天生自带一个属性:prototype(原型),它是一个对象数据类型的值,在当前prototype对象中,存储了类需要给其实例使用的公有的属性和方法

    (2).prototype这个对象,浏览器会默认为其开一个堆内存,在这个堆内存中天生自带一个属性:constructor(构造函数),这个属性存储的值就是当前函数本身

    (3).每一个类的实例(每一个对象)都天生自带一个属性:__proto__,属性值是当前对象所属类的原型(prototype)

    function Fn(name,age){
        this.name = name;
        this.age = age;
        this.say = function(){
        console.log('name:'+this.name+'age:'+this.age)
        }
    }
    
    Fn.prototype.say = function(){
      console.log('hello world')
    }
    
    Fn.prototype.eat = function(){
      console.log('吃东西')
    }
    var f1 = new Fn('alhh',18)
    var f2 = new Fn('yt',30)

    原型的作用:提供实例所需的公有的属性和方法 

    原型链:先找私有,私有没有找公有 ,公有没有 ,一直向上找,一直找到Object的基类为止  (类比作用域链)

    例如见图: 数组push有三种方法执行 ① :ary.push():ary首先通过原型链的查找机制,找到Array原型上的push方法,然后让push方法执行(执行push内置方法实现向数组末尾追加新内容),这里面push的方法this是ary

      ②:ary.__proto__.push():this相对于第一种来说改变了this,这里的this是ary.__proto__

    ③:Array.prototype.push() 也是让push方法执行

    例2:ary.hasOwnProperty():通过原型链找到Object基类原型上的hasOwnProperty这个方法,并且执行这个方法

    某一个实例或者某一个对象之所以能操作某些属性和方法,是因为在它的原型链上能找到这些属性和方法,如果找不到则不能进行操作

     数组和类数组的区别 :两者结构一样,数组是Array的实例 但是类数组是Object实例,所以类数组不能用数组的那些方法

     把类数组转化为数组 Array.prototype.slice.call(arguments)

    (1):私有和公有是一个相对论,我们需要看相对于哪个对象而言:

    1.相对于实例来说,push是公有的

    2.相对于Array.prototype来说,push就是自己私有的

    凡是通过__proto__找到的属性都是公有的,反之都是私有的

    (2):关于原型链中提供的私有(公有)方法中的this指向问题:

      1.看点前面是谁,this就是谁

      f1.__proto__.say():this->f1.__proto__   ;Fn.prototype.say():this->Fn.prototype

      2.把需要执行方法中的this进行替换

      3.替换完成后,如果想知道结果,只需要按照原型链的查找机制去查找即可

    (3):在原型上批量扩展属性和方法

    1.设置别名(小名)

    var pro  = Fn.prototype; //指向同一个堆内存

    pro.aa = function(){}

    2.重新构造原型

    新的:Fn.prototype.cc = function(){}

    旧的:Fn.prototype = {

     //=>让原型指向自己开辟的堆内存有个问题:自己开辟的堆内存中没有constructor这个属性,所以实例在调用constructor的时候找到是Object,这样不好,此时我们应该重新设置一下constructor,保证机制的完整性

     constructor:Fn,  //此举解决上述问题

     aa:function(){},

     bb:function(){}

    }

    var f = new Fn()

    f.cc //undefined :重新做原型指向后,之前在浏览器默认开辟的堆内存中存储的属性和方法都没用了(之前开辟的cc,后来aa,bb又重新开辟了,所以cc没用了),只有在新内存中存储的才有用的(就是此时的aa),

    Array.prototype.aa = 11 //这样可以

    Array.prototype = { } //内置类原型不允许我们进行重构 ,这样是不行的 ,原有的会没有的

    //jQuery源码片段
    (function(){
        var jQuery  = function(selector,context){
        return new jQuery.fn.init(selector,context)
    }
        jQuery.fn = jQuery.prototype = {
            constructor:jQuery,
            init:function(selector,context){
        }
      } 
        window.jQuery = window.$ = jQuery;
        
    }()

    3.基于内置类的原型扩展方法

    基本方法数组去重

    function distinct(ary){
    var obj = {}
    for(var i=0;i<ary.length;i++){
        var item = ary[i]
        if(typeof obj[item] !=='undefined'){
        arr[i] = ary[ary.lenth-1];
        i--;
        continue;
        }
        obj[item] = item
      }
        obj =null
        return ary
    }

    (1).新增加的方法最后设置一个前缀,防止我们新增的方法和内置的方法冲突,把内置方法替换了

    Array.prototype.myDistinct = function myDistinct(){
    //this:ary当前要处理的那个数组
    var obj = {}
    for(var i=0;i<this.length;i++){
        var item = ary[i]
        if(typeof obj[item] !=='undefined'){
        this[i] = this[this.lenth-1];
        i--;
        continue;
       }
        obj[item] = item
      }
        obj =null
      return this //加return可以实现链式写法,返回去重后的数组,这样执行完成这个方法后,我们还可以继续调取数组中的其它方法 } ary.myDistinct()
    //不用return,这里直接修改this的值也就是修改ary的值
    //链式写法:执行完成一个方法紧跟着就调取下一个方法(原因:执行完成一个方法后,返回的结果依然是当前类的实例,这样就可以继续调取当前类的其它方法操作了)
    ary.sort(function(a,b){
        return a-b
        }
    ).push(100)
    
    ary.sort(function(a,b){
        return a-b
        }
    ).push(100).pop() //就会报错 因为push之后返回的是数组的长度是个数字,pop是数组的实例

    (2).面试题 (3).plus(2).minus(1) =>4

    Number.prototype.plus = function plus(){
    //this是3
      return this+arguments[0] 
    }
    Number.prototype.minus = function minus(){
    return this-arguments[0] 
    }

    !!!

    继承的方式

    1. 原型继承

    //原型继承是js中最常用的一种继承方式
    function A(){
        this.x = 100
    }
    A.prototype.getX = funtion (){
        console.log(this.x)
    }
    function B(){
        this.y = 1
    }
    B.prototype = new A

    总结:IE浏览器屏蔽了我们使用或者修改__proto__  ;所有的类都是Object的实例,Object是对象类型的基类,在它的原型上没有__proto__这个属性:因为即使有,也是指向自身

  • 相关阅读:
    消除ie上的:为了有利于保护安全性,IE已限制此网页运行可以访问计算机的脚本或 ActiveX 控件
    JS代码放在head和body中的区别
    iis与apache共用80端口方法集
    如何制作wordpress模板,WordPress模板技术手册
    zt 给博墅加个clocklink时钟
    MSSQL数据库:存储过程实例学习(1)从两个表中取出头两行,然后合并到一个表中
    ecmall 用户后台店铺条幅 在firefox7 以上无法预览的问题
    .NET MVC 实现动态换版
    一道嚼烂的面试题
    [1].gSOAP简介
  • 原文地址:https://www.cnblogs.com/alhh/p/10273546.html
Copyright © 2020-2023  润新知