最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • “类”设计模式和“原型”设计模式——“复制”和“委托”的差异

    正文概述 掘金(掘金安东尼)   2021-05-21   549

    小引

    JavaScript 技能持有者一定有问过这个问题:

    你期望得到的答案应该为:“是” 或 “不是”。

    但是可惜,你得不到这样简单的答案!

    你大概了解一通之后,你会被告知:

    wtf!为什么是不纯粹?能不能纯粹一点?!我们喜欢纯粹,不喜欢混沌!

    ......

    实际上,死扣定义真的没太必要。定义背后的故事才是最重要的!

    看完本篇,你就会明白这种“混沌”是什么、来自何处,以及去往何方!!

    撰文不易,多多鼓励。点赞再看,养成习惯。???

    “类”设计模式

    妇孺皆知,面向对象三大特性:【封装】、【继承】、【多态】。

    1. 所谓封装,即把客观事物封装成抽象的类。

    2. 所谓继承,即子类继承父类的能力。

    3. 所谓多态,即子类可以用更特殊的行为重写所继承父类的通用行为。

    其中,“类”的概念最最关键!【类】描述了一种代码的组织结构形式,它是软件中对真实世界中问题领域的建模方法。

    举个例子:

    就好比我们现实中修房子,你要修一个“写字楼”、或者一个“居民楼”、或者一个“商场”,你就得分别找到修“写字楼”、“居民楼”、“商场”的【设计蓝图】。

    但是设计蓝图只是一个建筑计划,并不是真正的建筑。要想在真实世界实现这个建筑,就得由建筑工人将设计蓝图的各类特性(比如长宽高、功能)【复制】到现实世界来。

    这里的【设计蓝图】就是【类】,【复制】的过程就是【实例化】,【实例】就是【对象】

    类的内部通常有一个同名的构造方法,我们设想下,它的伪代码就可能是这样的:

    class Mall { // “商场”类
    
        Mall( num ){ // 同名构造方法
            garage = num // 地下车库数量
        }
    
        shop( goods ) { // 买东西
           output( "We can buy: ", goods )
        }
    
    }
    
    // 构造函数大多需要用 new 来调,这样语言引擎才知道你想要构造一个新的类实例。
    vanke = new Mall(1) // vanke 有 1 个地下车库
    
    vanke.shop("KFC") // "We can buy: KFC"
    
    

    java 是典型的面向对象语言。基于“类”,我们再通过以下一段 java 代码来看看对继承多态的理解。

    public abstract class Animal{ // 抽象类
         abstract void sound();
    }
    public class Chicken extends Animal{ // 继承
        public void sound(){
          sound("咯咯咯");
        }
    }
    public class Duck extends Animal{
        public void sound(){
          sound("嘎嘎嘎");
        }
    }
    
    public static void main(String args[]){
      Aninal chicken = new Chicken();
      Animal duck = new Duck();
      chicken.sound(); //咯咯咯
      duck.sound();  //嘎嘎嘎
    }
    

    鸡和鸭都属于动物分类,都可以发出叫声(继承),但是它们却可以发出不同的叫声(多态),很容易理解。

    继承可以使子类获得父类的全部功能; 多态可以使程序有良好的扩展;

    回想下:在 JS 中,我们可能会怎样写:

    var Duck = function () {};
    var Chicken = function () {};
    var makeSound = function ( animal ) {
        if( animal instanceof Duck){
            console.log("嘎嘎嘎");    
        }else if( animal instanceof Chicken){
            console.log("咯咯咯");    
        }
    };
    makeSound(new Duck());
    makeSound(new Chicken());
    

    这里既没用到继承,也没用到多态。这样【写判断】是代码“不清爽”的罪魁祸首!

    • 此处留一个疑问,如果不用判断,还可以怎么写?

    在 vue2 中,我们可能会这么写:

    export default {
      data() {
          return {
    
          },
          mounted(){
              this.Chicken()
              this.Duck()
          },
          methods:{
              funtion AnimalSound(sound){
                  console.log("叫声:" + sound)
              },
              funtion Chicken(){
                  this.AnimalSound("咯咯咯")
              },
              funtion Duck(){
                  this.AnimalSound("嘎嘎嘎")
              }
          }
      }
    

    像这种函数嵌套调用是很常见的。没有看到继承,也没有看到多态,甚至都没有看到最根本的“类”?!

    (实际上,每个函数都是一个 Function 对象。按照最开始定义所述,对象是类的实例,所以也是能在函数中看到“类”的!)

    在 JavaScript 中,函数成了第一等公民! 函数似乎什么都能做!它可以返回一个对象,可以赋值给一个变量,可以作为数组项,可以作为对象的一个属性......

    但这明显不是“类的设计模式”吧!

    “类”设计模式和“原型”设计模式——“复制”和“委托”的差异

    “类的设计模式” 意味着对【设计蓝图】的【复制】,在 JS 各种函数调用的场景下基本看不到它的痕迹。

    “原型”设计模式

    其实,众所周知,JS 也是能做到【继承】和【多态】的!只不过它不是通过类复制的方式,而是通过原型链委托的方式!

    一图看懂原型链?

    “类”设计模式和“原型”设计模式——“复制”和“委托”的差异

    看不懂?没关系,记住这两句话再来看:

    1. 一个对象的显示原型的构造函数指向对象本身(很熟悉有没有?在本文哪里见过?)
    2. 一个对象的隐式原型指向构造这个对象的函数的显示原型。

    原来,JS 不是通过在类里面写同名构造函数的方式来进一步实现的实例化,它的构造函数在原型上!这种更加奇特的代码服用机制有异于经典类的代码复用体系。

    这里再附一个经典问题?JS new 操作会发生什么?

    会是像类那样进行复制吗?

    答案是否定的!

    JS 访问一个对象的属性或方法的时候,先在对象本身中查找,如果找不到,则到原型中查找,如果还是找不到,则进一步在原型的原型中查找,一直到原型链的最末端。复制不是它所做的,这种查找的方式才是!对象之间的关系更像是一种委托关系,就像找东西,你在我这找不到?就到有委托关系的其它人那里找找看,再找不到,就到委托委托关系的人那里找......直至尽头,最后还找不到,指向 null。

    不过你也可以通过这种委托的关系来模拟经典的面向对象体系:类、继承、多态。但“类”设计模式只是一种可选的设计模式,你可以模拟,也可以不模拟!

    现实是 ES6 class 给我们模拟了:

    class Widget { 
        constructor(width,height) { 
            this.width = width || 50; 
            this.height = height || 50; 
            this.$elem = null; 
        } 
        render($where){ 
            if (this.$elem) { 
                this.$elem.css( { 
                    width: this.width + "px", 
                    height: this.height + "px" 
                }).appendTo( $where ); 
            } 
        } 
    } 
    class Button extends Widget { 
        constructor(width,height,label) { 
            super( width, height ); 
            this.label = label || "Default"; 
            this.$elem = $( "<button>" ).text( this.label ); 
        } 
        render($where) { 
            super.render( $where ); 
            this.$elem.click( this.onClick.bind( this ) ); 
        } 
        onClick(evt) { 
            console.log( "Button '" + this.label + "' clicked!" ); 
        } 
    }
    

    看起来,非常不错,很清晰!

    没有 .prototype 显示原型复杂的写法,也无需设置 .proto 隐式原型。还似乎用 extends 、super 实现了继承和多态。

    然而,这只是语法糖的陷阱!JS 没有类,没有复制,它的机制是“委托”。

    class 并不会像传统面向类的语言一样在申明时作静态复制的行为,如果你有意或者无意修改了父类,那子类也会收到影响。

    举例:

    class C { 
     constructor() { 
        this.num = Math.random(); 
     } 
     rand() { 
        console.log( "Random: " + this.num ); 
     } 
    } 
    var c1 = new C(); 
    c1.rand(); // "Random: 0.4324299..."
    C.prototype.rand = function() { 
        console.log( "Random: " + Math.round( this.num * 1000 )); 
    }; 
    var c2 = new C(); 
    c2.rand(); // "Random: 867"
    c1.rand(); // "Random: 432" ——噢!
    

    ES6 class 混淆了“类设计模式”和“原型设计模式”。它最大的问题在于,它的语 法有时会让你认为,定义了一个 class 后,它就变成了一个(未来会被实例化的)东西的 静态定义。你会彻底忽略 Class 是一个对象,是一个具体的可以直接交互的东西。当然,它还有其它细节问题,比如属性覆盖方法、super 绑定的问题,有兴趣自行了解。

    小结

    • “类设计模式”的构造函数挂在同名的类里面,类的继承意味着复制,多态意味着复制 + 自定义。

    • “原型设计模式”的构造函数挂在原型上,原型的查找是一种自下而上的委托关系。

    • “类设计模式”的类定义之后就不支持修改。

    • “原型设计模式”讲究的是一种动态性,任何对象的定义都可以修改,这和 JavaScript 作为脚本语言所需的动态十分契合!

    你可以用“原型设计模式”来模拟“类设计模式”,但是这大概率是得不偿失的。

    最后,如果再被问道:JavaScript 是面向对象语言吗?

    如果这篇文章看懂了,就可以围绕:“类设计模式”和“原型设计模式”来吹了。

    如果本文没有看懂,就把下面的标答背下来吧......

    “类”设计模式和“原型”设计模式——“复制”和“委托”的差异

    关注公众号《掘金安东尼》,持续输出ing!!!

    最近有点疲于写业务代码?......谁能支支招?

    参考文献

    • 命名函数表达式探秘
    • 函数式和面向对象编程有什么区别?
    • tutorials/js.mp
    • 你不知道的 JavaScript
    • JavaScript 轻量级函数式编程

    起源地下载网 » “类”设计模式和“原型”设计模式——“复制”和“委托”的差异

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元