【问题标题】:What's the difference between 'var webApp = { .. }' and 'var webApp = function (){ .. }''var webApp = { .. }' 和 'var webApp = function (){ .. }' 有什么区别
【发布时间】:2018-06-06 10:57:32
【问题描述】:

我最近对模块化 JS 进行了很多试验,但我仍然想知道我是否以“正确的方式”编写它。

例如,如果我有一个包含输入和提交按钮的页面,这些按钮应该在提交后显示数据(例如表格和图表),所以我在 IFFE 下编写我的代码,这样就没有任何东西可以访问它,而是使用像这样的对象变量这个:

var webApp = { ... } 

在其中我缓存来自 DOM 的元素,添加绑定事件和其他有用的功能。

这是我用于表单的真实代码,该表单在加载数据时应显示图形、表格和进度条,并且所有内容都在一个对象中管理qwData

(function() {

    const qwData = {

        // Initialize functions
        init: function() {
            this.cacheDom();
            this.bindEvents();
        },
        // Cache vars 
        cacheDom: function() {
            this.dataDisplayed      = false;
            this.countUsers         = <?php echo $_SESSION['all_users_count_real']; ?>;
            this.customerMachines   = <?php echo $_SESSION['customer_statistics']['total_machines']; ?>;
            this.$form              = $('#frm');
            this.$alistToolbar      = this.$form.find('.alist-toolbar');
            this.start_date         = this.$form[0][9].value;
            this.end_date           = this.$form[0][10].value;
            this.dateCount          = this.countDays(this.start_date, this.end_date);
            this.show               = document.querySelector('#btn-show');
            this.downloadBtn        = document.querySelector('#download_summary_button');
            this.$dataContainer     = $('#qw-data-container');
            this.$qwTable           = $('#qwtable');
            this.$qwTbody           = this.$qwTable.find('tbody');
            this.$qwTd              = this.$qwTbody.find('td');
            this.qwChart            = echarts.init(document.getElementById('main-chart'));
            this.progressBar        = document.querySelector('.progress-bar');
            Object.defineProperty(this, "progress", {
                get: () => {
                   return this.progressPrecent || 0;
                },
                set: (value) => {

                    if( value != this.progressPrecent ) {
                      this.progressPrecent = value;
                      // this.setQwChartProgress(value);
                      this.setProgressBarValue(value);
                      this.setProgressButton(value);
                    }
                }, 
                  configurable: true
            });
            this.qwChartProgress    = this.progress;
        },
        // Bind click events (or any events..)
        bindEvents: function() {

            var that = this;

            // On click "Show" BTN
            this.show.onclick = this.sendData.bind(this);

            // On Change inputs
            this.$form.change(function(){
                that.updateDatesInputs(this);
            });

            // downloadQw
            this.downloadBtn.onclick = this.downloadQw.bind(this);
        },
        downloadQw: function(e){
            e.preventDefault();

            var customer = "<?php echo $_SESSION['company_name']; ?>";
            var filename = customer + "qws_"+ this.start_date + "-" + this.end_date + ".zip";

            $.ajax({
                url: "/aaa/api/download_csv.php",
                method: "GET",
                dataType : "json",
                data: { 
                    customer: customer,
                    filename: filename
                },
                success:function(result){
                if(result.status){
                    window.location.href="/aaa/api/download_csv.php?customer="+customer+"&filename="+filename+"&download=1";
                }
            },
                error:function(){
                }
            })
        },
        setQwChartProgress: function(value){
            if (value != 0) {
                // Show Chart Loading 
                this.qwChart.showLoading({
                    color: (value == 99) ? '#00b0f0' : '#fff',
                    text: value + '%' 
                });
            }
        },
        setProgressButton: function(value){

            if ( value >= 100 || value == 0 ){
                this.show.value     = 'Show';
            }
            else {
                this.show.value     = value +'%';
                // this.show.disabled   = true;
                this.disableShow();
            }
        },
        resetShowButton: function(){
            this.show.value = 'Show';
            this.disableShow();
        },
        disableShow: function(){
            // this.show.style.color = "grey";
            // this.show.disabled   = true;
            this.show.classList.add("isDisabled");
        }, 
        enableShow: function(){
            // this.show.style.color = "#66aa66";
            // this.show.disabled   = false;
            this.show.classList.remove("isDisabled");
        },
        updateDatesInputs: function(){
            this.start_date     = this.$form[0][9].value;
            this.end_date       = this.$form[0][11].value;
            this.dateCount      = this.countDays(this.start_date,this.end_date);
            // this.show.disabled   = false;
            this.enableShow();
            this.removeError();
        },
        removeError: function(){
            if (this.errors) {
                this.errors.remove();
                delete this.errors;
            }
        },
        countDays: function(date1, date2){

            // First we split the values to arrays date1[0] is the year, [1] the month and [2] the day
            var date1 = date1.split('-');
            var date2 = date2.split('-');

            // Now we convert the array to a Date object, which has several helpful methods
            date1 = new Date(date1[0], date1[1], date1[2]);
            date2 = new Date(date2[0], date2[1], date2[2]);

            // We use the getTime() method and get the unixtime (in milliseconds, but we want seconds, therefore we divide it through 1000)
            var date1_unixtime = parseInt(date1.getTime() / 1000);
            var date2_unixtime = parseInt(date2.getTime() / 1000);

            // This is the calculated difference in seconds
            var timeDifference = date2_unixtime - date1_unixtime;

            // in Hours
            var timeDifferenceInHours = timeDifference / 60 / 60;

            // and finaly, in days :)
            var timeDifferenceInDays = timeDifferenceInHours  / 24;

            if (timeDifferenceInDays > 0){
                return timeDifferenceInDays;
            } else {
                // alert('Error: The date are invalid.');
            }
        },
        // Get data, prevent submit defaults and submit. 
        sendData: function(e) {
            e.preventDefault();

            if (this.show.classList.contains('isDisabled')) {

                this.showErrorDiv("Please select a new date range before submitting.");
            } else {

                let that                = this;
                let estimatedTotalTime  = ( (this.countUsers*this.customerMachines)/777 ) * 0.098; 
                let estimatedTime       = estimatedTotalTime/99;
                let estimatedTimeMs     = estimatedTime*1000;
                let timer               = setInterval( function(){that.incrementProgress(timer);}, estimatedTime); 

                console.log('Total Time: ' + estimatedTotalTime + 's');
                console.log('Estimated Time for 1%: ' + estimatedTime + 's');

                $.ajax({
                    type: 'POST',
                    url: "/manageit/ajax.php?module=qw_module",
                    dataType: 'json',
                    data: {
                            start_ts: that.start_date,
                            stop_ts: that.end_date, 
                            submitted: true, 
                            company_name: "<?php echo $_SESSION['company_name']; ?>"
                    },
                    beforeSend: function() {

                        // Show Chart Loading 
                        that.qwChart.showLoading({ 
                            color: '#00b0f0', 
                            // text: that.qwChartProgress
                            text: ''
                        });

                        // If data div isn't displayed
                        if (!that.dataDisplayed) {
                            // Show divs loading
                            that.showMainDiv();
                        } else {
                            that.$qwTbody.slideUp('fast');
                            that.$qwTbody.html('');
                        }
                    },
                    complete: function(){},
                    success: function(result){

                        // Reset show btn
                        that.resetShowButton();

                        // Clear timer
                        clearInterval(timer);

                        // Set progressbar to 100%
                        that.setProgressBarTo100();

                        // Show Download Button
                        that.downloadBtn.style.display = 'inline-block';

                        // Insert Chart Data
                        that.insertChartData(result);

                        // Insert Table Data
                        that.insertTableData(result);
                    }
                });

                that.dataDisplayed = true;
            }
        },
        showErrorDiv: function(errorTxt){

            if (!this.errors){
                this.errors             = document.createElement("div");
                this.errors.className   = "qw_errors_div";
                this.errors.textContent = errorTxt;
                this.$alistToolbar.append(this.errors);
            } 
        },
        // Insert Data to Table
        insertTableData: function(json){

            let str = '';
            let isOdd = ' rowspan="2" ';

            for ( let i=1; i<9; i++ ) {

                str += '<tr>';

                for (let j = 0; j < 8; j++) {

                    if ((i%2 === 0) && (j==0)){
                        // do nada
                    } else {
                        str += '<td '; 
                        str += ((i % 2 !== 0)&&(j==0)) ? isOdd : '';
                        str += '> '; 
                        str += json[i][j]; 
                        str += '</td>';
                    }
                }
                str += '</tr>'; 
            }

            this.$qwTbody.html(str);

            this.$qwTbody.slideDown('fast', function(){
                if ($(this).is(':visible'))
                    $(this).css('display','table-row-group');
            });

            // Apply colors on table.
            this.tableHover();
        },
        tableHover: function(){

            this.$qwTd              = this.$qwTbody.find('td');
            var that =  this;

            this.$qwTd.eq(0).hover( function(){
                that.$qwTd.eq(0).css('background-color', '#f5f5f5');
                that.$qwTd.eq(0).parent().css('background-color', '#f5f5f5');
                that.$qwTd.eq(0).parent().next().css('background-color', '#f5f5f5');    
            }, function(){
                that.$qwTd.eq(0).css('background-color', '');
                that.$qwTd.eq(0).parent().css('background-color', '');
                that.$qwTd.eq(0).parent().next().css('background-color', '');   
            });

            this.$qwTd.eq(15).hover( function(){
                that.$qwTd.eq(15).css('background-color', '#f5f5f5');
                that.$qwTd.eq(15).parent().css('background-color', '#f5f5f5');
                that.$qwTd.eq(15).parent().next().css('background-color', '#f5f5f5');   
            }, function(){
                that.$qwTd.eq(15).css('background-color', '');
                that.$qwTd.eq(15).parent().css('background-color', '');
                that.$qwTd.eq(15).parent().next().css('background-color', '');  
            });

            this.$qwTd.eq(30).hover( function(){
                that.$qwTd.eq(30).css('background-color', '#f5f5f5');
                that.$qwTd.eq(30).parent().css('background-color', '#f5f5f5');
                that.$qwTd.eq(30).parent().next().css('background-color', '#f5f5f5');   
            }, function(){
                that.$qwTd.eq(30).css('background-color', '');
                that.$qwTd.eq(30).parent().css('background-color', '');
                that.$qwTd.eq(30).parent().next().css('background-color', '');  
            });

            this.$qwTd.eq(45).hover( function(){
                that.$qwTd.eq(45).css('background-color', '#f5f5f5');
                that.$qwTd.eq(45).parent().css('background-color', '#f5f5f5');
                that.$qwTd.eq(45).parent().next().css('background-color', '#f5f5f5');   
            }, function(){
                that.$qwTd.eq(45).css('background-color', '');
                that.$qwTd.eq(45).parent().css('background-color', '');
                that.$qwTd.eq(45).parent().next().css('background-color', '');  
            });
        },
        incrementProgress: function(timer){

            if (this.progress == 99)
                clearInterval(timer);
            else 
                this.progress++;
        },
        // Insert Data to Chart
        insertChartData: function(json){

            var posList = [
                'left', 'right', 'top', 'bottom',
                'inside',
                'insideTop', 'insideLeft', 'insideRight', 'insideBottom',
                'insideTopLeft', 'insideTopRight', 'insideBottomLeft', 'insideBottomRight'
            ];

            this.qwChart.configParameters = {
                rotate: {
                    min: -90,
                    max: 90
                },
                align: {
                    options: {
                        left: 'left',
                        center: 'center',
                        right: 'right'
                    }
                },
                verticalAlign: {
                    options: {
                        top: 'top',
                        middle: 'middle',
                        bottom: 'bottom'
                    }
                },
                position: {
                    options: echarts.util.reduce(posList, function (map, pos) {
                        map[pos] = pos;
                        return map;
                    }, {})
                },
                distance: {
                    min: 0,
                    max: 100
                }
            };

            this.qwChart.config = {
                rotate: 90,
                align: 'left',
                verticalAlign: 'middle',
                position: 'insideBottom',
                distance: 15,
                onChange: function () {
                    var labelOption = {
                        normal: {
                            rotate: this.qwChart.config.rotate,
                            align: this.qwChart.config.align,
                            verticalAlign: this.qwChart.config.verticalAlign,
                            position: this.qwChart.config.position,
                            distance: this.qwChart.config.distance
                        }
                    };
                    this.qwChart.setOption({
                        series: [{
                            label: labelOption
                        }, {
                            label: labelOption
                        }, {
                            label: labelOption
                        }]
                    });
                }
            };

            var labelOption = {
                normal: {
                    show: true,
                    position: this.qwChart.config.position,
                    distance: this.qwChart.config.distance,
                    align: this.qwChart.config.align,
                    verticalAlign: this.qwChart.config.verticalAlign,
                    rotate: this.qwChart.config.rotate,
                    // formatter: '{c}  {name|{a}}',
                    formatter: '{name|{a}}',
                    fontSize: 16,
                    rich: {
                        name: {
                            // textBorderColor: '#fff', 
                            // color: '#333',
                            // color: '#717171',
                            color: '#525252',
                            shadowColor: 'transparent', 
                            shadowBlur: 0, 
                            textBorderColor: 'transparent',
                            textBorderWidth: 0, 
                            textShadowColor: 'transparent', 
                            textShadowBlur: 0
                        }
                    }
                }
            };

            option = {
                color: ['#007bff', '#00b0f0', 'red', '#e5323e'],
                tooltip: {
                    trigger: 'axis',
                    axisPointer: {
                        type: 'shadow'
                    }
                },
                legend: {
                    data: ['Inactives / Viewers', 'Inactives / Viewers / Less than 1min per day', 'Light no Macro'] 
                },
                toolbox: {
                    show: true,
                    orient: 'vertical',
                    left: 'right',
                    top: 'center',
                    feature: {
                        mark: {show: true},
                        // dataView: {show: true, readOnly: false},
                        // magicType: {show: true, type: ['line', 'bar', 'stack', 'tiled']},
                        restore: {show: true},
                        saveAsImage: {show: true}
                    }
                },
                calculable: true,
                xAxis: [
                    {
                        type: 'category',
                        axisTick: {show: false},
                        data: ['Excel', 'Word', 'PowerPoint', 'All 3 Apps']
                    }
                ],
                yAxis: [
                    {
                        type: 'value', 
                        name: 'Score'
                    }
                ],
                series: [
                    {
                        name: 'Light no Macro',
                        type: 'bar',
                        label: labelOption,
                        color: 'red',
                        data: [ [3, json[7][7]] ]
                    },
                    {
                        name: 'Inactives / Viewers',
                        type: 'bar',
                        barGap: 0,
                        label: labelOption,
                        data: [ json[1][7], json[3][7], json[5][7], json[8][7] ]
                    },
                    {
                        name: 'Inactives / Viewers / Less than 1min per day',
                        type: 'bar',
                        label: labelOption,
                        data: [ json[2][7], json[4][7], json[6][7] ]
                    }
                ]
            };

            // Set charts options
            this.qwChart.setOption(option);
            // Hide Loading
            this.qwChart.hideLoading();
        },
        // Show Main div on submition (only)
        showMainDiv: function(){
            // Show all contatner div
            this.$dataContainer.slideDown('slow');
        },
        // Sets a new value for the progress bar
        setProgressBarValue: function(value){

            this.progressBar.style.width = this.returnNumWithPrecent(value);
        },
        returnNumWithPrecent: function(num){

            return num.toString() + '%';
        },
        setProgressBarTo100: function(){
            var that = this;
            // Show Download Button
            this.progress = 100;
            setTimeout(function () {
                // Show Download Button
                that.progress = 0;
            }, 1000);
        }
    }
    // run object
    qwData.init();
})();

