资讯专栏INFORMATION COLUMN

Beautiful Mixins -《Beautiful JavaScript》读书分享

enali / 2460人阅读

摘要:是基于原型链继承的,但是其诡异的写法可能让好多初学者望而却步。在我们有和,很容易切换上下文,将各种互不相干糅合,达到的目的。最近在读这本书,有一些好的内容,正好可以跟大家分享,但并不是全部,有兴趣的同学也可以自己读一下,请支持正版。

刚开始,代码只是代码而已。当代码多了,我们发明了函数,以便代码可以重用。到后来,我们的函数也越来越多,所以我们又发明了别的一些编程概念。但是当我们沉迷于这些新概念的同时,我们可能就错过了一些近在眼前的美丽事物……

类继承

类概念主要存在于面向对象编程中的,可能大家上学时都学过,比如Java、C#、Objective-C等面向对象语言。每个实体都需要先要有一个类,这个类对于我们来说是比较抽象的(不是说abstract class哦),然后子类还可以继承父类。

这感觉似乎有点亚里士多德形而上学和分类法的味道,但简单来说这就是面向对象的基础,在写出实际代码之前,你得先写出一些类、接口,然后类继承类、类实现接口……

实际上我们人类并不是非常擅长分类,打个比方,一个按钮(Button),它应该是一个矩形(Rectangle),还是一个控件(Control)呢?我们可以让Button继承Rectangle,让Rectangle继承Control……等等,是不是有什么不对?

所以,面向对象的概念可能会在项目刚开始时就把你压垮,但这并不是说面向对象就不好,只是有时候并不适合你的项目。

原型链继承

可能大家都知道,JavaScript是不支持class关键字的,因为JavaScript本身就不作为一个面向对象语言来设计,但并不妨碍大家想出各种招来实现class

JavaScript是基于原型链(Prototype)继承的,但是其诡异的写法可能让好多初学者望而却步。

function Circle() {
  this.radius = 7;
}
Circle.prototype = {
  area: function () {
    return Math.PI * this.radius * this.radius;
  },
  grow: function () {
    this.radius++;
  },
  shrink: function () {
    this.radius--;
  }
};

在ES5之后,我们有了Object.create方法,好理解了一些。

var circle = Object.create({
  area: function () {
    return Math.PI * this.radius * this.radius;
  },
  grow: function () {
    this.radius++;
  },
  shrink: function () {
    this.radius--;
  }
}, {
  radius: {
    writable: true,
    configurable: true,
    value: 7
  }
});

只是第二参数用起来有点复杂,其实就是跟Object.defineProperties()的参数一样,但是我们现在又有了ES6呀:

class Cicle {
  constructor() {
    this.radius = 7;
  }
  area() {
    return Math.PI * this.radius * this.radius;
  }
  grow() {
    this.radius++;
  }
  shrink() {
    this.radius--;
  }
}

这都是一些使用JavaScript实现class的例子,实际上,JavaScript还可以有另外一种重用代码的方式——Mixins。

Mixins

这就是我们今天的主题,近在眼前的美丽事物,但可不是什么新概念,用过Ruby或Python的同学,可能有所耳目,Mixins的字面意思就是把东西掺合在一起。

在JavaScript我们有callapply,很容易切换上下文,将各种互不相干糅合,达到Mixins的目的。

假设我们要做一个圆形按钮(RoundButton),它有两个特性:

是圆的

可点击

我们可以把这两个特性分别写做2个函数:

// 是圆的
var withCircle = function () {
  this.area = function () {
    return Math.PI * this.radius * this.radius;
  };
  this.grow = function () {
    this.radius++;
  };
  this.shrink = function () {
    this.radius--;
  }
}

// 可点击
var withClickable = function () {
  this.hover = function () {
    console.log("hovering");
  };
  this.press = function () {
    console.log("button pressed");
  };
  this.release = function () {
    console.log("button released");
  };
  this.fire = function () {
    this.action.fire();
  };
}

这是我们的圆形按钮:

var RoundButton = function(radius, label, action) {
  this.radius = radius;
  this.label = label;
  this.action = action;
};

现在我们要让给这个圆形按钮附上那两个特性:

withCircle.call(RoundButton.prototype);
withClickable.call(RoundButton.prototype);

var button = new RoundButton(4, "yes!", function() {
  return "you said yes!"
});
button1.fire(); // 输出 "you said yes!"

这样写,是不是瞬间显得既简洁又自然?让人一眼看懂代码在做什么。

当然这些附加特性的函数用的多了,也就创建了许多函数,这里可以简单的用一个立即执行函数(Immediately Invoked Function Expression)的闭包来对其进行优化一下,以withCircle为例:

var withCircle = (function () {
  function area() {
    return Math.PI * this.radius * this.radius;
  }
  function grow() {
    this.radius++;
  }
  function shrink() {
    this.radius--;
  }
  return function () {
    this.area = area;
    this.grow = grow;
    this.shrink = shrink;
  };
})();

这样就不需要每次使用都新建函数了,从而节省更多的资源。

Advice

有时候,你无法确保某些函数可能会覆盖原有的功能,例如以下例子:

Button.prototype.press = function() {
  console.log("pressed");
};

// 这时再用我们的 withClickable,就会覆盖掉 press
withClickable.call(Button.prototype);

这时候我们应该采用Advice,Twitter的Flight框架已经提供了此功能:

var withClickable = function () {
  this.after("press", function () {
    console.log("press again.");
  });
};

withAdvice.call(Button.prototype);
withClickable.call(Button.prototype);

var button = new Button();
button.press(); // 输出 "pressed", "press again."

两个press并不会互相冲突,而是有先后顺序的执行,就类似通过addEventListener添加了多个事件而不是直接修改onclick一样,具体细节可以参考Flight的API。

小结

作为一名程序员,我们或许在上学时就被灌输了面向对象的固有思想,毕竟面向对象从上世纪90年代到现在,经久不衰,自由它的优势。但是在JavaScript中,如果你并不善于面向对象的抽象思维,何不尝试一下Mixins呢?而且Mixins与类继承相比,还能更好的解耦合,可以用于任何Object之上,正好用上了JavaScript若类型的优势。

最近在读《Beautiful JavaScript》这本书,有一些好的内容,正好可以跟大家分享,但并不是全部,有兴趣的同学也可以自己读一下,请支持正版。

原文链接:http://t.cn/RteECIF
微信号:程序员晋级之路『code-learning』

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/80185.html

相关文章

  • Flutter是跨平台开发终极之选吗?Android开发该如何快速上手Flutter?

    摘要:月日,谷歌正式发布了的。到底能不能成为跨平台开发终极之选是基于前端诞生的,但是对前端开发来说,的环境配置很麻烦,需要原生的平台知识,还要担心遇上网络问题。现在已经不是曾经的小众框架,这两年里它已经逐步成长为主流的跨平台开发框架之一。 ...

    luckyyulin 评论0 收藏0
  • 006-读书笔记-Vue官网 混合

    摘要:混合中的钩子函数同名钩子函数都会执行如果组件中存在钩子函数,混合中也存在相同的钩子函数,那么两个钩子函数都会执行。最终的执行结果多个混合的钩子函数多个混合的钩子函数,会根据混合使用的顺序来执行。 1.认识混合 混合(mixins)是一种分发Vue组件中可复用功能的非常灵活的方式 混合的作用:抽取多个组件的共同部分,增强组件的可复用性 混合的实质:混合对象类似一个Vue实例,可以包含V...

    kk_miles 评论0 收藏0
  • 基础 - 收藏集 - 掘金

    摘要:的语言的动态性意味着我们可以使用以上种数据类型表示变换过渡动画实现案例前端掘金以下所有效果的实现方式均为个人见解,如有不对的地方还请一一指出。 读 zepto 源码之工具函数 - 掘金Zepto 提供了丰富的工具函数,下面来一一解读。 源码版本 本文阅读的源码为 zepto1.2.0 $.extend $.extend 方法可以用来扩展目标对象的属性。目标对象的同名属性会被源对象的属性...

    wuaiqiu 评论0 收藏0
  • 双十二大前端工程师读书清单

    摘要:本文最早为双十一而作,原标题双大前端工程师读书清单,以付费的形式发布在上。发布完本次预告后,捕捉到了一个友善的吐槽读书清单也要收费。这本书便从的异步编程讲起,帮助我们设计快速响应的网络应用,而非简单的页面。 本文最早为双十一而作,原标题双 11 大前端工程师读书清单,以付费的形式发布在 GitChat 上。发布之后在读者圈群聊中和读者进行了深入的交流,现免费分享到这里,不足之处欢迎指教...

    happen 评论0 收藏0
  • 双十二大前端工程师读书清单

    摘要:本文最早为双十一而作,原标题双大前端工程师读书清单,以付费的形式发布在上。发布完本次预告后,捕捉到了一个友善的吐槽读书清单也要收费。这本书便从的异步编程讲起,帮助我们设计快速响应的网络应用,而非简单的页面。 本文最早为双十一而作,原标题双 11 大前端工程师读书清单,以付费的形式发布在 GitChat 上。发布之后在读者圈群聊中和读者进行了深入的交流,现免费分享到这里,不足之处欢迎指教...

    余学文 评论0 收藏0

发表评论

0条评论

enali

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<