• js之构造函数、原型模式


    单例模式

    把描述同一事物的属性放进同一个空间地址下,避免了全局变量的干扰,这种开发的模式就是单例模式。

    var singleton1={
       fn:function{
       }
    }
    var singleton2={
       fn:function{
       }
    }
    singleton1.fn();
    singleton2.fn();
    

    高级单例模式

    采用了自执行函数闭包的作用,保护里面的私有变量不受外界的干扰;同时,如果在闭包外面使用里面的函数时,可以把其return作为返回值。

      var niu = (function () {
           var  num = 1;
           var  niuCode = {
               fn:function () {
                   console.log(num);
               },
               sum:function () {
                   console.log(num);
               }
           }
           return niuCode;//niuCode只是一个空间地址,所以定义一个niu来接收它。
       })();
        box.onclick = niu.fn;
    

    工厂模式

    把实现同一功能的代码放到一个函数中,当想实现这个功能时,执行这个函数即可;减少了代码的冗余,“高内聚,低耦合”,实现了函数的封装。

      function createPerson(name,age) {
            var obj ={};// 创建一个对象
            obj.name = name;// name获取形参的值
            obj.age = age;
            return obj;//return 出一个对象
        }
        // p1接收了createPerson 的返回值;
        var p1 = createPerson("张三",12);
        var p2 = createPerson("王五",30);
    

    构造函数

    new:操作符,在函数执行前加new

    1. 形成一个私有作用域之后,代码执行之前,会默认创建一个空的对象(堆内存);
    2. 并且改变了当前作用域下的this指向,让其指向那个空对象;
    3. 当代码运行完,把这个对象返回,这是浏览器默认返回的。

    :内置类和自定义类;所有的类都是函数;
    实例:通过构造函数或者类new出来的都是一个实例。

    普通函数和构造函数区别

    • 普通函数 形成私有作用域-->形参赋值-->变量提升-->代码从上到下执行-->作用域是否销毁
    • 构造函数 形成私有作用域-->形参赋值-->变量提升-->创建对象,让this指向这个对象-->代码从上到下执行-->return这个对象-->作用域是否销毁

    构造函数的特征

    1. 构造函数中的this永远指向实例
    2. 构造函数的函数名一般都是大写,为了区分普通的函数。
    3. 如果构造函数,并且不需要参数的情况下,可以省略小括号。
    4. 如果return一个基本数据类型值,不能覆盖内置的(即默认的返回的对象或者实例);如果return一个引用的数据类型值,会覆盖默认的return的对象。
    5. instanceof:检测当前实例是否属于这个的方法,属于返回true,不属于返回false。
    6. hasOwnProperty:检测当前属性是否是私有属性,如果是返回true;不是返回false
      实例.hasOwnProperty(属性)
    //封装一个检测共有属性的方法
    function hasPublicProperty(obj,attr){
    if(obj[attr}){//由于传入的参数是字符串,所以不能用obj.attr,可以用obj[attr]或者attr in obj;
                   if(obj.hasOwnProperty(attr)===false){
             return true;
          }else{
             return false;
          }
          else{
             return false;
           }
       }
    }
    hasPublicProperty(obj,"tostring")
    
    //优化以后
    function hasPublicProperty(obj,attr){
           return attr in obj && !obj.hasOwnProperty(attr)?true:false;
    }
    hasPublicProperty(obj,"tostring")
    

    原型模式

    1. 所有的函数数据类型都天生自带一个属性:prototype(原型),这个属性的值是一个对象:存储了当前类供实例调取使用的共有属性和方法。浏览器会默认给它开辟一个堆内存。
    2. 在浏览器默认给prototype开辟的堆内存中,即prototype属性中有一个天生自带的属性:constructor,这个属性存储的值是当前原型所属的类(当前函数本身)。
    3. 所有的对象数据类型都天生自带一个__proto__的属性,这个属性的属性值指向当前实例所属类的prototype(如果不能确定它是谁的实例,都是object的实例,即object.prototype)
    4. 所有的函数,包括普通函数、类(内置类、自定义类)都是Function的一个实例。
      (理解这四句话,基本上原型模式就算理解了)

    原型链

    它是一种基于__proto__向上查找的机制。当我们操作实例的某个属性或者方法的时候,首先找自己空间中私有的属性或者方法

    1. 找到了,则结束查找,使用自己私有的即可
    2. 没有找到的话,则基于__proto__找所属类的prototype,如果找到,就用这个共有的,如果没找到,基于原型上的__proto__继续向上查找,一直找到Object.prototype的原型为止,如果再没有,操作的属性或者方法不存,得到undefined。

    这样通过__proto__向上查找就会形成一个原型链。

    原型重定向

    在实际项目基于面向对象开发的时候(构造原型设计模式),我们根据需要,很多时候会重定向类的原型(让类的原型指向自己开辟的堆内存)
    当我们需要给类的原型批量设置属性和方法的时候,一般都是让原型重定向到自己创建的对象中

    Fn.prototype={
       aa:function()
    }
    

    存在的问题:

    1. 自己开辟的堆内存中没有consturctor属性,导致类的原型构造函数缺失。

    解决:手动在堆内存增加constructor属性

    1. 当原型重定向后,浏览器默认开辟的那个原型堆内存会被释放掉,如果之前已经存储了一些方法和属性,这些东西都会丢失。(所以:内置类的原型不允许重定向到自己开辟的堆内存,因为内置类原型上自带很多属性和方法,重定向后都没了,这样是不被允许的)
    2. 内置类:内置类扩展时,如果和内置的方法名相同,会对其进行覆盖。但是内置类原型的空间地址不可以被修改,只能向其中新增一些方法。所以类.prototype={}只适用于自定义类,不能用于内置类,比如Array等这些内置类。

    零碎知识点

    私有属性和公有属性

    • 私有属性:自己堆内存中存储的属性相对自己来说是私有的
    • 公有属性:自己基于__proto__找到的属性,想对自己来说是公有的

    基于内置类的原型扩展方法,供它的实例调取使用

    //实现数组的去重
    Array.prototype.myUnique=function myUnique(){//后边函数的名字可加可不加
    //方法中的this一般都是当前类的实例(也就是我们要操作的数组)
       var obj={};
       for(var i=0;i<this.length;i++){
           var item=this[i];
           obj.hasOwnProperty(item)? (this[i]=this[this.length-1],this.length--,i--):obj[item]=item;
           }
        obj=null;//释放堆内存
        return this;   
    };
    ary.myUnique();
    //链式写法求数组最大值
    var max=ary.myUnique().sort(function(a,b){return a-b}).pop();
    
    1. 我们增加的方法最好设置“my”前缀(前缀自己定),防止把内置方法重写

    JS的链式写法
    保证每一个方法执行返回的结果依然是当前类的实例,这样就可以继续调取方法使用了

    ary.sort(function(a,b){return a-b}).reverse().pop();
    //执行完返回的结果是删除的那一项的值,所在再调用数组方法会报错,也可以接着使用其它内置类方法
    

    创建变量之字面量创建和实例创建

    var a=1;
    var a1=new Number(1);
    
    • 通过字面量方式创建的实例(基本数据类型的实例)不是一个标准的实例,不能使用instanceof进行检测
    • 通过new创建的实例不能通过typeof进行数据类型的检测,都返回"object"

    可枚举属性和不可枚举属性

    用for...in...遍历会输出可枚举的属性

    • 可枚举属性:私有的属性和自定义的共有属性。
    • 不可枚举属性:是公有属性并且内置的属性。

    call、apply、bind

    call

    [Fn].call([this],[param]...)
    fn.call:当前实例(函数Fn)通过原型链的查找机制,找到Function.prototype上的call方法
    fn.call():把找到的call方法执行

    当call执行的时候,内部处理了一些事情

    1. 首先把要操作函数中的this关键字变为call方法第一个传递的参数值
    2. 把call方法第二个及第二个以后的实参获取到
    3. 把要操作的函数执行,并且把第二个以后的传递进来的实参传给函数

    call中的细节(适用于apply和bind)

    1. 非严格模式下,如果参数不传,或者第一个参数是null、undefined,this都指向window;
    2. 严格模式下,第一个参数是谁,this就指向谁(包括null、undefined),不传this是undefined。

    apply

    apply:和call基本上一模一样,唯一区别在于传参的方式。apply把需要传递给FN的参数放到一个数组(或者类数组)中传递进去,虽然写的是一个数组,但是也相当于FN接受的是数组的每一项,而不是整个数组。

    bind

    bind:预处理this;提前改变this指向,但是FN不立即执行;bind的传参和call方式传参一致,需要一个个向里传。

    fn.call(obj,10,20); //改变fn中的this,并且把fn立即执行
    fn.bind(obj,10,20); //改变fn中的this,此时的fn并没有执行(不兼容IE6~8)
    
  • 相关阅读:
    MATLAB01
    Diffie-Hellman 密钥交换
    古典密码
    正则表达式
    装饰器初析
    进制转换的栈实现
    Log4j(异常日志)
    2018/6/6
    2018.1.1T19B3-u4
    2018.1.1T19-B3-U3jiangyi
  • 原文地址:https://www.cnblogs.com/wangxingren/p/10223994.html
Copyright © 2020-2023  润新知