# Javascript

# 【bootstrap-table】相關連結

- [bootstap-table 官網](https://bootstrap-table.com/)
- [bootstrap table filterBy数据刷选过滤器和查询条件 - itxst.com](https://www.itxst.com/bootstrap-table-methods/bjemnemz.html)

# 【Bootstrap-table】bs_table_common.js

```JavaScript

var bootstrapTableBaseConfig = {
    height: getHeight(),
    idField:"dv_no",
    toolbar:"#toolbar",
    minimumCountColumns:2, //最小顯示欄位數量
    pagination:true,
    pageSize:'10000000', //預設row筆數
    pageList:['ALL'],
    queryParams:getQueryParams,
    search:false,
    showColumns:true,
    showFooter:false,
    showPaginationSwitch:true,
    showRefresh:true,
    showExport:true, //匯出按鈕
    showToggle:false,
    detailView:false,
    detailFormatter:detailFormatter,
    // responseHandler:responseHandler,
    virtualScroll:false,
    sidePagination:"server",
    contextMenu: '#bootstraptable-context-menu',
    contextMenuButton: '.bootstraptable-button',
    classes:'table table-bordered table-hover table-sm table-condensed nowrap',
}


/** common function begin */

function ajaxUpdata(config){

    var url = config.url || "";
    var update_type = config.update_type || "更新";
    var update_data = config.update_data || {};
    var ajax_type = config.ajax_type || "POST";
    var ajax_data_type = config.ajax_data_type || "json";
    var after_success_func = config.after_success_func || function(){};
    var after_error_func = config.after_success_func || function(){};
    // var bs_table = config.bs_table || $table;

    console.log('ajaxUpdata_config',config);
    
    var success_func = config.success_func || function(response){
        console.log("response",response);
        if (response.msg != undefined && (response.msg == "success")) {
            if(config.bs_table !== undefined) config.bs_table.bootstrapTable('refresh');
            alert(update_type+"資料成功!!");
        } else {
            alert((response.msg == undefined)?update_type+"失敗請洽系統管理員":response.msg);
        }
        after_success_func();
    }

    var error_func = success_func || function(response){
        var err_msg = response.msg || "";
        alert(err_msg+","+update_type+"失敗請洽系統管理員!!");
        // dialog.close();  //關閉對話視窗
        after_error_func();
    }
    
    var ajax_config = {
                url:  url,
                type: ajax_type,
                data: update_data,
                error: error_func,
                success: success_func,
                dataType: ajax_data_type
                };
    console.log(ajax_config);
    //寫入資料庫
    $.ajax(ajax_config);
}


function getEditDataStr(beforDate,afterData){
    var resultTextArr = [];
    for(var key in beforDate){
        if (afterData[key] !== undefined && beforDate[key] != afterData[key]
        ){
            if (beforDate[key] == null && afterData[key] == '') continue;
            resultTextArr.push(key+":"+beforDate[key]+"=>"+afterData[key]);
        }
    }
    if(resultTextArr.length > 0){
        return resultTextArr.join(",");
    }else{
        return "";
    }
}

function getDataByClass(class_name,perfix){
    var data = {};
    _perfix = perfix || "";
    $('.'+class_name).each(function(){
        var el = this;
        console.log(el.id);
        var id = (el.id).replace(_perfix,'');
        var value = $(el).val();
        data[id] = value;
        
    });
    return data;
}


function initTypeahead(config){
    
    console.log('initTypeahead typeahead',typeof jQuery().typeahead);
    console.log('initTypeahead Bloodhound',typeof Bloodhound);
    //check typeahead exist
    if(typeof jQuery().typeahead !== 'function') return;
    if(typeof Bloodhound !== 'function') return;

    console.log("initTypeahead");

    var selectFunc = (config && config.selectFunc) || function(event, data){
        $("#USER_ID").val(data['USER_ID']);
        $("#USER_NAME").val(data['USER_NAME']);
    }
    var userInfo = new Bloodhound({
        datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
        queryTokenizer: Bloodhound.tokenizers.whitespace,
        remote: {
            url: '/db/php/getOisUser.php?search=%QUERY',
            wildcard: '%QUERY'
        }
    });

    $('#typeahead_input').typeahead({

        	// classNames: {
        	// 	input: 'Typeahead-input',
        	// 	hint: 'Typeahead-hint',
        	// 	selectable: 'Typeahead-selectable'
        	// }
        // },
        hint: true,
        highlight: true,
        minLength: 1
        },
        {
        name: 'best-pictures',
        display: function(data){
            return  data['USER_ID']  ;
            // return  data['USER_ID']  + '-' + data['AD_ID']  + '-' + data['USER_NAME'];
        },
        source: userInfo,
        templates: {
            //查無資料顯示
            empty: [
            '<div class="empty-message">',
                '查無使用者',
            '</div>'
            ].join('\n'),
            
            suggestion: function(data) {
                return '<p><strong>' + data['USER_ID']  + '-' + data['AD_ID']  + '-' + data['USER_NAME'] + ' #' + data['EXT'] + '</strong></p>';
            }
        }
    });

    $('.typeahead').on('typeahead:select', function(event, data) {
        selectFunc(event, data);
    });

    $('.typeahead').on('typeahead:change ', function(event, data) {
        // console.log('change : ' , data);
    });

    $('.js_typeahead_reset_btn').on('click',function(){
        $('.js_typeahead_reset').each(function(index,el){
            $(el).val('');
        });
    });

    // for(var i = 1;i<=5;i++){
    //     if($('.js_typeahead_reset_btn_'+i).length > 0){
    //         $('.js_typeahead_reset_btn_'+i).on('click',function(){
    //             $('.js_typeahead_reset_'+i).each(function(index,el){
    //                 $(el).val('');
    //             });
    //         });
    //     }
    // }
}

function isEmpty(str){
    if(str === undefined || str.trim() === "" || str === null){
        return true;
    }
    return false;
}


function getEditDataStr(beforDate,afterData){
    var resultTextArr = [];
    for(var key in beforDate){
        if (afterData[key] !== undefined && beforDate[key] != afterData[key]
        ){
            if (beforDate[key] == null && afterData[key] == '') continue;
            resultTextArr.push(key+":"+beforDate[key]+"=>"+afterData[key]);
        }
    }
    if(resultTextArr.length > 0){
        return resultTextArr.join(",");
    }else{
        return "";
    }
}
/* ==============================================================================================*/

function responseHandler(res) {
    $.each(res.rows, function (i, row) {
        row.state = $.inArray(row.dv_no, selections) !== -1;
    });
    return res;
}

function getHeight(_height) {
    var _height = _height || 110;
    return $(window).height() - $(".panel-heading").height() - _height;
}

// function getQueryParams(params){

//     var queryParams = {};
//     $(".query").each(function(i,e){
//         var id = (e.id).replace("q_","");
//         queryParams[id]=$(e).val();
//     });
//     $.extend(queryParams,params);
//         console.log("getQueryParams",queryParams);

//     return queryParams;
// }


function getQueryParams(params){
    var queryParams = {};
    $(".query").each(function(i,e){
        var id = (e.id).replace("q_","");
        queryParams[id]=$(e).val();
    });
    $.extend(queryParams,params);
    return queryParams;
}



/* bootsrap table formatter */
function detailFormatter(index, row) {
    var html = [];
    var trans = [];
    $.each(row, function (key, value) {
        if (key != 'total' && key != 'check'){
            var name=(trans[key] === undefined) ? key: trans[key];
            var value=(value === null || value === undefined ) ? '': value;
            html.push('<p><b>' + name + ':</b> ' + value + '</p>');
        }
    });
    return html.join('');
}


function operateFormatter(value, row, index) {

    return [
        '<a class="add" href="javascript:void(0)" title="add">',
        '<i class="glyphicon glyphicon-plus text-success"></i>',
        '</a> ',
        '<a class="edit" href="javascript:void(0)" title="edit">',
        '<i class="glyphicon glyphicon-edit"></i>',
        '</a> ',
        '<a class="remove" href="javascript:void(0)" title="remove">',
        '<i class="glyphicon glyphicon-remove text-danger"></i>',
        '</a>'
    ].join('');
}

function editFormatter(value, row, index){
    return [
    '<a class="edit" href="javascript:void(0)" title="edit">',
    '<i class="glyphicon glyphicon-edit"></i>',
    '</a>'
    ].join('');
}

function yesNoFormatter(value,row,index){
    return (value == 1)?'是':'否';
}

function snFormatter(value,row,index) {
    return index+1;
}

function scanNumberFormatter(data) {
    return (data == 1)?'是':'否';
}

function totalTextFormatter(data) {
    return 'Total';
}

function totalNameFormatter(data) {
    return data.length;
}

function nowrapFormatter(){
    return { css:{"whiie-space":"nowrap"}} ;
}

function totalPriceFormatter(data) {
    var total = 0;
    $.each(data, function (i, row) {
        total += +(row.price.substring(1));
    });
    return '$' + total;
}


 // function buildTable($el) {
        //     var i, j, row,
        //             columns = [],
        //             data = [];
        //     var opt = $el.bootstrapTable('getOptions');
        //     var fixedColumnsOption = { fixedColumns: true,fixedNumber: +$('#fixedNumber').val()};
        //     $.extend(opt,fixedColumnsOption);
        //     console.log("bootstrap_option",opt);
        //     $el.bootstrapTable('destroy').bootstrapTable(opt);
        // }

$(window).resize(function () {
    
    var $table = $table || null;
    if($table !== null){
        
        $table.bootstrapTable('resetView', {
            height: getHeight()
        });
    }
});
```

選項設定預設值

```JavaScript
var DEFAULTS = {
    height: undefined,
    classes: 'table table-bordered table-hover',
    buttons: {},
    theadClasses: '',
    headerStyle: function headerStyle(column) {
      return {};
    },
    rowStyle: function rowStyle(row, index) {
      return {};
    },
    rowAttributes: function rowAttributes(row, index) {
      return {};
    },
    undefinedText: '-',
    locale: undefined,
    virtualScroll: false,
    virtualScrollItemHeight: undefined,
    sortable: true,
    sortClass: undefined,
    silentSort: true,
    sortName: undefined,
    sortOrder: undefined,
    sortReset: false,
    sortStable: false,
    rememberOrder: false,
    serverSort: true,
    customSort: undefined,
    columns: [[]],
    data: [],
    url: undefined,
    method: 'get',
    cache: true,
    contentType: 'application/json',
    dataType: 'json',
    ajax: undefined,
    ajaxOptions: {},
    queryParams: function queryParams(params) {
      return params;
    },
    queryParamsType: 'limit',
    // 'limit', undefined
    responseHandler: function responseHandler(res) {
      return res;
    },
    totalField: 'total',
    totalNotFilteredField: 'totalNotFiltered',
    dataField: 'rows',
    footerField: 'footer',
    pagination: false,
    paginationParts: ['pageInfo', 'pageSize', 'pageList'],
    showExtendedPagination: false,
    paginationLoop: true,
    sidePagination: 'client',
    // client or server
    totalRows: 0,
    totalNotFiltered: 0,
    pageNumber: 1,
    pageSize: 10,
    pageList: [10, 25, 50, 100],
    paginationHAlign: 'right',
    // right, left
    paginationVAlign: 'bottom',
    // bottom, top, both
    paginationDetailHAlign: 'left',
    // right, left
    paginationPreText: '&lsaquo;',
    paginationNextText: '&rsaquo;',
    paginationSuccessivelySize: 5,
    // Maximum successively number of pages in a row
    paginationPagesBySide: 1,
    // Number of pages on each side (right, left) of the current page.
    paginationUseIntermediate: false,
    // Calculate intermediate pages for quick access
    search: false,
    searchHighlight: false,
    searchOnEnterKey: false,
    strictSearch: false,
    regexSearch: false,
    searchSelector: false,
    visibleSearch: false,
    showButtonIcons: true,
    showButtonText: false,
    showSearchButton: false,
    showSearchClearButton: false,
    trimOnSearch: true,
    searchAlign: 'right',
    searchTimeOut: 500,
    searchText: '',
    customSearch: undefined,
    showHeader: true,
    showFooter: false,
    footerStyle: function footerStyle(column) {
      return {};
    },
    searchAccentNeutralise: false,
    showColumns: false,
    showColumnsToggleAll: false,
    showColumnsSearch: false,
    minimumCountColumns: 1,
    showPaginationSwitch: false,
    showRefresh: false,
    showToggle: false,
    showFullscreen: false,
    smartDisplay: true,
    escape: false,
    filterOptions: {
      filterAlgorithm: 'and'
    },
    idField: undefined,
    selectItemName: 'btSelectItem',
    clickToSelect: false,
    ignoreClickToSelectOn: function ignoreClickToSelectOn(_ref) {
      var tagName = _ref.tagName;
      return ['A', 'BUTTON'].includes(tagName);
    },
    singleSelect: false,
    checkboxHeader: true,
    maintainMetaData: false,
    multipleSelectRow: false,
    uniqueId: undefined,
    cardView: false,
    detailView: false,
    detailViewIcon: true,
    detailViewByClick: false,
    detailViewAlign: 'left',
    detailFormatter: function detailFormatter(index, row) {
      return '';
    },
    detailFilter: function detailFilter(index, row) {
      return true;
    },
    toolbar: undefined,
    toolbarAlign: 'left',
    buttonsToolbar: undefined,
    buttonsAlign: 'right',
    buttonsOrder: ['paginationSwitch', 'refresh', 'toggle', 'fullscreen', 'columns'],
    buttonsPrefix: CONSTANTS.classes.buttonsPrefix,
    buttonsClass: CONSTANTS.classes.buttons,
    icons: CONSTANTS.icons,
    iconSize: undefined,
    iconsPrefix: CONSTANTS.iconsPrefix,
    // glyphicon or fa(font-awesome)
    loadingFontSize: 'auto',
    loadingTemplate: function loadingTemplate(loadingMessage) {
      return "<span class=\"loading-wrap\">\n      <span class=\"loading-text\">".concat(loadingMessage, "</span>\n      <span class=\"animation-wrap\"><span class=\"animation-dot\"></span></span>\n      </span>\n    ");
    },
    onAll: function onAll(name, args) {
      return false;
    },
    onClickCell: function onClickCell(field, value, row, $element) {
      return false;
    },
    onDblClickCell: function onDblClickCell(field, value, row, $element) {
      return false;
    },
    onClickRow: function onClickRow(item, $element) {
      return false;
    },
    onDblClickRow: function onDblClickRow(item, $element) {
      return false;
    },
    onSort: function onSort(name, order) {
      return false;
    },
    onCheck: function onCheck(row) {
      return false;
    },
    onUncheck: function onUncheck(row) {
      return false;
    },
    onCheckAll: function onCheckAll(rows) {
      return false;
    },
    onUncheckAll: function onUncheckAll(rows) {
      return false;
    },
    onCheckSome: function onCheckSome(rows) {
      return false;
    },
    onUncheckSome: function onUncheckSome(rows) {
      return false;
    },
    onLoadSuccess: function onLoadSuccess(data) {
      return false;
    },
    onLoadError: function onLoadError(status) {
      return false;
    },
    onColumnSwitch: function onColumnSwitch(field, checked) {
      return false;
    },
    onPageChange: function onPageChange(number, size) {
      return false;
    },
    onSearch: function onSearch(text) {
      return false;
    },
    onToggle: function onToggle(cardView) {
      return false;
    },
    onPreBody: function onPreBody(data) {
      return false;
    },
    onPostBody: function onPostBody() {
      return false;
    },
    onPostHeader: function onPostHeader() {
      return false;
    },
    onPostFooter: function onPostFooter() {
      return false;
    },
    onExpandRow: function onExpandRow(index, row, $detail) {
      return false;
    },
    onCollapseRow: function onCollapseRow(index, row) {
      return false;
    },
    onRefreshOptions: function onRefreshOptions(options) {
      return false;
    },
    onRefresh: function onRefresh(params) {
      return false;
    },
    onResetView: function onResetView() {
      return false;
    },
    onScrollBody: function onScrollBody() {
      return false;
    },
    onTogglePagination: function onTogglePagination(newState) {
      return false;
    }
  };
```

# 【ES6】其他新語法

### 預設值

```JavaScript
function sum(a, b = 1) { // 加入預設值避免錯誤
  return a + b;
}
console.log(sum(1));
```

# 【ES6】物件拷貝

淺層拷貝(只複製一層，內容物件還是參考至同一位址)

```JavaScript
const person = {
  name: '小明',
  obj: {}
}

const person2 = {...person} //方法1
const person3 = Object.assign({},person); //方法2
```

深層拷貝

```JavaScript
const person = {
  name: '小明',
  obj: {}
}

// 使用josn轉成字串，再轉回物件
const person2 = JSON.parse(JSON.stringify(person))
```

# 【ES6】物件拷貝

淺層拷貝(只複製一層，內容物件還是參考至同一位址)

```JavaScript
const person = {
  name: '小明',
  obj: {}
}

const person2 = {...person} //方法1
const person3 = Object.assign({},person); //方法2
```

深層拷貝

```JavaScript
const person = {
  name: '小明',
  obj: {}
}

// 使用josn轉成字串，再轉回物件
const person2 = JSON.parse(JSON.stringify(person))
```

# 【ES6】非同步 與 promise

非同步

```JavaScript
function getData() {
  setTimeout(() => {
    console.log('... 已取得遠端資料');
  }, 0);
}
// 請問取得資料的順序為何
function init() {
  console.log(1);
  getData();
  console.log(2);
}
init();
////////////// result
1
2
... 已取得遠端資料
```

premise 使程式流程同步

```JavaScript
const promiseSetTimeout = (status) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (status) {
        resolve('promiseSetTimeout 成功')
      } else {
        reject('promiseSetTimeout 失敗')
      }
    }, 0);
  })
}

// #2-1 基礎運用 (成功用 then 捕捉, 失敗用 catch捕捉)
// 呼叫成功
console.log(1)
promiseSetTimeout(1)
.then((res)=>{
    console.log(res);
})
.catch((err)=>{
    console.log(err);
})
console.log(2)
/** result
1
promiseSetTimeout 成功
2
*/

// 呼叫失敗
console.log(1)
promiseSetTimeout(2)
.then((res)=>{
    console.log(res);
})
.catch((err)=>{
    console.log(err);
})
console.log(2)
/** result
1
promiseSetTimeout 失敗
2
*/
```

promise 串接 ( 不使用巢狀寫法)

```JavaScript
promiseSetTimeout(1)
.then((res)=>{
    console.log(res);

    // 使用return 發起第二次非同步
    return promiseSetTimeout(1);
})
// 第二次非同步成功
.then((res) => {
    console.log("第二次非同步成功");
})
```

# 【ES6】展開

### 合併陣列

```JavaScript
const groupA = ['小明', '杰倫', '阿姨'];
const groupB = ['老媽', '老爸'];
const groupC = [...groupA,...groupB]; //'小明', '杰倫', '阿姨', '老媽', '老爸'
```

### 繼承物件

```JavaScript
const methods = {
  fn1() {
    console.log(1);
  },
  fn2() {
    console.log(1);
  },
}
const methods2 = {
  ...methods,
  fn3(){
    console.log(1);
  }
}
```

### 轉換為純陣列

```JavaScript
// NodeList 或 HTMLCollection 或 Arguments 是「類陣列」的物件
// 無法使用 map 等純物件的方法
const doms = document.querySelectorAll('li');
console.log(doms); 
// 轉為純陣列
const newDom = [...doms];
newDom.map((item,i)=>{
  console.log(i);
});
```

# 【ES6】陣列方法

[https://www.youtube.com/watch?v=\_vFuDQ\_6Xt8](https://www.youtube.com/watch?v=_vFuDQ_6Xt8)

# 【ES6】箭頭函式

### 改寫原 functoin

```JavaScript
// 傳統寫法
function fn(a, b) {
  return a * b;
}
// 箭頭函式
const fn = (a, b) => {
  return a * b;
}
// 省略 return
const fn = (a, b) => {
  a * b;
}
/////////////////////////////
function fn(name) {
  return "Hello"+name;
}
// 單行省略 return , {}
const fn = (name) => "Hello "+name;

```

### this 指向不會指向全域，而是指向自己

```JavaScript
var name = '全域'
const person = {
  name: '小明',
  callName: function () { 
    console.log('1', this.name); // 1 小明
    setTimeout(function () {
      console.log('2', this.name); // 2 全域
      console.log('3', this); // 3 window
    }, 10);
  },
}
```

# 【ES6】賦值解構

### 取出特定值作為變數

```JavaScript
// #1 取出特定值作為變數
const person = {
  name: '小明',
  age: 16,
  like: '鍋燒意麵'
}
const {name, age} = person;
console.log(name, age); // 小明, 16
```

### 陣列解構

<div id="bkmrk-"><div></div></div>```shell
const arr = ['小明', '杰倫', '漂亮阿姨'];
const [Ming, Jay ] = arr;
console.log(Ming, Jay); // '小明', '杰倫'
```

### 解構加展開

```JavaScript
const people = {
  Ming: {
    name: '小明',
    age: 16,
    like: '鍋燒意麵'
  },
  Jay: {
    name: '杰倫',
    age: 18,
    like: '跑車'
  },
  Auntie: {
    name: '漂亮阿姨',
    age: 22,
    like: '名牌包'
  }
}
const { Ming,...other} = people;
console.log(Ming, other );
// Ming
// {
//     name: '小明',
//     age: 16,
//     like: '鍋燒意麵'
// }

// other
// {
//   Jay: {
//     name: '杰倫',
//     age: 18,
//     like: '跑車'
//   },
//   Auntie: {
//     name: '漂亮阿姨',
//     age: 22,
//     like: '名牌包'
//   }
// }
```

### 解構加上重新命名

```JavaScript
// 冒號：別名
const { Ming:AAA,...other} = people;
console.log(AAA, other );
```

### 函式參數解構

```JavaScript
const person = {name: "Sam", age: 18};

const fn = function({name, age}){
console.log(name,age) 
}

fn(person); // Sam 18
```

### 解構在React應用

###  

```JavaScript

const { useState } = React;
```

# 【ES6】async / await

### 使用方式

```JavaScript
// #1 當資料有順序性的時候
const fn1 = () =>{
  axios.get("https://randomuser.me/api/")
  .then((res)=> {
    console.log("fn1 seed",res.data.info.seed);
    const seed = res.data.info.seed;
    return axios.get(`https://randomuser.me/api/?seed=${seed}`)
  })
  .then((res)=>{
    console.log("fn1 seed2",res.data.info.seed)
  })
}