但我看到 other examples 在函数而不是对象下编写功能:

webApp = function (){ ... };

如示例:

var Background = (function() {
  'use strict';
  // placeholder for cached DOM elements
  var DOM = {};
  /* =================== private methods ================= */
  // cache DOM elements
  function cacheDom() {
    DOM.$background = $('#background');
  }

  // coordinate async assembly of image element and rendering
  function loadImage() {
    var baseUrl = 'https://source.unsplash.com/category',
        cat     = 'nature',
        size    = '1920x1080';
    buildElement(`${baseUrl}/${cat}/${size}`)
      .then(render);
  }

  // assemble the image element
  function buildElement(source) {
    var deferred = $.Deferred(function (task) {
      var image = new Image();
      image.onload = function () {
        task.resolve(image);
      };
      image.onerror = function () {
        task.reject();
      };
      image.src = source;
    });
    return deferred.promise();
  }

  // render DOM
  function render(image) { 
    DOM.$background
      .append(image)
      .css('opacity', 1);
  }

  /* =================== public methods ================== */
  // main init method
  function init() {
    cacheDom();
    loadImage();
  }

  /* =============== export public methods =============== */
  return {
    init: init
  };
}());

我对此有两个问题:

  1. 使用对象和在对象内部设置函数、变量等有什么区别:

    var webApp = { ... };

