本文例子来自《Functional Programming in JavaScript》第一章.
一个咖啡 store 的例子, 展示不同种类和不同尺寸的 coffee 价格.
比较容易想到的实现方式是:
html 代码
- <ul id="products"></ul>
- <script>
- // 哥伦比亚咖啡
- var columbian = {
- name: 'columbian',
- basePrice: 5
- };
- // 法式烘培咖啡
- var frenchRoast = {
- name: 'french roast',
- basePrice: 8
- };
- // 脱咖啡因咖啡
- var decaf = {
- name: 'decaf',
- basePrice: 6
- };
- // 根据种类和大小显示价格
- function printPrice(coffee, size) {
- if (size == 'small') {
- var price = coffee.basePrice + 2;
- } else if (size == 'medium') {
- var price = coffee.basePrice + 4;
- } else {
- var price = coffee.basePrice + 6;
- }
- var node = document.createElement('li');
- var label = coffee.name + ' ' + size;
- var textNode = document.createTextNode(label + 'price: $' + price);
- node.appendChild(textNode);
- document.getElementById('products').appendChild(node);
- }
- // 使用
- printPrice(columbian, 'small');
- printPrice(columbian, 'medium');
- printPrice(columbian, 'large');
- printPrice(frenchRoast, 'small');
- printPrice(frenchRoast, 'medium');
- printPrice(frenchRoast, 'large');
- printPrice(decaf, 'small');
- printPrice(decaf, 'medium');
- printPrice(decaf, 'large');
- </script>
问题很多, 自然不必多说, 比如一旦 size 变多, 就得增加 if else.
书中给出的函数式写法:
HTML 代码
- <ul id="products"></ul>
- <script>
- // 抽取函数, 视图和业务分离
- var printPrice = function(price, label) {
- var node = document.createElement('li');
- var textnode = document.createTextNode(label + 'price: $' + price);
- node.appendChild(textnode);
- document.getElementById('products').appendChild(node);
- };
- // 封装产品类
- var columbian = function(){
- this.name = 'columbian';
- this.basePrice = 5;
- };
- var frenchRoast = function(){
- this.name = 'french roast';
- this.basePrice = 8;
- };
- var decaf = function(){
- this.name = 'decaf';
- this.basePrice = 6;
- };
- // 尺寸对象
- var small = {
- getPrice: function(){return this.basePrice + 2},
- getLabel: function(){return this.name + 'small'}
- };
- var medium = {
- getPrice: function(){return this.basePrice + 4},
- getLabel: function(){return this.name + 'medium'}
- };
- var large = {
- getPrice: function(){return this.basePrice + 6},
- getLabel: function(){return this.name + 'large'}
- };
- // 构建数组
- var coffeeTypes = [columbian, frenchRoast, decaf];
- var coffeeSizes = [small, medium, large];
- // 混入辅助函数
- function plusMixin(constructor, mixin) {
- constructor.prototype = Object.create(constructor.prototype);
- for (var prop in mixin) {
- if (mixin.hasOwnProperty(prop)) {
- constructor.prototype[prop] = mixin[prop];
- }
- }
- return constructor;
- };
- // 生成 9 个对象实例
- var coffees = coffeeTypes.reduce(function(previous, current) {
- var newCoffee = coffeeSizes.map(function(mixin) {
- var newCoffeeObj = plusMixin(current, mixin);
- return new newCoffeeObj();
- });
- return previous.concat(newCoffee);
- }, []);
- // 显示每个对象
- coffees.forEach(function(coffee) {
- printPrice(coffee.getPrice(), coffee.getLabel());
- });
- </script>
首先得说, 代码量确实很大. OOP 和 FP 结合得很好. 这里关于混入, 在书中第 7 章有很好的论说.
通过混入的方式来实现原型继承.
我想到的另外一种写法是, 对象间的混入:
HTML 代码
- <ul id="products"></ul>
- <script>
- // 抽取函数, 视图和业务分离
- var printPrice = function(price, label) {
- var node = document.createElement('li');
- var textnode = document.createTextNode(label + 'price: $' + price);
- node.appendChild(textnode);
- document.getElementById('products').appendChild(node);
- };
- // 产品对象
- var columbian = {
- name: 'columbian',
- basePrice: 5
- };
- var frenchRoast = {
- name: 'french roast',
- basePrice: 8
- };
- var decaf = {
- name: 'decaf',
- basePrice: 6
- };
- // 尺寸对象
- var small = {
- getPrice: function(){return this.basePrice + 2},
- getLabel: function(){return this.name + 'small'}
- };
- var medium = {
- getPrice: function(){return this.basePrice + 4},
- getLabel: function(){return this.name + 'medium'}
- };
- var large = {
- getPrice: function(){return this.basePrice + 6},
- getLabel: function(){return this.name + 'large'}
- };
- // 构建数组
- var coffeeTypes = [columbian, frenchRoast, decaf];
- var coffeeSizes = [small, medium, large];
- // 混入辅助函数
- function plusMixin(object, mixin) {
- object = Object.create(object);
- for (var prop in mixin) {
- if (mixin.hasOwnProperty(prop)) {
- object[prop] = mixin[prop];
- }
- }
- return object;
- };
- // 生成 9 个对象实例
- var coffees = coffeeTypes.reduce(function(previous, current) {
- var newCoffee = coffeeSizes.map(function(mixin) {
- return newCoffeeObj = plusMixin(current, mixin);
- });
- return previous.concat(newCoffee);
- }, []);
- // 显示每个对象
- coffees.forEach(function(coffee) {
- printPrice(coffee.getPrice(), coffee.getLabel());
- });
- </script>
- <ul id="products"></ul>
- <script>
- // 抽取函数, 视图和业务分离
- var printPrice = function(price, label) {
- var node = document.createElement('li');
- var textnode = document.createTextNode(label + 'price: $' + price);
- node.appendChild(textnode);
- document.getElementById('products').appendChild(node);
- };
- // 产品对象
- var columbian = {
- name: 'columbian',
- basePrice: 5
- };
- var frenchRoast = {
- name: 'french roast',
- basePrice: 8
- };
- var decaf = {
- name: 'decaf',
- basePrice: 6
- };
- // 尺寸对象
- var small = {
- getPrice: function(){return this.basePrice + 2},
- getLabel: function(){return this.name + 'small'}
- };
- var medium = {
- getPrice: function(){return this.basePrice + 4},
- getLabel: function(){return this.name + 'medium'}
- };
- var large = {
- getPrice: function(){return this.basePrice + 6},
- getLabel: function(){return this.name + 'large'}
- };
- // 构建数组
- var coffeeTypes = [columbian, frenchRoast, decaf];
- var coffeeSizes = [small, medium, large];
- // 显示每个对象
- coffeeTypes.forEach(function(coffee) {
- coffeeSizes.forEach(function(size) {
- Object.setPrototypeOf(coffee, size);
- printPrice(coffee.getPrice(), coffee.getLabel());
- });
- });
- </script>
来源: http://www.qdfuns.com/article/17398/3989dac52015e6a7df23530fd2dc9e9a.html