// #2 使用 async await 來修改
const fn2 = async ()=> {
  const res = await axios.get("https://randomuser.me/api/")
  const seed = res.data.info.seed;
  console.log("fn2 seed",res.data.info.seed);
  const res2 = await axios.get(`https://randomuser.me/api/?seed=${seed}`);
  const seed2 = res2.data.info.seed;
  console.log("fn2 seed2",res2.data.info.seed);
}


// #3 立即函式的寫法
(async function(){
  const res = await axios.get("https://randomuser.me/api/")
  const seed = res.data.info.seed;
  console.log("fn3 seed",res.data.info.seed);
  const res2 = await axios.get(`https://randomuser.me/api/?seed=${seed}`);
  const seed2 = res2.data.info.seed;
  console.log("fn3 seed2",res2.data.info.seed);
})();


```

### 錯誤處理

```JavaScript
const fn4 = async () => {
  try {
    const res = await axios.get("https://randomuser.me/api/")
    return res
  } catch (error) {
    throw error
  }
}

fn4()
.then((res)=>{
  console.log(res);
})
.catch((err)=>{
  console.log(err);
});
```

### async function 本身就是 promise

[![image-1674748127619.png](https://bookstack.treemanou.com/uploads/images/gallery/2023-01/scaled-1680-/MPzDB9hd4GsVTixg-image-1674748127619.png)](https://bookstack.treemanou.com/uploads/images/gallery/2023-01/MPzDB9hd4GsVTixg-image-1674748127619.png)

### async Function 與 React

```JavaScript