和一个具有相同特征的函数变量(只是带有一个 语法写法不同)。就像我粘贴的链接中的示例一样。

var webApp = function (){ ... };
  1. 是否有权在一个对象/函数中编写所有(有点分开的元素,如图形、表格、进度条)的代码?这应该更好地分离到不同的对象吗?如果有更新更好的方法来编写此类代码,请提及我应该研究的内容。

【问题讨论】:

  • 我不清楚你在问什么。由于这就是人们在 2012 年左右实现“模块”的方式,您是在询问历史使用情况还是您今天是否应该这样做?另请注意,您的示例并不严格等价。
  • @JaredSmith Since this is how people achieved 'modules' circa 2012, are you asking about historical usage or whether you should do any of this today? 我刚刚开始探索模块化 JS,这是我通过我发现的教程学到的,如果有更好/更新的方法来编写这种代码,我什至不知道 - 如果你能告诉我要研究什么,我会非常高兴。我的最终目标是知道如何编写我提到的代码。
  • @RickSanchez 我写的可能比你想知道的要多:D

标签: javascript javascript-objects modularity


【解决方案1】:

互联网教程的一个问题是,它们会停留在相关点之上,并且很少有作者会及时更新它们。 JS 领域的事情发展得非常快,5 年前的行业标准(例如 jQuery)现在看起来很奇怪,当你仍然偶然发现它时。

