JavaScript设计模式(组合模式)

1. 组合模式的定义

组合模式,将对象组合成 树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。

image.png

2. 场景

2.1 宏命令

<html>

<body>
    <button id="button">按我</button></body>

<script>

    const MacroCommand = function () {
        return {
            commandsList: [],
            add: function (command) {
                this.commandsList.push(command);
            },
            execute: function () {
                for (let i = 0, command; command = this.commandsList[i++];) {
                    command.execute();
                }
            }
        }
    };

    const openAcCommand = {
        execute: function () {
            console.log('打开空调');
        }
    };

    /**********家里的电视和音响是连接在一起的,所以可以用一个宏命令来组合打开电视和打开音响的命令 *********/

    const openTvCommand = {
        execute: function () {
            console.log('打开电视');
        }
    };

    const openSoundCommand = {
        execute: function () {
            console.log('打开音响');
        }
    };

    const macroCommand1 = MacroCommand();
    macroCommand1.add(openTvCommand);
    macroCommand1.add(openSoundCommand);

    /*********关门、打开电脑和打登录 QQ的命令****************/

    const closeDoorCommand = {
        execute: function () {
            console.log('关门');
        }
    };

    const openPcCommand = {
        execute: function () {
            console.log('开电脑');
        }
    };

    const openQQCommand = {
        execute: function () {
            console.log('登录 QQ');
        }
    };

    const macroCommand2 = MacroCommand();
    macroCommand2.add(closeDoorCommand);
    macroCommand2.add(openPcCommand);
    macroCommand2.add(openQQCommand);

    /*********现在把所有的命令组合成一个“超级命令”**********/

    const macroCommand = MacroCommand();
    macroCommand.add(openAcCommand);
    macroCommand.add(macroCommand1);
    macroCommand.add(macroCommand2);

    /*********最后给遥控器绑定“超级命令”**********/

    const setCommand = (function (command) {
        document.getElementById('button').onclick = function () {
            command.execute();
        }
    })(macroCommand);

</script>

</html>

2.2 扫描文件夹

/******************************* Folder ******************************/ 
const Folder = function( name ){
    this.name = name;
    this.files = [];
};

Folder.prototype.add = function( file ){ 
    this.files.push( file );
};

Folder.prototype.scan = function(){
    console.log( '开始扫描文件夹: ' + this.name );
    for ( let i = 0, file, files = this.files; file = files[ i++ ]; ){ 
        file.scan();
    }
};

/******************************* File ******************************/ 
const File = function( name ){
    this.name = name;
};

File.prototype.add = function(){
    throw new Error( '文件下面不能再添加文件' ); 
};
File.prototype.scan = function(){
    console.log( '开始扫描文件: ' + this.name ); 
};
const folder = new Folder( '学习资料' );
const folder1 = new Folder( 'JavaScript' ); 
const folder2 = new Folder ( 'jQuery' );

const file1 = new File( 'JavaScript设计模式与开发实践' ); 
const file2 = new File( '精通 jQuery' );
const file3 = new File( '重构与模式' )


folder1.add( file1 ); folder2.add( file2 );

folder.add( folder1 ); folder.add( folder2 ); folder.add( file3 );

const folder3 = new Folder( 'Nodejs' );
const file4 = new File( '深入浅出 Node.js' ); 
folder3.add( file4 );

const file5 = new File( 'JavaScript语言精髓与编程实践' );

folder.add( folder3 ); 
folder.add( file5 );

folder.scan(); 

2.3 react

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title></title>
    <style>
        .red {
            color: red;
        }
    </style>
</head>

<body>
    <div id="root"></div>
    <script>
        function ReactElement(type, props) {
            this.type = type;
            this.props = props;
        }
        let React = {
            createElement(type, props = {}, ...childrens) {
                childrens.length === 1 ? childrens = childrens[0] : void 0
                return new ReactElement(type, { ...props, children: childrens })
            }
        };
        let render = (eleObj, container) => {
            // 先取出第一层 进行创建真实dom
            let { type, props } = eleObj;
            let elementNode = document.createElement(type); // 创建第一个元素
            for (let attr in props) { // 循环所有属性
                if (attr === 'children') { // 如果是children表示有嵌套关系
                    if (typeof props[attr] == 'object') { // 看是否是只有一个文本节点
                        props[attr].forEach(item => { // 多个的话循环判断 如果是对象再次调用render方法
                            if (typeof item === 'object') {
                                render(item, elementNode)
                            } else { //是文本节点 直接创建即可
                                elementNode.appendChild(document.createTextNode(item));
                            }
                        })
                    } else { // 只有一个文本节点直接创建即可
                        elementNode.appendChild(document.createTextNode(props[attr]));
                    }
                } else if (attr === 'className') { // 是不是class属性 class 属性特殊处理
                    elementNode.setAttribute('class', props[attr]);
                } else {
                    elementNode.setAttribute(attr, props[attr]);
                }
            }
            container.appendChild(elementNode)
        };
        //ReactDOM.render(<div>hello<span>world</span></div>);
        //ReactDOM.render(React.createElement("div",null,"hello,",React.createElement("span",null,"world")));
        render(React.createElement("div", null, "hello,", React.createElement("span", { class: "red" }, "world")), document.getElementById('root'));

    </script>
</body>

</html>

3. 总结

  • 组合模式可以让我们使用树形方式创 建对象的结构。我们可以把相同的操作应用在组合对象和单个对象上。在大多数情况下,我们都 可以忽略掉组合对象和单个对象之间的差别,从而用一致的方式来处理它们。

  • 然而,组合模式并不是完美的,它可能会产生一个这样的系统:系统中的每个对象看起来都 与其他对象差不多。它们的区别只有在运行的时候会才会显现出来,这会使代码难以理解。此外,
    如果通过组合模式创建了太多的对象,那么这些对象可能会让系统负担不起。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容