// async Function 與 React
const App = () => {
  const [data, setData] = React.useState({}) 

  React.useEffect(() => {
    (async () => {
      const res = await axios.get('https://jsonplaceholder.typicode.com/todos/1');
      console.log(res);
      setData(res.data);
    })();
  }, []);

  return <div>
    {data.title}
  </div>
}

```

# 【ES6】ESModual

[![image-1674749911584.png](https://bookstack.treemanou.com/uploads/images/gallery/2023-01/scaled-1680-/EuEw8ikkjfuNOjuR-image-1674749911584.png)](https://bookstack.treemanou.com/uploads/images/gallery/2023-01/EuEw8ikkjfuNOjuR-image-1674749911584.png)

### 什麼樣的匯出對應什麼樣的匯入

#### 預設匯入(一個匯出檔只能有一個)

<div id="bkmrk-%E5%B8%B8%E8%A6%8B%E7%9A%84%E5%8C%AF%E5%87%BA%E6%96%B9%E5%BC%8F%EF%BC%8C%E9%80%9A%E5%B8%B8%E7%94%A8%E6%96%BC%E5%8C%AF%E5%87%BA%E7%89%A9%E4%BB%B6%EF%BC%8C%E5%9C%A8-r"><div>常見的匯出方式，通常用於匯出物件，在 React 開發中可用來匯出<span style="background-color: #ffff00;">元件</span></div><div></div></div>```JavaScript
/** 匯出頁 exportDefault.js */
export default function fn(){
	console.log("default export")
}