所以,把我一直在唠叨别人忽略的好习惯付诸实践:

JavaScript 模块状态,2018 年中,快速变化,#deprecated

一团糟。

首先你有 ES 6 模块。 ES 6 重命名为 ES 2015,模块部分被取出并制成单独的规范,这意味着浏览器可以兼容 ES 2015 并且仍然没有原生模块。然而,3 年后,每个具有相关全球市场份额的浏览器(chrome、android chrome、firefox、iOS Safari)都至少实现了原生模块系统的基本版本(Edge、Opera 等也是如此)。我不清楚,因为我相信规范允许路径更宽容(我们稍后会回到那个),但这里是语法,相对或绝对文件路径,需要扩展名:

import Foo from './foo.js'; // Import default export from foo.js, alias to 'Foo'
import { Foo } from './foo.js'; // Import named export 'Foo' from foo.js

export default function () {}; // exports a function as default, importer aliases
const Foo = class Foo {};
export Foo; // exports named class Foo

与其他任何东西相比,它们有很多优势(首先,您不需要特殊的工具或构建过程),但由于它们是非常最近的,它们在JavaScript 生态系统。因为他们来的时间很长,而且人们有工作要做,所以他们实现了各种其他的模块模式/工具/系统。最早的问题之一是您的问题,但这种模式虽然肯定比没有好,但有足够的问题让人们开始四处寻找。

