Index
概要
基本上发布在Developer Network上的文章,都会注意使用ECMA Script5版本来写JavaScript代码,以确保在Internet Explorer也能够运行。
但是,最新版本ECMA Script 2017等比起旧版更加好用很多,因此本次就向大家介绍几个最新的语法和kintone的JavaScript自定义中可能用到的地方。
※对于Internet Explorer上无法运行的语法,会备注【不支持IE】。
如果也希望在IE上可以使用,可以使用Webpack 或Polyfill,详情请参考 webpack入门 ~Babel,Polyfill为你带来美好的ES6体验~ 。
用const / let定义变量让初始化更加安全
一直以来定义变量都是使用var abc = 123; 等,今后推荐使用const和let。
var的作用域是函数,const / let 的作用域是块
块是决定变量名称或者函数等可访问的范围。在块内定义的变量只能在块内使用,出了块就访问不到。
var的作用域比较广,可以到函数作用域。但是const / let 是块作用域,因此只有在if块等用 {} 括住的范围内有效。// 在函数内声明 function someFunc() { var var_func_number = 1000; const const_func_number = 2000; let let_func_number = 3000; } // 使用typeof确认是否有声明过(已声明过的情况返回true) // 下面因为在函数外面不可以访问,因此都返回false console.log('var: ', typeof var_func_number !== 'undefined'); console.log('const: ', typeof const_func_number !== 'undefined'); console.log('let: ', typeof let_func_number !== 'undefined'); // 在if块内声明变量 if (true) { var var_block_number = 1000; const const_block_price = 2000; let let_block_price = 3000; } // 使用typeof确认是否有声明过(已声明过的情况返回true) // 用var声明时,在块之外也可以访问 console.log('var: ', typeof var_block_number !== 'undefined'); // 用const / let声明时,块外面不可以访问 console.log('const: ', typeof const_block_number !== 'undefined'); console.log('let: ', typeof let_block_number !== 'undefined');
const不可以重复声明或更改值
const不可以重复声明也不可以更改变量的值。如果要声明的是常量,就使用const。
代码写长后,可能会不小心重复声明同一变量名,或更改变量的值,为了防止类似的错误,推荐大家积极使用const。
※ 但请注意:可改写Object或者数组内的内容。var var_price = 1000; var_price = 2000; // 用var声明的变量的值被改了也不会报错 var var_price = 3000; // 可以重复声明 const const_price = 1000; const_price = 2000; // 用const声明的变量的值被改时会报错 const const_price = 3000; // const不可以重复声明
let可更改变量的值(和const一样不可重复声明)
let和const一样不可以重复声明,但是可以更改变量的值。如果事先知道代码执行过程中需要更改变量的值,请使用let声明。
简单地说,基本上使用const,除非明确需要再更改值的情况才使用let。var var_price = 1000; var_price = 2000; // 用var声明的变量的值被改时不会报错 var var_price = 3000; // 可以重复声明 let let_price = 1000; let_price = 2000; // 用let声明的变量的值可以更改 let let_price = 3000; // let不可以重复声明
尽量避免使用or语句
比如,经常会碰到类似kintone的记录列表的数据等需要循环处理的情况,如果使用for语句的话,很容易不小心陷入无限循环,而且代码也会很长。
因此,在对数组进行处理时,尽量使用下述的forEach() / filter() / map() / reduce() 。
forEach()
单纯只是想访问所有记录里的每天数据时,适合用forEach。
例)要在console.log里输出公司名称列表
用for写时
缺点是如果记录的条数指定错误时,可能陷入无限循环,而且要像records[i]这样写,所有需要记住[ i ]是指什么。const records = [ { $id: { value: 1, type: 'RECORD_NUMBER', }, '公司名称': { value: 'ABC公司', type: 'SINGLE_LINE_TEXT', }, }, { $id: { value: 2, type: 'RECORD_NUMBER', }, '公司名称': { value: '示例公司', type: 'SINGLE_LINE_TEXT', }, }, { $id: { value: 3, type: 'RECORD_NUMBER', }, '公司名称': { value: '公司XYZ', type: 'SINGLE_LINE_TEXT', }, }, ]; for (let i = 0; i < records.length; i++) { console.log(records[i]['公司名称'].value); // 显示结果 }
用forEach写的时
不用特意记住哪一行,records中的数据逐一代入 record变量中,执行循环处理。这样写,既可提高可读性,写起来也方便。
以往用for进行的处理基本上可以按照这样写,所以建议在写之前先考虑一下是否可以用forEach来写。const records = [ { $id: { value: 1, type: 'RECORD_NUMBER', }, '公司名称': { value: 'ABC公司', type: 'SINGLE_LINE_TEXT', }, }, { $id: { value: 2, type: 'RECORD_NUMBER', }, '公司名称': { value: '示例公司', type: 'SINGLE_LINE_TEXT', }, }, { $id: { value: 3, type: 'RECORD_NUMBER', }, '公司名称': { value: '公司XYZ', type: 'SINGLE_LINE_TEXT', }, }, ]; records.forEach(function(record) { console.log(record['公司名称'].value); // 显示结果 });
filter()
使用filter的话,可以获取条件一致的数组。
比如,只想要销售额达到n元以上的数据,可以用非常简单的代码实现。
例)获取合计达10,000元以上的记录
使用filter的话可获取和条件一致的数组。
下面就是获取销售额达n元以上的数据的例子。
用for写时
const records = [ { $id: { value: 1, type: 'RECORD_NUMBER', }, '合计': { value: '1000', type: 'NUMBER', }, }, { $id: { value: 2, type: 'RECORD_NUMBER', }, '合计': { value: '20000', type: 'NUMBER', }, }, { $id: { value: 3, type: 'RECORD_NUMBER', }, '合计': { value: '30000', type: 'NUMBER', }, }, ]; let results = []; for (let i = 0; i records.length; i++) { if (records[i]['合计'].value > 10000) { results.push(records[i]); } } console.log(results); // 显示结果
用filter写时
不需要用if语句也可以获取任意数据,代码看上去也更加清晰。
实际上,只是这种单纯处理的话可以在执行kintone API的时候直接指定条件,但是如果条件更加复杂,这个方法就非常好用了。const records = [ { $id: { value: 1, type: 'RECORD_NUMBER', }, '合计': { value: '1000', type: 'NUMBER', }, }, { $id: { value: 2, type: 'RECORD_NUMBER', }, '合计': { value: '20000', type: 'NUMBER', }, }, { $id: { value: 3, type: 'RECORD_NUMBER', }, '合计': { value: '30000', type: 'NUMBER', }, }, ]; const results = records.filter(function(record) { return (record['合计'].value > 10000); }); console.log(results); // 显示结果
map
使用map的话,可对整个数组进行处理。(返回新数组)
Array.prototype.map()
例)求合计(含消费税)
用for写时
const records = [ { $id: { value: 1, type: 'RECORD_NUMBER', }, '合计': { value: '0', type: 'NUMBER', }, '单价': { value: '1000', type: 'NUMBER', }, '用户数': { value: '2', type: 'NUMBER', }, }, { $id: { value: 2, type: 'RECORD_NUMBER', }, '合计': { value: '0', type: 'NUMBER', }, '单价': { value: '3000', type: 'NUMBER', }, '用户数': { value: '4', type: 'NUMBER', }, }, { $id: { value: 3, type: 'RECORD_NUMBER', }, '合计': { value: '0', type: 'NUMBER', }, '单价': { value: '20000', type: 'NUMBER', }, '用户数': { value: '8', type: 'NUMBER', }, }, ]; let results = []; for (let i = 0; i records.length; i++) { results.push(records[i]['合计'].value = records[i]['单价'].value * records[i]['用户数'].value * 1.08); }
用map写时
用这个可以大大减少代码行数。
从records[i]...开始的代码可以大大简短,而且还可以降低缺陷的风险。
实际上,如果只是单纯计算合计,那么用kintone标准功能的计算字段就可以了,但是如果遇到更加复杂的计算,无法使用计算字段时这个写法就可以派上用场了。const records = [ { $id: { value: 1, type: 'RECORD_NUMBER', }, '合计': { value: '0', type: 'NUMBER', }, '单价': { value: '1000', type: 'NUMBER', }, '用户数': { value: '2', type: 'NUMBER', }, }, { $id: { value: 2, type: 'RECORD_NUMBER', }, '合计': { value: '0', type: 'NUMBER', }, '单价': { value: '3000', type: 'NUMBER', }, '用户数': { value: '4', type: 'NUMBER', }, }, { $id: { value: 3, type: 'RECORD_NUMBER', }, '合计': { value: '0', type: 'NUMBER', }, '单价': { value: '20000', type: 'NUMBER', }, '用户数': { value: '8', type: 'NUMBER', }, }, ]; const results = records.map(function(record) { record['合计'].value = record['单价'].value * record['用户数'].value * 1.08; return record; }); console.log(results); // 输出结果
reduce
使用reduce可以计算records数组内的合计的总和。
使用reduce写的代码跟上面比可能会显得更加复杂一点,但是如果习惯了,在求数据的合计等情况特别的方便。
reduce一般是从左到右处理数组的,也可以用 reduceRight反向处理。
Array.prototype.reduce()
例)求所有合计的总和
用for写时
const records = [ { $id: { value: 1, type: 'RECORD_NUMBER', }, '合计': { value: '10000', type: 'NUMBER', }, }, { $id: { value: 2, type: 'RECORD_NUMBER', }, '合计': { value: '20000', type: 'NUMBER', }, }, { $id: { value: 3, type: 'RECORD_NUMBER', }, '合计': { value: '30000', type: 'NUMBER', }, }, ]; var results = 0; for (var i = 0; i records.length; i++) { results += Number(records[i]['合计'].value); } console.log(results); // 输出结果
用reduce写时
代码行数基本上没有什么差别,所以看不出有什么不一样,但是不使用index变量[i]可以减少缺陷的概率。
也可以用forEach写,但是用reduce不需要初始化,处理可以全部写在reduce内。
像下面例子一样,在求合计时,或者循环records数组计算合计等的情况下,非常推荐使用的一个函数。const records = [ { $id: { value: 1, type: 'RECORD_NUMBER', }, '合计': { value: '10000', type: 'NUMBER', }, }, { $id: { value: 2, type: 'RECORD_NUMBER', }, '合计': { value: '20000', type: 'NUMBER', }, }, { $id: { value: 3, type: 'RECORD_NUMBER', }, '合计': { value: '30000', type: 'NUMBER', }, }, ]; var results = records.reduce(function(prev, record) { return prev + Number(record['合计'].value); }, 0); console.log(results); // 输出结果
Arrow函数让函数代码更加简洁【不支持IE】
声明函数的方法有如下几种。
以往的函数声明方法(一般的function句式)
function someFunc() { // 处理内容 }; // 或者 var someFunc = function() { // 处理内容 }
Arrow函数
const someFunc = () => { // 处理内容 };
从ES6起新增了如上Arrow函数。 (使用=> 这种形状的箭头,所以称为Arrow函数。 )
Arrow函数的优点
Arrow函数有几个优点。
更加简洁
如上只是声明函数的话,其实没有什么差别,但是如果是Callback函数等,函数多样化时就显得特别的简洁。
特别是比如使用Array.map()或者filter()等获取记录列表中必要的数据,然后按照条件分支处理时。
以下例子是使用Array.prototype.filter()函数判断是否含税的数据,然后再用Array.prototype.map()获取含税的金额,生成新数组。const records = [ { $id: { value: 1, type: 'RECORD_NUMBER', }, '税种': { value: '含税', type: 'RADIO_BUTTON', }, '合计': { value: '10000', type: 'NUMBER', }, }, { $id: { value: 2, type: 'RECORD_NUMBER', }, '税种': { value: '不含税', type: 'RADIO_BUTTON', }, '合计': { value: '20000', type: 'NUMBER', }, }, { $id: { value: 3, type: 'RECORD_NUMBER', }, '税种': { value: '含税', type: 'RADIO_BUTTON', }, '合计': { value: '30000', type: 'NUMBER', }, }, ]; // 税种字段的值为“含税”或者“不含税” // 合计字段的值为金额。 // 以往的函数声明方法 const includedTaxPriceArray = records.filter(function(record) { return record['税种'].value === '含税'; }).map(function(record) { return record['合计'].value * 1.1; }); // 用Arrow函数的方法 const includedTaxPriceArrayWithArrowFunc = records.filter((record) => { return record['税种'].value === '含税'; }).map((record) => { return record['合计'].value * 1.1; }); console.log(includedTaxPriceArray); console.log(includedTaxPriceArrayWithArrowFunc)
像这样没有了 function看过去简洁多了。除此之外,还有其他的使用规则。
比如,如果参数只有一个,可以省略参数的括号;如果处理只有一行,那么可以省略Return等,如果把这些规则也用上,又可以省更多。// Arrow函数(省略括号以及Return的版本) const includedTaxPriceArrayWithArrowFunc = records.filter(record => record['税种'].value === '含税').map(record => record.price.value * 1.1);
const records = [ { $id: { value: 1, type: 'RECORD_NUMBER', }, '税种': { value: '含税', type: 'RADIO_BUTTON', }, '合计': { value: '10000', type: 'NUMBER', }, }, { $id: { value: 2, type: 'RECORD_NUMBER', }, '税种': { value: '不含税', type: 'RADIO_BUTTON', }, '合计': { value: '20000', type: 'NUMBER', }, }, { $id: { value: 3, type: 'RECORD_NUMBER', }, '税种': { value: '含税', type: 'RADIO_BUTTON', }, '合计': { value: '30000', type: 'NUMBER', }, }, ]; // 税种字段的值为[含税]或者[不含税] // 合计字段的值为金额。 // 用Arrow函数的方法 const includedTaxPriceArrayWithArrowFunc = records.filter(record => record['税种'].value === '含税').map(record => record['合计'].value * 1.1); console.log(includedTaxPriceArrayWithArrowFunc);
不用return的那部分,可很方便地定义可返回新函数的函数(柯里化)。 ... spread opereator等请看后续
const contracts = [ { '合同名称': { 'type': 'SINGLE_LINE_TEXT', 'value': '合同C' }, '费用': { 'type': 'NUMBER', 'value': '50000' }, '商品ID': { 'type': 'NUMBER', 'value': '1' }, '客户ID': { 'type': 'NUMBER', 'value': '3' }, '日期': { 'type': 'DATE', 'value': '2019-08-31' }, '$id': { 'type': '__ID__', 'value': '3' } }, { '合同名称': { 'type': 'SINGLE_LINE_TEXT', 'value': '合同B' }, '费用': { 'type': 'NUMBER', 'value': '150000' }, '商品ID': { 'type': 'NUMBER', 'value': '2' }, '客户ID': { 'type': 'NUMBER', 'value': '4' }, '日期': { 'type': 'DATE', 'value': '2019-08-30' }, '$id': { 'type': '__ID__', 'value': '2' } }, { '记录编号': { 'type': 'RECORD_NUMBER', 'value': '1' }, '合同名称': { 'type': 'SINGLE_LINE_TEXT', 'value': '合同A' }, '费用': { 'type': 'NUMBER', 'value': '100000' }, '商品ID': { 'type': 'NUMBER', 'value': '3' }, '客户ID': { 'type': 'NUMBER', 'value': '1' }, '日期': { 'type': 'DATE', 'value': '2019-08-29' }, '$id': { 'type': '__ID__', 'value': '1' } } ]; const customers = [ { '客户名称': { 'type': 'SINGLE_LINE_TEXT', 'value': '样品公司' }, '$id': { 'type': '__ID__', 'value': '5' } }, { '客户名称': { 'type': 'SINGLE_LINE_TEXT', 'value': '金枪鱼水产' }, '$id': { 'type': '__ID__', 'value': '4' } }, { '客户名称': { 'type': 'SINGLE_LINE_TEXT', 'value': '样品株式会社' }, '$id': { 'type': '__ID__', 'value': '3' } }, { '客户名称': { 'type': 'SINGLE_LINE_TEXT', 'value': '街田商社' }, '$id': { 'type': '__ID__', 'value': '2' } }, { '客户名称': { 'type': 'SINGLE_LINE_TEXT', 'value': 'AA物流公司' }, '$id': { 'type': '__ID__', 'value': '1' } } ]; const products = [ { '商品名称': { 'type': 'SINGLE_LINE_TEXT', 'value': '电脑' }, '$id': { 'type': '__ID__', 'value': '3' } }, { '商品名称': { 'type': 'SINGLE_LINE_TEXT', 'value': '商用冰箱' }, '$id': { 'type': '__ID__', 'value': '2' } }, { '商品名称': { 'type': 'SINGLE_LINE_TEXT', 'value': '大型电视' }, '$id': { 'type': '__ID__', 'value': '1' } } ]; // 假设分别有合同应用的记录、与该应用关联起来的客户应用的记录和商品应用的记录。 // 创建一个函数用于返回将多条记录结合起来的函数 const joinRecordsCreator = (leftRecords) => (rightRecords, joinKey) => leftRecords.map(leftRecord => ({...rightRecords.find((rightRecord) => rightRecord[joinKey].value === leftRecord.$id.value), ...leftRecord})) // 上面是将Contracts和其他应用的记录结合起来的函数 const joinContracts = joinRecordsCreator(contracts); // 获取使用joinContracts函数将Contracts和Customers结合起来的数组 const contractsJoinedWithCustomers = joinContracts(customers, '$id'); // 获取使用joinContracts函数将Contracts和Products结合起来的数组 const contractsJoinedWithProducts = joinContracts(products, '$id'); console.log(contractsJoinedWithCustomers); console.log(contractsJoinedWithProducts);
可固定this
在kintone的JS自定义过程中,应该会碰到在操作DOM元素时需要访问this的情况。
一般的函数定义需要注意“this”是指什么,但是arrow函数的话,this是固定的,因此不会引起混乱。
取而代之,使用currentTarget等可以获取特定的元素本身。// 一般情况下的函数,this是指按钮本身 $('button').click(function() { console.dir(this); // 打印button元素。 }); // 用箭头函数时,因为this是固定的,因此不是指button元素。 // 要获取按钮元素,需要使用回调的参数的currentTarget $('button').click((e) => { console.dir(e.currentTarget); // 打印button元素。 });
Async/Await来替代Promise【不支持IE】
要从多个应用中获取数据等情况,需要使用Promise进行同步处理。
Promise是在.then()内写要执行的处理,对于不太习惯层层嵌套的人来说可能不太直观。
下面例子是从某个应用中获取4条数据,然后求合计。
例) 获取4条数据并求和(Promise)
※ 一般情况下使用批量获取记录的API比较合理,这里作为Promise的范例,特意用一条条获取的方法。(function() { 'use strict'; // 获取4条数据并求和 // 省略params1-4。 // kintone通过返回Promise,可让保存前处理等处理待机 kintone.events.on(['app.record.create.submit', 'app.record.edit.submit'], function(event) { return new kintone.Promise(function(resolve, reject) { return kintone.api('/k/v1/record', 'GET', params1); }).then(function(resp1) { // 应答内容放入resp1。等待处理结束后再执行后续处理 return kintone.api('/k/v1/record', 'GET', params2); }).then(function(resp2) { // 应答内容放入resp2。等待处理结束后再执行后续处理 return kintone.api('/k/v1/record', 'GET', params3); }).then(function(resp3) { // 应答内容放入resp3。等待处理结束后再执行后续处理 return kintone.api('/k/v1/record', 'GET', params4); }).then(function(resp4) { // 应答内容放入resp4。等待处理结束后再执行后续处理 // 到这里获取数据的处理已经全部结束了 // 对resp1 - 4求和并将和合计值输出到“合计字段” event.record.总计.value = resp1.record.合计.value + resp2.record.合计.value + resp3.record.合计.value + resp4.record.合计.value; resolve(event); // resolve返回promise处理完成 }); }); })();
下面是替代上述的Promise写法,而使用Async/Await来写,使用了通常的函数。
并不是说 哪个对哪个错,而是跟用多个Promise或用复杂的语法相比,使用Async/Await的写法会比较直观。
例) 获取4条数据并求和(Async/Await)
(() => { 'use strict'; // 获取4条数据并求和 // 省略params1-4。 kintone.events.on(['app.record.create.submit', 'app.record.edit.submit'], async function(event) { // 加上async // 需要等待的处理要加上await const resp1 = await kintone.api('/k/v1/record', 'GET', params1); // 应答内容放入resp1。等待处理结束后再执行后续处理 const resp2 = await kintone.api('/k/v1/record', 'GET', params2); // 应答内容放入resp2。等待处理结束后再执行后续处理 const resp3 = await kintone.api('/k/v1/record', 'GET', params3); // 应答内容放入resp3。等待处理结束后再执行后续处理 const resp4 = await kintone.api('/k/v1/record', 'GET', params4); // 应答内容放入resp4。等待处理结束后再执行后续处理 // 到这里获取数据的处理已经全部结束了 // 对resp1 - 4求和并将和合计值输出到“合计字段” event.record.总计.value = resp1.record.合计.value + resp2.record.合计.value + resp3.record.合计.value + resp4.record.合计.value; return event; }); })();
但是,要注意的是:加了async后会返回PromiseObject,不支持Promise的事件句柄里,不用立即执行函数包住会报错。
通过解构赋值实现智能的变量提取方法【不支持IE】
这个功能也特别的方便。一般情况下,要取出kintone.events.on()中的record变量,需要这样:
在kintone.events.on()中用event.record取出record变量(通常情况)
kintone.events.on('app.record.create.submit', (event) => { const record = event.record; console.log(record); });
用解构赋值的方法不需要写两遍“record”。
在kintone.events.on()中从event.record里取出record变量(解构赋值)
kintone.events.on('app.record.create.submit', (event) => { const {record} = event; console.log(record); });
像这样可以节省了写两边“record”的麻烦。
甚至还可以如下获取record下的price和customer字段。kintone.events.on('app.record.create.submit', (event) => { const {record} = event; const {price, customer} = record; console.log(price, customer); });
还可以先取出price和customer。
kintone.events.on('app.record.create.submit', (event) => { const {record:{price, customer}} = event; console.log(price, customer); });
并不是要求一定要这样写,但是这样写可以减少代码量,看起来更加清晰。如果像让你的代码变得更加优雅,不妨可以试一试。
使用展开语法展开【不支持IE】
如果习惯了展开语法也特别的方便。展开语法用3个点[ ... ]来表示,可以展开数组、函数的参数、对象。
展开语法用于数组时
对kintone的记录数组(records数组)进行展开并合并
const records1 = [ { '合同名称': { 'type': 'SINGLE_LINE_TEXT', 'value': '合同C' }, '费用': { 'type': 'NUMBER', 'value': '50000' }, '日期': { 'type': 'DATE', 'value': '2019-08-31' }, '$id': { 'type': '__ID__', 'value': '3' } }, { '合同名称': { 'type': 'SINGLE_LINE_TEXT', 'value': '合同B' }, '费用': { 'type': 'NUMBER', 'value': '150000' }, '日期': { 'type': 'DATE', 'value': '2019-08-30' }, '$id': { 'type': '__ID__', 'value': '2' } }, ]; const records2 = [ { '合同名称': { 'type': 'SINGLE_LINE_TEXT', 'value': '合同A' }, '费用': { 'type': 'NUMBER', 'value': '100000' }, '日期': { 'type': 'DATE', 'value': '2019-08-29' }, '$id': { 'type': '__ID__', 'value': '1' } } ]; // 将记录数records1和records2合并成一个数组 const resultRecords = [...records1, ...records2]; console.log(resultRecords);
也可将其与Array.prototype.concat()结合使用,但这更加直观!
如果records1、records2内分别有三个元素,那么[...records1, ...records2]这样写和以下写法基本同一个意思:
[records1-1, records1-2, records1-3, records2-1, records2-2, records2-3]
如果理解为用...将数组展开的话,应该就好懂了。
展开语法用于函数的参数时
用于函数的参数时,可支持变量的参数。
以下是对多条合同记录的合计金额求和的函数
const record1 = { '合同名称': { 'type': 'SINGLE_LINE_TEXT', 'value': '合同C' }, '费用': { 'type': 'NUMBER', 'value': '50000' }, '日期': { 'type': 'DATE', 'value': '2019-08-31' }, '$id': { 'type': '__ID__', 'value': '3' } }; const record2 = { '合同名称': { 'type': 'SINGLE_LINE_TEXT', 'value': '合同B' }, '费用': { 'type': 'NUMBER', 'value': '150000' }, '日期': { 'type': 'DATE', 'value': '2019-08-30' }, '$id': { 'type': '__ID__', 'value': '2' } }; const record3 = { '合同名称': { 'type': 'SINGLE_LINE_TEXT', 'value': '合同A' }, '费用': { 'type': 'NUMBER', 'value': '100000' }, '日期': { 'type': 'DATE', 'value': '2019-08-29' }, '$id': { 'type': '__ID__', 'value': '1' } }; const sumContractPriceRecords = (...records) => { // 参数records为数组 return records.reduce((sum, record) => sum + Number(record.费用.value), 0); }; // 求record1,2,3的合计的总和 const sum = sumContractPriceRecords(record1, record2, record3); console.log(sum);
展开语法用于对象时
可以像数组一样展开对象,因此可以如下结合对象。
const contract = { '合同名称': { 'type': 'SINGLE_LINE_TEXT', 'value': '合同C' }, '费用': { 'type': 'NUMBER', 'value': '50000' }, '日期': { 'type': 'DATE', 'value': '2019-08-31' }, '$id': { 'type': '__ID__', 'value': '3' } }; const customer = { '客户名称': { 'type': 'SINGLE_LINE_TEXT', 'value': '样品公司' }, '$id': { 'type': '__ID__', 'value': '5' } }; // 将客户信息的customerRecord和合同信息的contractRecord结合 // 此时,如果发生$id等冲突时,以后者为优先 const joinedRecord = {...contract, ...customer}; console.log(joinedRecord);
这个也是跟数组一样,类似于将contractRecord和customerRecord展开。
安全无副作用的代码写法
编程中,状态被改变称作为副作用。代码尽量减少副作用可以避免缺陷。用语言比较难表达,我们来看实际例子。
// 假设有条kintone的记录含有price字段 const record = { $id: { type: 'RECORD_NUMBER', value: 1, }, price: { type: 'NUMBER', value: 1000, }, }; // 以下函数用于计算含消费税(10%) const addTax = (record) => { record.price.value = record.price.value * 1.1; return record; }; // 保存已经计算了消费税的record const addedTaxRecord = addTax(record); // 对比计算了消费税的record和计算前的record的值,看结果如何? console.log('含税金额', addedTaxRecord.price.value); console.log('税前金额', record.price.value);
一眼看上去,感觉上述代码会在console中显示含税金额(1100)和税前金额(1000)。但是实际如下显示。
竟然两者都变成含税金额(1100)了。这个就是“对原始变量产生副作用”的状态。像这样,如果不随时注意是否可能产生副作用,就会发生意想不到的缺陷。
这个是JavaScript的变量总是去访问值而引起的现象。要回避这个有几个方法。
IE之外的浏览器时
使用SpreadOperator可以如下改写addTax函数。创建新的Object并返回。
// 假设有条kintone的记录含有price字段 const record = { $id: { type: 'RECORD_NUMBER', value: 1, }, price: { type: 'NUMBER', value: 1000, }, }; // 以下函数用于计算含消费税(10%)(不起副作用的写法) const addTax = (record) => { return { ...record, // 展开record数组 price: { // 覆盖price ...record.price, // 要保留type等,因此也要展开price内容 value: record.price.value * 1.1, // 仅覆盖value }, }; }; // 保存已经计算了消费税的record const addedTaxRecord = addTax(record); // 对比计算了消费税的record和计算前的record的值,看结果如何? console.log('含税金额', addedTaxRecord.price.value); console.log('税前金额', record.price.value);
IE时
可以采用先复制Objec的方法,或者使用immer以及immutable.js等库来保证旧数据不改变的情况下对数据进行操作的方法。
不使用jQuery单纯用JS操作DOM
要对DOM元素进行操作时,当然可以使用jQuery,但是假设你只想对一个DOM元素进行操作,特意导入jQuery的话就太累赘了。下面介绍单靠JS对DOM进行操作的方法。
获取元素
使用document.querySelector() / querySelectorAll() 可以获取元素。/* 要获取元素id = foo的元素时 */ // jQuery const elementJquery = $('#foo'); // JavaScript const element = document.querySelector('#foo'); // 要获取Class等多个元素时使用querySelectorAll const elements = document.querySelectorAll('.foo'); console.log('获取到的元素的text: ', elementJquery.text()); console.log('获取到的元素的text: ', element.innerText); console.log('获取到的元素的text: ', [...elements].map(e => e.innerText));
※ 当然也可以使用document.getElementById(),但是使用上述的方法不用管是class还是id,使用querySelector都可以取。
更改获取到的元素的Style
用元素.style.属性 = "值"; 的写法可以更改元素的style。
可使用CSS可设置的style。// 将id: foo的元素的文字改为红字粗体 const element = document.querySelector('#foo'); element.style.color = "red"; element.style.fontWeight = "bold";
当然,也可以对用kintone.app.record.getFieldElement获取的元素进行同样更改。
导入TypeScript
在使用TypeScript来进行kintone自定义开发(日语)中也介绍了,kintone表格的Record结构相对比较复杂,
因此使用TypeScript的话可以减少弄错对象的key以及类型。
使用Visual Studio Code等的IDE,会自动补全代码。如果今后从事JavaScript自定义的机会比较多,推荐您导入。
最后
综上所述,JavaScript在不断演变,使编程越来越轻松。
将const/let、Spread语法以及Arrow函数等组合使用,或积极使用一些其他新功能,可以大大减少缺陷,使你的代码无副作用更加优秀。请务必尝试一下!