/** 匯入 index.html */
/** export ${name}(隨便取) from ${path} */
<script type="module">
	export aa from "./exportDefault.js";
	aa();
</script>
```

```JavaScript
/** 匯出頁 exportDefault.js */
export default {
	name: "sam",
    func: function(){
    	console.log("預設方法...")
    }
}

/** 匯入 index.html */
/** import ${name}(隨便取) from ${path} */
<script type="module">
	import aa from "./exportDefault.js";
	console.log(aa.name);
    aa.func();
</script>
```

#### 具名匯入(可多個)

<div id="bkmrk-%E5%8F%AF%E7%94%A8%E6%96%BC%E5%8C%AF%E5%87%BA%E5%B7%B2%E5%AE%9A%E7%BE%A9%E7%9A%84%E8%AE%8A%E6%95%B8%E3%80%81%E7%89%A9%E4%BB%B6%E3%80%81%E5%87%BD%E5%BC%8F%EF%BC%8C%E5%B0%88%E6%A1%88"><div>可用於匯出已定義的變數、物件、函式，專案開發中通常用於 “方法匯出”</div><div>第三方的框架、函式、套件很常使用具名定義 “<span style="background-color: #ffff00;">方法</span>”</div></div>```JavaScript
/** 匯出頁 exportModule.js */
export const myName = "Jhon"
export function fn1() {
	console.log("function 1");
}