AMD 模块

另一个较早的产品是require.js 的异步模块定义。虽然它有一些引人注目的技术优势,但它实际上已经死了。

Common.js 模块

node.js 以它自己的基于 common.js 模块(基本上已经成为 common.js 的事实风格)的模块系统爆炸式地出现在现场。人们开始说“嘿,能够在浏览器中执行此操作真是太好了”,因此,browserify。 Browserify 是一个工具,它可以遍历你的依赖图并将require 表达式转换为浏览器可以处理的东西(基本上,通过创建一个函数)。 Node 的模块有点不适合浏览器,但融合在一个标准上比 8000 万个竞争的 adhoc 实现要好。人们看着这三个相互竞争的模块模式/系统(你的问题中的一个,AMD,common.js)并说我们可以统一这些。因此

通用模块定义

如果您在野外看到过如下代码:

(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD
        define(['jquery'], factory);
    } else if (typeof exports === 'object') {
        // Node, CommonJS-like
        module.exports = factory(require('jquery'));
    } else {
        // Browser globals (root is window)
        root.returnExports = factory(root.jQuery);
    }
}(this, function ($) {

那么您已经看到了 UMD。请注意它如何检查它所在的环境是为 AMD 还是 common.js 设置的。 Transformers 的编写目的是为了将两种样式都转换为这种样式,以解决遗留代码和易读性问题(这是相当多的样板文件)。

但人们想要更多:他们希望能够在代码中表达所有他们的 web 应用程序的依赖项(包括 css 和图像),并使用一个工具来分片和选择性地加载它。另外,此时本机模块规范已在草稿中,人们希望使用该语法。因此

Webpack 模块

Webpack 是目前使用的事实上的系统(尽管很多人仍然使用 browserify)。 Webpack 的模块语法如下所示:

import Foo from 'foo'; // imports default export from foo.js somewhere on PATH

是不是很眼熟?非常相似(但与本机模块略有不同)。 Webpack 还可以做以下事情:

import 'Something.css'; // commonly seen in react apps
import 'logo.svg'; // ditto

这很方便,随着人们转向组件化系统,能够在该组件的入口点文件中表达所有组件依赖关系是一件好事。不幸的是,HTML 导入本来可以让您在没有构建步骤的情况下本地执行此操作,但最终却以可怕的方式死亡。

与本机模块系统的不兼容、微妙的(路径和文件扩展名)和粗略的(导入非 js 资产)是不幸的,这意味着一个或另一个将不得不改变,因为我试图写最近基于原生模块的应用程序,非常很难使用库(几乎没有一个提供原生模块风格)。

使用什么是一种固执己见的问题,但如果您使用的是框架,请使用该框架的其他用户常用的任何内容。 Common.js 和 webpack 很常见,有很多工具可以使用它们,这可能是你目前最好的选择。另一件需要注意的是动态导入,它已经登陆多个浏览器。

抱歉,这一切都太混乱了,您只是在一个非常过渡的时期进入了 JavaScript。

【讨论】:

  • 我可能有点困惑。您的意思是,我在帖子中写的任何内容都与今天的 javascript“不相关”,我应该学习 ES 6(与 ES 2015 相同?我理解正确?)写法不同(类为“类”语法等,而不是普通的 js 函数/对象)。我现在的学习流程应该是什么?如果我的知识基于粘贴在主帖中的代码.. 我应该学习编写 ES6 吗?或者如何先使用 webpack?我主要用PHP,不想学nodejs,有必要用nodejs吗?
  • @RickSanchez 如果你想要我的意见(我在回答中试图避免的事情)关于你应该使用什么来完成今天的工作,请使用 webpack。至于您问题中的代码,techniques 是相关的(例如 IIFE、闭包),但使用它们来实现模块却不是。不需要理解 node.js(双关语),但是如果您看到 var foo = require('foo');,那么即使某些代码转换器为前端重写了它,这就是它的来源。至于为什么我们不再像在您的代码中那样使用模块模式,这是一个全新的问题,但简短的回答是......
  • ...(续)循环依赖呢?名称冲突?延迟加载?捆绑/分片?静态分析?事实证明,在真正的模块系统中有很多你真正想要的东西,而使用 IIFE 并不能解决问题。
  • 所以我会在周末搜索一个像样的 webpack + ES6 (+bable?) 在线课程
  • @RickSanchez 好计划。编写最小公分母 JavaScript 很乏味,我们中的许多人使用最新的标准化东西,并依赖 babel 和 webpack 之类的工具,并使用 gulp(甚至是旧的 Make)之类的东西将它们结合在一起。请注意,这些是专业开发人员构建面向客户的生产网站的工具,如果您不希望在该级别玩游戏(这完全没问题),那么您可能根本不需要模块 i>.
【解决方案2】:

IIFE 模式允许私有变量 - DOMcacheDomloadImage - 不能从 IIFE 外部访问。在对象模式中,一切都可以作为对象的属性公开访问。有时,如果不先声明一些变量,您根本无法构造您想要的对象,因此将它们隔离在 IIFE 中也很有用。

【讨论】:

    【解决方案3】:

    如果您熟悉面向对象的编程,则可以通过类的私有和公共属性和函数的类比来理解它。 其他方法:模块化模式,请参阅节点模块。这是关于包装的。

    看这段代码:

    var obj = (function() {
        let a = function() { console.log('function a'); };
        let b = function() { a(); console.log('function b'); };
        return { b: b };
    })();
    
    obj.b();
    // function a
    // function b
    
    obj.a();
    // TypeError: obj.a is not a function
    

    或者以这种方式更熟悉:

    function MyStuff() {
        let a = function() { console.log('function a'); };
        let b = function() { a(); console.log('function b'); };
        return { b: b };
    }
    
    var obj = MyStuff();
    
    obj.b();
    // function a
    // function b
    
    obj.a();
    // TypeError: obj.a is not a function
    

    在函数内部,您有一个本地范围,不能从外部访问。在这个本地范围内,您可以创建变量、函数等。

    在 return 语句中,您将一个对象导出函数和变量返回到外部范围。这就像决定哪些功能/道具是公开的,哪些应该保持私有。

    当您以var webApp = {...} 方式创建对象时,您是在直接创建一个对象,它的所有属性和函数都对您定义变量“webApp”的范围公开。

    回答您的第二个问题,是的,您应该按关注点将内容分成单独的对象。请参阅 SOLID 原则。 :)

    【讨论】:

    • 我熟悉 OOP,但不是 javascript,我不明白 var webApp = function (){ .. }var webApp = { .. } 的第一个例子有什么区别 - 我理解你的例子,但是不知道它与我的问题有什么关系,也许我错过了一些东西并且不太了解你
    • 你是对的,没有涵盖整个问题。我正在更新答案。 :)
    • 如果我将对象放在像qwData = { .. } 示例这样的IFFE 下,这是否解决了隐私问题? (如果是的话,它与你解释的方式写有什么不同,哪种更好/何时使用每种情况)
    • 我不确定我是否正确理解了您的问题,但请查看MyStuff 函数。它返回一个仅公开引用函数 b 的对象。所以只有 b 可以通过obj 直接访问。如果我创建了像 var obj = {a: function(){...}, b: function(){...}} 这样的变量 obj,那么 a 和 b 都可以访问。对于封装,最好使用构造函数,也为了代码重用,因为您可以随时调用 MyStuff,而无需重新定义它的属性/函数。
    猜你喜欢
    • 1970-01-01
    • 2015-05-27
    • 2019-09-21
    • 2018-12-07
    • 1970-01-01
    • 1970-01-01
    • 2013-06-28
    • 2011-06-01
    • 1970-01-01
    相关资源
    最近更新 更多