# Javascript
# 基础
# Reflect
Reflect 暴露语言内部方法
// 老写法 Function.prototype.apply.call(Math.floor, undefined, [1.75]); // 1 // 新写法 Reflect.apply(Math.floor, undefined, [1.75]); // 1
未定义属性,不报错,返回 false
让 Object 命令式操作变为函数行为
// 老写法 "assign" in Object; // true // 新写法 Reflect.has(Object, "assign"); // true
保留 Proxy 的所有默认行为
Proxy(target, { set: function (target, name, value, receiver) { var success = Reflect.set(target, name, value, receiver); if (success) { console.log("property " + name + " on " + target + " set to " + value); } return success; }, });
# script 标签
六属性:async(异步脚本) defer(延迟脚本) src type(MIME 类型) crossorigin
nomodule:true 表示这个脚本在支持 ES2015 modules 的浏览器中不执行,用来为不支持 es6 浏览器提供回退版本。
crossorigin:使那些将静态资源放在另外一个域名的站点打印错误信息
# defer 和 async
同:都不会阻塞页面的渲染 defer:通知浏览器该脚本将在文档完成解析,DOM 构建完成后后,触发 DOMContentLoaded 事件前执行。有 defer 属性的脚本会阻止 DOMContentLoaded 事件,直到脚本被加载并且解析完成。 async:浏览器不会因 async 脚本而阻塞,脚本在文档中的顺序不重要 —— 先加载完成的先执行。
# 模块化规范
# AMD
异步模块,加载完执行回调函数
// utils.js
define([], function () {
return {
add: function (a, b) {
console.log(a, b);
},
};
});
// main.js 先加载utils,然后执行add
require(["./utils"], function (utils) {
utils.add(1, 2);
});
# CMD
按需加载
define(function (require, exports, module) {
console.log(1);
if (false) {
var utils = require("./utils");
utils.add(1, 2);
}
});
# UMD
兼容 AMD 和 CMD
(function (root, factory) {
if (typeof define === "function" && define.amd) {
// AMD
define(["utils"], factory);
} else if (typeof exports === "object") {
// commonjs
let utils = require("utils");
module.exports = factory(utils);
} else {
// 都不是
root.result = factory(root.utils);
}
})(this, function (utils) {
utils.add(1, 2);
});
# CommonJS
避免全局变量污染,类似于命名空间
- 同步加载模块,运行时加载
- 每个 js 文件是一个模块,每个模块内部都有一个 module 变量,代表当前模块。 module.exports
let num = 1;
function add(x) {
return num + x;
}
exports.num = num; //exports即 module.exports;
module.exports.add = add;
- 加载某一个模块,就是加载这个模块的 module.exports 属性 ,require 的基本功能是读取并执行 JS 文件,如果模块导出的是一个函数,就不能定义在 exports 对象上。
require 加载时,会执行模块中的代码,然后将模块的 module.exports 属性作为返回值进行返回。
require 属性:
- resolve:需要解析的模块路径。
- main:Module 对象,表示当进程启动时加载的入口脚本。
- extensions:如何处理文件扩展名。
- cache:被引入的模块将被缓存在这个对象中。
当我们在一个项目中多次 require 同一个模块时,CommonJS 并不会多次执行该模块文件;
而是在第一次加载时,将模块缓存;
以后再加载该模块时,就直接从缓存中读取该模块.可以通过delete require.cache[modulePath]
将缓存的模块删除。
require ={
...,
cache:{
'D:\\demo\\main.js': Module {},
'D:\\demo\\number.js': Module {}
}
}
# EJS
import 编译时加载,会优先执行(变量提升),是引用,是只读引用。也有缓存。
export const utils = {
add: function (a, b) {
console.log(a + b);
},
};
import { utils } from "./utils";
utils.add(1, 2);
# 区别
通过上面我们对 CommonJS 规范和 ES6 规范的比较,我们总结一下两者的区别:
- CommonJS 模块是运行时加载,ES6 模块是编译时输出接口
- CommonJS 模块输出的是一个值的复制,ES6 模块输出的是值的引用
- CommonJS 加载的是整个模块,即将所有的方法全部加载进来,ES6 可以单独加载其中的某个方法
- CommonJS 中 this 指向当前模块 module.exports,ES6 中 this 指向 undefined
- CommonJS 默认非严格模式,ES6 的模块自动采用严格模式
# 错误类型
- SyntaxError: 语法错误
- ReferenceError: 引用错误 要用的东西没找到
- RangeError: 范围错误 专指参数超范围
- TypeError: 类型错误 错误的调用了对象的方法
- EvalError: eval()方法错误的使用
- URIError: url 地址错误
# 原型链
为什么要有原型链?
JavaScript 采用原型编程,所有对象都能共享原型上的方法,节省内存;
同时基于原型这一实现思想,JavaScript 通过找对原型链,方便地实现了继承。
# 基本类型
typeof 判断基础数据类型
Object.prototype.toString 判断某个对象属于哪种内置类型
instanceOf 检测 constructor.prototype 是否存在于参数 object 的原型链上。
Object.prototype.toString.call(null); // "[object Null]"
Object.prototype.toString.call(undefined); // "[object Undefined]"
Object.prototype.toString.call(“abc”);// "[object String]"
Object.prototype.toString.call(123);// "[object Number]"
Object.prototype.toString.call(true);// "[object Boolean]"
function typeOf(obj) {
return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase()
}
typeOf([]) // 'array'
typeOf({}) // 'object'
typeOf(new Date) // 'date'
# 装箱拆箱
# 装箱
将基本数据类型转换为对应的引用类型。
隐式装箱:
var s1 = "hello"; //隐式装箱
var s2 = s1.substring(2);
// 执行过程:
// 1. 创建String类型的一个实例; var s1 = new String('call_me_R');
// 2. 在实例中调用substring方法 var s2 = s1.substring(2);
// 3. 销毁该实例 s1 = null;
显示装箱: 通过基本包装类型对象对基本类型进行显示装箱。
var objStr = new String("call_me_R");
objStr.job = "frontend engineer";
objStr.sayHi = function () {
console.log("hello kitty");
};
console.log(objStr.job); // frontend engineer
objStr.sayHi(); // hello kitty
// 在执行流离开当前作用域之前一直保留在内存中
# 拆箱
拆箱是指把引用类型转换成基本的数据类型。通常通过引用类型的 valueOf()和 toString()方法来实现。
valueOf 和 toString 返回值的区别:
valueOf 按值的类型返回值;
toString 按 string 类型返回值。
# ES6+
- at 获取数组的倒数项 arr.at(-1)
- Object.hasOwn() 自身是否有该属性。 obj.hasOwnProperty 缺点:可被覆盖;Object.create(null)创建的对象无该方法。
- 数组后序查找 findLast,findLastIndex
# 箭头函数
const fn = () => {};
fn.__proto__ === Function.prototype; //true
fn.prototype === undefined; //true,因此不能作为构造函数
箭头函数不会创建自己的 this,它只会从自己的作用域链的上一层继承 this;通过 call() 或 apply() 方法调用一个函数时,只能传递参数(不能绑定 this),他们的第一个参数会被忽略。
# 小数处理
- 四舍五入
.toFixed()
Math.round() - 不四舍五入 ~~ 将后面的字符转换成 Int 类型
~~"1.51"; // 1
# 可选链
?. 短路返回值是 undefined
obj?.prop;
obj?.[expr];
arr?.[index];
func?.(args);
# ||和??
?? 前面是 null、undefined 取后面的
|| 前面值转成布尔类型是 false 取后面的
# for … of
遍历可迭代对象的值(Array,Map,Set,String,TypedArray,arguments,dom 集合,生成器等),可以由 break,throw,continue 或 return 终止
可迭代对象:该对象(或者它原型链上的某个对象)必须有一个键为 @@iterator 的属性(@@表示 Symbol),可通过常量 Symbol.iterator 访问该属性
const str = "str";
str[Symbol.iterator](); // 返回Iterator 对象
const obj = {};
console.log(obj[Symbol.iterator]); //undefined
# for … in
遍历对象的所有除 Symbol 以外的可枚举属性的 key,包括继承的可枚举属性
// 仅遍历自有属性
const obj = {};
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
console.log(`obj.${prop} = ${obj[prop]}`);
}
}
# Promise
# 用法
Promise.reject("error")
.then(
() => {
console.log("success1");
},
() => {
console.log("error1");
}
)
.then(
() => {
console.log("success2");
},
() => {
console.log("error2");
}
);
// 结果:
// error1
// success2
Promise.reject("error")
.then(() => {
console.log("success1");
})
.catch((err) => {
console.log(err);
})
.then(
() => {
console.log("success2");
},
() => {
console.log("error2");
}
);
// 结果:
// error
// success2
# 数据处理
初始化数组
new Array(3) //[empty*3]
new Array(3).fill('') //['','','']
new Array(1,2) //[1,2]
[empty*3] 稀疏数组,有预留长度为 3,但实际是空的,无法遍历
数组去除空值
const arr = [1, 2, 0, undefined, 11, "12", null];
console.log(arr.filter((i) => i)); // [1, 2, 11, '12']
数组去重 ES5
function unique(arr) {
var res = arr.filter(function (item, index, array) {
return array.indexOf(item) === index;
});
return res;
}
对象数组根据某一属性去重
let amap = new Map();
data = data.filter((a) => !amap.has(a.value) && amap.set(a.value, 1));