/** 匯入 index.html */
/** import { 解構名稱 } from ${path} */
<script type="module">
	import {fn1, myName} from "./exportDefault.js";
    console.log("myName",myName);
	fn1();
</script>
```

#### 全部匯出(不常用)

```JavaScript
/** 匯出頁 exportModule.js */
export const myName = "Jhon"
export function fn1() {
	console.log("function 1");
}

/** 匯入 index.html */
/** import * as ${別名} from ${path} */
<script type="module">
	import * as bb from "./exportDefault.js";
    console.log("myName",bb.myName);
	bb.fn1();
</script>
```

#### sideEffect (舊函式庫，立即執行)

```JavaScript
/** 匯出頁 sideEffect.js */
(function(global){
	global.$ = "I am jquery";
})(window)

/** 匯入 index.html */
/** import ${path} */
<script type="module">
	import "./sideEffect.js";
    console.log("$",$); // I am jquery
	bb.fn1();
</script>
```

# 【ES6】import / export

匯入的類型要看匯出的形式，所以我用匯入方式來說明

### 使用default 匯出

匯入就可以取任何名稱

```JavaScript
import fn from './func.js';
fn();
// 匯出檔案 func.js
// export default function (){
//   console.log("fn")
// }
```

### 具名匯入

匯入必須使用完整名稱，也可以匯入的時候給予別名

```JavaScript
import {fn2, fn3 as new_fn3, person} from './func.js';
fn2();
new_fn3();
export const person = {name:"sam",age:18}

