1、currentTarget 和 target
target在事件流的目标阶段,指被点击的对象;currentTarget在事件流的捕获、目标及冒泡阶段,指向事件绑定的元素(一般为父级元素)
只有当事件流处在目标阶段的时候,两个的指向才是一样的。
2. 模块化
CommonJS
Node 会把整个待加载的 .js文件放入一个包装函数load中执行。在执行这个load()函数前,Node事先准备好了module对象. module中有一个空的exports变量。
var load = function (exports, module) {
// .js的文件内容
...
// load函数返回:
return module.exports;
};
var exported = load(module.exports, module);
- commonJS 用
同步的方式加载模块- 系统模块
- 自定义模块
- 第三方模块
- module.exports
module.exports = {}更改指向后也没关系,因为获取数据是根据变量来的
- exports
- 初始时与 module.exports 指向同一块内存地址
- 在使用 module.exports = {} 或 exports = {} 后会更改指向
- require
- 导入的是
module.exports对象
- 导入的是
//1.js
module.exports.name = 'Tom'
exports.age = 18
console.log(module.exports) //{ name: 'Tom', age: 18 }
console.log(exports) //{ name: 'Tom', age: 18 }
//2.js
module.exports = { name: 'Tom' }
exports.age = 18
console.log(module.exports) //{ name: 'Tom' }
console.log(exports) //{ age: 18 }
const x = require('./2');
console.log(x); //{ name: 'Tom' }
require查找规则# 45.CommonJS详解
- 导入一个 Node 核心模块,比如:path、http
- 直接返回核心模块,并且停止查找
- 是以 ./ 或 ../ 或 / 开头的
- 将路径当做一个文件在对应的目录下查找
- 如果有后缀名,按照后缀名的格式查找对应的文件
- 如果没有后缀名,会按照如下顺序:
- ①、直接查找同名文件
- ②、查找
.js文件 - ③、查找
.json文件 - ④、查找
.node文件
- 没有找到对应的文件,将路径作为一个目录
- 查找目录下面的 index 文件
- ①、查找
x/index.js文件 - ②、查找
x/index.json文件 - ③、查找
x/index.node文件
- ①、查找
- 查找目录下面的 index 文件
- 如果没有找到,那么报错:
not found
- 将路径当做一个文件在对应的目录下查找
- 不是核心模块,也不是一个路径
- 会先在当前文件所在目录下的
node_modules目录下寻找 - 如果没有找到,再去上级目录下的 node_modules 目录下寻找
- 如果依然没找到,继续去上上级目录下的 node_modules 目录下寻找
- 直至找到根目录,依然没有找到,则报错
not found
- 会先在当前文件所在目录下的
AMD
- AMD规范采用异步方式加载模块,模块的加载不影响它后面语句的运行
- 使用 require.js 的步骤
define()定义模块require.config()指定引用路径等require()加载模块。
# Javascript模块化编程(三):require.js的用法
//math.js
// 定义模块
define(function() {
return { add: function(x, y){ return x + y; } }
})
// 定义模块。依赖underscore模块,将模块放在[]内
define(['underscore'], function(_) {
return { reduce: function(){} };
})
// 引用模块。执行业务代码
require(['jquery', 'math'], function($, math) {
var sum = math.add(10,20);
$("#sum").html(sum);
});
//main.js(主模块)
// config()指定各模块路径和引用名
require.config({
baseUrl: "js/lib",
paths: {
"jquery": "jquery.min", //实际路径为js/lib/jquery.min.js
"underscore": "underscore.min",
}
});
// 引用模块。执行业务代码
require(["jquery", "underscore", "math"], function($, _, math) {
math.add(1, 2)
});
<script src="js/require.js" data-main="js/main"></script>
CMD
- AMD 推崇依赖前置、提前执行;CMD推崇依赖就近、延迟执行
ES6 Module
编译时加载的好处:
- 可以对模块进行静态分析(宏、类型检测)
- 不需要
UMD模块规范了
关键字
- export
- export default
- import
import命令会被 JavaScript 引擎静态分析,在编译时就引入模块代码,而不是在代码运行时加载,所以无法实现条件加载。也正因为这个,使得静态分析成为可能。
- as
//1.js
function a() {}
// export a; //①、❎ Declaration or statement expected. 需要是一个声明或表达式
// export const b = 10 //②、✅
// export { a }; //③、✅
// export { a as c }
export default a; //④、✅
//export default { a }; //⑤、✅
//2.js
import a from './1';
console.log('a =', a) // ②③、a = undefined
console.log('a =', a) // ④、a = function{}
console.log('a =', a) // ⑤、a = Object({ function{} })
ES6的模块不是对象。无法用变量 a 接收到对象,而是在编译阶段对模块导出数据进行同名赋值。
# SyntaxError: Cannot use import statement outside a module
ES6 模块与 CommonJS 模块的差异
- CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
- CommonJS 模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。
- ES6 模块的运行机制与 CommonJS 不一样。JS 引擎对脚本静态分析的时候,遇到模块加载命令
import,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。换句话说,ES6 的import有点像 Unix 系统的“符号连接”,原始值变了,import加载的值也会跟着变。因此,ES6 模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。
- CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
- 运行时加载: CommonJS 模块就是对象;即在输入时是先加载整个模块,生成一个对象,然后再从这个对象上面读取方法,这种加载称为“运行时加载”。
- 编译时加载: ES6 模块不是对象,而是通过
export命令显式指定输出的代码,import时采用静态命令的形式。即在import时可以指定加载某个输出值,而不是加载整个模块,这种加载称为“编译时加载”。
CommonJS 加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。
3. 加法精度问题
使用 antd 的 InputNumber 组件时,动态设置 min、max 属性,因为加法的精度问题,导致数据越界。
解决:
<InputNumber
min={minValue ? (minValue * 100 + 1) / 100 : ((maxValue || 0) * 100 + 1) / 100}
max={1000}
precision={2}
step={0.01}
style={{width: 150}}
/>
4. Array().fill({})
注意:数组的每个元素指向的是相同的对象。
let arr1 = new Array(3).fill({ a: 0 });
arr1[1].a = 1;
console.log(arr1); // [{a: 1}1, {a: 1}, {a: 1}]
let arr2 = new Array({ a: 0}, { a: 0 }, { a: 0});
arr2[1].a = 1;
console.log(arr2); // [{a: 0}, {a: 1}, {a: 0}]
5. 删除对象/数组中 null、undefined
# js删除对象/数组中null、undefined、空对象及空数组方法示例
- 方案①:递归处理
export const delEmptyQueryNodes = (obj = {}) => {
Object.keys(obj).forEach((key) => {
let value = obj[key];
//递归
value && typeof value === 'object' && delEmptyQueryNodes(value);
(value === null || value === undefined) && delete obj[key];
});
return obj;
};
- 方案②:借助数组的方法(以下代码未处理多层级)
// filter 过滤有数据的 key;reduce 可以换成 forEach 等遍历的方法
const params = Object.keys(data)
.filter((key) => data[key] !== null && data[key] !== undefined)
.reduce((acc, key) => ({ ...acc, [key]: data[key] }), {});
6. 字符串转换为数字
/** 浮点数字符串 */
parseInt('1.3') //1
Number('1.3') //1.3
+'1.3' //1.3
parseFloat('1.3') //1.3
Math.floor('1.3') //1
~~'1.3' //1
'1.3' * 1 //1.3
/** 中间包含字母 */
parseInt('1.f3') //1
Number('1.f3') //NaN
+'1.f3' //NaN
parseFloat('1.f3') //1
Math.floor('1.f3') //NaN
~~'1.f3' //0
'1.f3' * 1 //NaN
/** 头部包含字母 */
parseInt('1.f3') //NaN
Number('1.f3') //NaN
+'1.f3' //NaN
parseFloat('1.f3') //NaN
Math.floor('1.f3') //NaN
~~'1.f3' //0
'1.f3' * 1 //NaN