// 匯出檔案 func.js
// export function fn2(){
//   console.log("fn2")
// }
// export function fn3(){
//   console.log("fn3")
// }

```

### 全部匯入

```JavaScript
import * as obj from './func.js';
obj.fn();
// 匯出檔案 func.js
// export function fn(){
//   console.log("fn")
// }
```

### 傳統lib匯入

ex.Jquery

```JavaScript
// import file
import './fn.js';

// module file
(function() {
  console.log('IIFE');
})();

```

# 【JS】相關資源

## 教學

---

- [现代 JavaScript 教程](https://zh.javascript.info/)

# 【JS】iframe 自適應內容高度

```JavaScript
<script type="text/javascript">
function SetCwinHeight() {
    var iframeid = document.getElementById("mainframe"); //iframe id  
    if (document.getElementById) {
        if (iframeid && !window.opera) {
            if (iframeid.contentDocument && iframeid.contentDocument.body.offsetHeight) {
                iframeid.height = iframeid.contentDocument.body.offsetHeight;
            } else if (iframeid.Document && iframeid.Document.body.scrollHeight) {
                iframeid.height = iframeid.Document.body.scrollHeight;
            }
        }
    }
}
</script>
```

```HTML
<iframe src="./sourcePage.html" name="mainframe" id="mainframe"
    width="100%" marginwidth="0" marginheight="0"
    scrolling="No" frameborder="0"
    onload="Javascript:SetCwinHeight()">
</iframe>
```

# 【ES6】null 判斷

[https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Operators/Optional\_chaining](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Operators/Optional_chaining)

**可選串連**運算子 **`?.`** 允許進行深層次的物件值存取，而無需透過明確的物件值串連驗證。`?.` 運算子的操作與 `.` 屬性存取運算子相似，後者會在參照到 [nullish](https://developer.mozilla.org/en-US/docs/Glossary/Nullish "此頁面目前僅提供英文版本") ([`null`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/null "此頁面目前僅提供英文版本") or [`undefined`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined "此頁面目前僅提供英文版本")) 的值時出現錯誤，而前者可選串連則回傳 `undefined` 。 當需要存取一個函數，而這函數並不存在時，則會回傳 `undefined`

```JavaScript
const adventurer = {
  name: 'Alice',
  cat: {
    name: 'Dinah',
  },
};

const dogName = adventurer.dog?.name;
console.log(dogName);
// Expected output: undefined

console.log(adventurer.someNonExistentMethod?.());
// Expected output: undefined

```

當一個物件 `obj` 是巢狀結構時，在沒有可選串連之下，要去查找一深層的屬性值需要驗證每層間的參照連結：

```JavaScript
let nestedProp = obj.first && obj.first.second && obj.first.second.third;
```

現在可以用?.做串接判斷

```JavaScript
let nestedProp = obj.first?.second?.third
```

在用 ?? 賦予預設值避免 undefined

```JavaScript
let nestedProp = obj.first?.second?.third ?? "default"
```

# 【Tool】常用方法

\*判斷為空

```JavaScript
const isEmpty = (val) => {
    // null or undefined
    if (val == null) return true;

    if (typeof val === "boolean") return false;

    if (typeof val === "number") return !val;

    if (val instanceof Error) return val.message === "";

    // eslint-disable-next-line default-case
    switch (Object.prototype.toString.call(val)) {
        // String or Array
        case "[object String]":
        case "[object Array]":
            return !val.length;

        // Map or Set or File
        case "[object File]":
        case "[object Map]":
        case "[object Set]": {
            return !val.size;
        }
        // Plain Object
        case "[object Object]": {
            return !Object.keys(val).length;
        }
    }

    return false;
};

const isNotEmpty = (val) => {
    return !isEmpty(val);
};
```

# 【JS】?? 與  ||  比較

在 JavaScript 中，`??` (Nullish Coalescing) 和 `||` (Logical OR) 看起來很像，都是用來提供**預設值**的運算子，但它們判斷「什麼時候該用預設值」的標準完全不同。

這篇教學將幫你釐清兩者的核心差異，並告訴你什麼時候該用哪一個。

<div aria-busy="false" aria-live="polite" id="bkmrk-"><div aria-busy="false" aria-live="polite">---

</div></div>## 核心差異：判斷標準

兩者的主要區別在於對於「虛值 (Falsy values)」的處理方式。

### 1. 邏輯或運算子 `||` (Logical OR)

只要左側的值轉換為布林值後是 false，就會回傳右側的值。

JavaScript 中的 Falsy 值包括：

<div aria-busy="false" aria-live="polite" id="bkmrk-null-undefined-0-%22%22-"><div aria-busy="false" aria-live="polite">- `null`
- `undefined`
- `0`
- `""` (空字串)
- `NaN`
- `false`

</div></div>### 2. 空值合併運算子 `??` (Nullish Coalescing)

只有當左側的值是 null 或 undefined 時，才會回傳右側的值。

它會保留 0、"" 和 false 等有效數據。

<div aria-busy="false" aria-live="polite" id="bkmrk--0"><div aria-busy="false" aria-live="polite">---

</div></div>## 對比表

<table border="1" id="bkmrk-%E7%89%B9%E6%80%A7-%7C%7C-%28or%29-%3F%3F-%28nulli"><thead><tr><th>特性</th><th>|| (OR)</th><th>?? (Nullish)</th></tr></thead><tbody><tr><td>**觸發預設值的條件**</td><td>任何 Falsy 值 (0, "", false...)</td><td>僅限 null 或 undefined</td></tr><tr><td>**對 0 的處理**</td><td>視為 False，會被預設值覆蓋</td><td>視為有效值，保留 0</td></tr><tr><td>**對 "" (空字串) 的處理**</td><td>視為 False，會被預設值覆蓋</td><td>視為有效值，保留 ""</td></tr><tr><td>**設計目的**</td><td>廣泛的邏輯備案</td><td>精確處理「缺失資料」</td></tr></tbody></table>

<div aria-busy="false" aria-live="polite" id="bkmrk--1"><div aria-busy="false" aria-live="polite">---

</div></div>## 實戰範例

假設我們正在開發一個使用者設定頁面：

<div aria-busy="false" aria-live="polite" id="bkmrk-javascript-const-use"><div aria-busy="false" aria-live="polite"><div data-hveid="0" data-ved="0CAAQhtANahcKEwjVvbzE0eGRAxUAAAAAHQAAAAAQWA"><div><span class="ng-tns-c3121828532-21">JavaScript</span><div><button aria-label="複製代碼" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c3121828532-21 mat-unthemed ng-star-inserted"></button></div></div><div><div>```
<span class="hljs-keyword">const</span> userSettings = {
  <span class="hljs-attr">speed</span>: <span class="hljs-number">0</span>,
  <span class="hljs-attr">nickname</span>: <span class="hljs-string">""</span>,
  <span class="hljs-attr">isPremium</span>: <span class="hljs-literal">false</span>,
  <span class="hljs-attr">theme</span>: <span class="hljs-literal">null</span>
};

<span class="hljs-comment">// --- 使用 || 的結果 ---</span>
<span class="hljs-built_in">console</span>.log(userSettings.speed || <span class="hljs-number">10</span>);      <span class="hljs-comment">// 輸出: 10 (即便 0 是有效的數值)</span>
<span class="hljs-built_in">console</span>.log(userSettings.nickname || <span class="hljs-string">"匿名"</span>); <span class="hljs-comment">// 輸出: "匿名" (即便空字串是有效的名字)</span>
<span class="hljs-built_in">console</span>.log(userSettings.isPremium || <span class="hljs-literal">true</span>);  <span class="hljs-comment">// 輸出: true (這會讓使用者的 false 設定失效)</span>

<span class="hljs-comment">// --- 使用 ?? 的結果 ---</span>
<span class="hljs-built_in">console</span>.log(userSettings.speed ?? <span class="hljs-number">10</span>);      <span class="hljs-comment">// 輸出: 0 (正確保留了數值)</span>
<span class="hljs-built_in">console</span>.log(userSettings.nickname ?? <span class="hljs-string">"匿名"</span>); <span class="hljs-comment">// 輸出: "" (正確保留了空字串)</span>
<span class="hljs-built_in">console</span>.log(userSettings.isPremium ?? <span class="hljs-literal">true</span>);  <span class="hljs-comment">// 輸出: false (正確保留了布林值)</span>
<span class="hljs-built_in">console</span>.log(userSettings.theme ?? <span class="hljs-string">"Light"</span>);  <span class="hljs-comment">// 輸出: "Light" (因為是 null)</span>

```

</div></div></div>---

</div></div>## 使用場景建議

### 什麼時候使用 `??`？ (推薦做法)

當你想要\*\*「安全地提供預設值」\*\*且不希望意外覆蓋掉 `0`、`false` 或 `""` 時。

<div aria-busy="false" aria-live="polite" id="bkmrk-%E8%99%95%E7%90%86-api-%E5%9B%9E%E5%82%B3%E7%9A%84%E9%81%B8%E6%93%87%E6%80%A7%E5%8F%83%E6%95%B8%E3%80%82-%E6%95%B8%E5%80%BC%E8%A8%88"><div aria-busy="false" aria-live="polite">- 處理 API 回傳的選擇性參數。
- 數值計算（例如：計數器、座標）。
- UI 表單輸入（允許空字串）。

</div></div>### 什麼時候使用 `||`？

當你想要\*\*「只要沒內容就給預設值」\*\*，或者你確定資料只會是字串/物件，且你不需要處理 `0` 或空字串的情況。

<div aria-busy="false" aria-live="polite" id="bkmrk-%E8%88%8A%E7%89%88%E7%80%8F%E8%A6%BD%E5%99%A8%E7%9B%B8%E5%AE%B9%E6%80%A7%EF%BC%88%3F%3F-%E6%98%AF-es2020"><div aria-busy="false" aria-live="polite">- 舊版瀏覽器相容性（`??` 是 ES2020 引入的）。
- 確保變數一定有「實質內容」（非空、非零）。

---

</div></div>## 注意事項：混合使用

在 JavaScript 中，出於運算優先級的安全性考慮，你**不能**在沒有括號的情況下直接混合使用 `&&`、`||` 與 `??`：

<div aria-busy="false" aria-live="polite" id="bkmrk-javascript-%2F%2F-%E2%9D%8C-%E9%8C%AF%E8%AA%A4%E8%AA%9E%E6%B3%95"><div aria-busy="false" aria-live="polite"><div data-hveid="0" data-ved="0CAAQhtANahcKEwjVvbzE0eGRAxUAAAAAHQAAAAAQWQ"><div><span class="ng-tns-c3121828532-22">JavaScript</span><div><button aria-label="複製代碼" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c3121828532-22 mat-unthemed ng-star-inserted"></button></div></div><div><div>```
<span class="hljs-comment">// ❌ 錯誤語法：SyntaxError</span>
<span class="hljs-comment">// const result = a || b ?? c;</span>

<span class="hljs-comment">// ✅ 正確語法：使用括號明確優先級</span>
<span class="hljs-keyword">const</span> result = (a || b) ?? c;

```

</div></div></div></div></div>> **總結建議：** 在現代開發中，為了避免處理數值 `0` 或布林值時出現非預期的 Bug，**優先考慮使用 `??`**。

需要我為你示範如何將現有的舊代碼從 `||` 重構成 `??` 嗎？