本文将系统梳理JavaScript知识体系,以分类总结、思维导图,问答的形式带你了解Javascript世界的全貌。


了解一下JavaScript历史,有助于学习理解JavaScript。 推荐阅读 《JavaScript 语言的历史》
为什么WebApp没有原生APP体验好?
字符集
JavaScript 是采用 unicode字符集编写,但是只能支持一种编码 UCS-2。UCS-2可以理解为2个字节的UTF-16编码 这也是JavaScript不能正确获取中文字符串长度的原因,es5及之前的字符串方法都有该问题, es6新增了许多字符串方法都支持4字节字符。
更多请阅读《JavaScript与字符集编码》
JavaScript区分大小写
标识符
标识符 必须以字母、 _、 $ 开头 后续字符可以是数字、字母、 _、 $
可选的分号
"("、"["、"/"、"+"、"-" 或 ` 开始,那么它极有可能和前一条语有合在一起解析。return break continue 和随后的表达式之间不能有换行注释
// 单行注释/**/ 多行注释垃圾回收
语言特性
JavaScript是一门解释型、动态类型、弱类型、多范式编程、基于原型、单线程、跨平台的高级语言 更多解释
词法作用域
JavaScript采用词法作用域,通过阅读包含变量定义在内的数行源码就能知道变量所在的作用域
补充知识点,有两个例外
- new Function('代码片段') 会在全局作用域下执行
- eval别名使用 如
var eva = eval; eva('代码片段')会在全局作用域下执行
window对象CatchBlock作用域、With Block 作用域、Closure 作用域js// 以下代码在非严格模式中都是全局作用域,因为变量被挂在到window对象下面
var a = 1;
b = 2;
function fa (){
c = 3;
}
fa();
// 变量 a b c 和 fa都被挂在到window下面
关于作用域 更多内容推荐阅读 《JS的9种作用域》
使用严格模式的好处
如何使用严格模式
<script type='module'>具体内容
| - | 影响 |
|---|---|
| 禁止的语法 | 1. 禁止使用with语句 2. 禁止使用八进制直接量( 0开头的不行,0o可以)3. 变量必须声明才能使用 4. 新增了一些保留字 |
| 对象的影响 | 对象属性(可写性、枚举行、值、存取器)相关违规操作 , 对象相关(可扩展性)的违规操作 都会报错, 如删除已封锁或冻结的对象的属性;对只读属性的修改(包括存取器) 如修改 Set的size属性;delete运算符后跟随非法的标识符(比如变量、函数、函数参数)时,将会抛出一个类型错误异常(包括声明的变量及核心的客户端属性) |
| 对函数的影响 | 1. 禁止同名参数; 2. 限制了对调用栈的检测能力; arguments.callee、arguments.callee.caller3. arguments不再有魔术行为4. 标识符 eval和arguments当做关键字,它们的值是不能修改,也不能声明为变量5. eval限制在当前作用域6.尾调用优化只在严格模式下开启 |
| 对this的影响 | 1. 全局代码块、函数调用(不是方法调用)this默认指向window 2. call/apply传入null/undefined保持原样不被转换为window |
补充:ES6函数的严格模式
ES2016 做了一点修改,规定只要函数参数使用了默认值、解构赋值、或者扩展运算符,那么函数内部就不能显式设定为严格模式,否则会报错
原因
注: 无声明直接赋值(严格模式已禁用)
js// var和function声明变量的问题
var a = 1
var a;// 可以重复声明 a仍是1
console.log(b); undefined
var b = 2;
fa(); // 函数声明语句 变量声明和函数体均提前
function fa() {
return 3
}
console.log(bb); // function声明优先级高, 这里打印的是function
var bb = 3;
function bb() {
return 4;
}
// 编译阶段虽然丢弃了var声明,但是在执行阶段赋值语句bb=3还存在,所以结果是3
bb // 3
// var和function都会被泄漏为全局变量 并且delete不能删除
window.fa;// fa function
window.bb; // 3
// var在循环中可能会得到预期之外的值
for(var i = 0; i< 3; i++) {
setTimeout(() => {
console.log(i); // 打印三次 3
})
}
console.log(i); // 3 泄漏为全局变量
var tmp = new Date();
function f() {
console.log(tmp); // undefined // 5> 因声明提前导致的内层变量可能会覆盖外层变量。
if (false) {
var tmp = 'hello world';
}
}
f();
// 总结 var的问题 1> 全局变量污染 2> 重复声明 3> 泄漏为全局变量 4> 循环中得到非预期的值
// 5> 因声明提前导致的内层变量可能会覆盖外层变量。
块级作用域声明变量的方式不存在var和function声明的问题
块级作用域有个特性 暂时性死区, 它也使得typeof不是100%安全的操作了,它的存在 避免了内层变量覆盖外层变量的问题。
const
number、 string、boolean、 null、 undefined、 BigInt Symbol、object(狭义的对象) array function包装对象
jsvar s = 'hello world';
var word = s.substring(s.indexOf(' ')+1, s.length);
null与undefined的区别
undefined 和 null 都是基本数据类型undefined表示未定义,一般变量声明了但没有赋值时值为undefined;null代表的含义是空对象,表示程序运行预期之中的空值。null是JavaScript保留字,而undefined不是,这意味着可以使用undefined来作为一个变量名,这也是很危险的,通常通过void 0 或函数参数默认值 来安全的获取undefinedtypeof null等于 "object" null == undefined null !== undefinednull --> 0 undefined --> NaNnull 是原型链的终点 undefined 会触发函数默认值数值的表示
js0b1010; // 二进制表示
0o12; // 八进制
10; // 十进制
0xa; // 十六进制
// 科学计数法 [digits][.digits][(E|e)[+|-]digits]
1e+1
为什么0.1+0.2 > 0.3
typeof可以判断基本类型和函数类型Object.prototype.toString 可以判断对象类型和数组类型
Reflect.toStringinstanceofjstypeof 123 // "number"
typeof '123' // "string"
typeof false // "boolean"
typeof undefined // "undefined"
typeof null // "object"
typeof 12n // "bigint"
typeof Symbol() // "symbol"
typeof [] // "object"
typeof {} // "object"
typeof (() => {}) // "function"
// typeof 引用类型 除 函数外 都是 "object"
// 判断 null
var isNull = (variable) => typeof variable === 'object' && !variable;
// 判断 NaN
NaN == NaN; // false
NaN === NaN; // false
// 全局变量 isNaN 会将参数转换为nuber类型在判断
// es6 Math.isNaN 如果不是number类型一律返回false
var isNaN = (variable) => typeof variable === 'number' && variable !== variable;
// 判断Infinity
// 全局变量 isFinite 会将其转换为 number 类型 在判断是否是 Infinity, 不是返回false
// es6 Math.isFinite 不会进行类型转换,如果不是 number类型 一律返回 false
// 判断 0 和 -0 需要借助Infinity 或使用es6 Object.is
0 == -0 // true
0 === -0 // true
1/0; // Infinity
1/-0; // -Infinity
Object.is(0, -0); // false
js[
null, // [object Null]
undefined, // [object Undefined]
123, // [object Number]
false, // [object Boolean]
'abc', // [object String]
12n, // [object BigInt]
Symbol(), // [object Symbol]
[], // [object Array]
{}, // [object Object]
()=>{}, // [object Function]
/^a/, // [object RegExp]
new Date(), // [object Date]
new Set(), // [object Set]
new Map(), // [object Map]
new WeakSet(), // [object WeakSet]
new WeakMap(), // [object WeakMap]
].forEach(
(variable) => console.log(
Object.prototype.toString.call(variable)
)
)
js// L instanceof R 判断 L是否是 R的实例
function instanceOf(L, R) {
while (true) {
if (L.__proto__ === null) return false;
if (L.__proto__ === R.prototype) {
return true
} else {
L = L.__proto__;
}
}
}
// 因为JS是基于原型的继承, 所以以下结果为true
[] instanceof Object;
/^s/ instanceof Object;
(() => {}) instanceof Object;
{} instanceof Function;
Object instanceof Function;
// instanceof在判断iframe中对象时存在缺陷
// iframe.contentWindow.arr instanceof Array // false
JavaScript 是一门动态类型的语言,在运行时,变量可能发生类型转换
强制类型转换
Number()Boolean()String()!if语句会转换为falsy类型falsy类型false、0、-0、0n、""、NaN、null、undefined+字符串连接,a + b 如果a 是字符串那么b会转换成字符串再与a 进行运算+ - * /(加号排除字符串连接的情况)
{}(任意对象) 转字符串
{}(任意对象) 转数值
Symbol类型
BigInt
BigInt()Number()String() Boolean()Object.defineProperty,Object.defineProperties可以设置对象属性Object.getOwnPropertyDescriptor() 可以获取某个属性的描述对象控制对象状态的方法
Object.preventExtensions()可以防止对象扩展,即不能新增属性; 使用Object.isExtensible()判断对象是否可以扩展Object.seal()可以禁用对象配置 ,除了不能添加新属性,也不可以修改它的可配置性和可枚举性,存取性与值特性不能转换,但可写性可以从true配置成false);使用Object.isSeal()可以检测对象是否禁用配置Object.freeze()可以冻结对象, 即对象不能扩展,所有对象属性不能配置且不能修改值;使用Object.isFrozen()可以判断一个对象是否被冻结| - | 自身可枚举属性 | 自身不可枚举属性 | 自身Symbol属性 |
|---|---|---|---|
in | ✅ | ✅ | ✅ |
Object.prototype.hasOwnProperty()Object.hasOwn()更安全一些 | ✅ | ✅ | ✅ |
Object.prototype.propertyIsEnumerable() | ✅ | - | ✅ |
注: symbol属性也有 属性的四特征
| - | 自身可枚举属性 | 自身不可枚举属性 | 自身Symbol属性 | 继承的可枚举属性 |
|---|---|---|---|---|
for ... in | ✅ | - | - | ✅ |
Object.assign() | ✅ | - | ✅ | - |
JSON.stringify() | ✅ | - | - | - |
Object.keys()Object.values()Object.entries() | ✅ | - | - | - |
Object.getOwnPropertyNames() | ✅ | ✅ | - | - |
Object.getOwnPropertySymbols() | - | - | ✅ | - |
Reflect.ownKeys() | ✅ | ✅ | ✅ | - |
总结:
Object.assign() Reflect.ownKeys() Object.getOwnPropertySymbols()for ... in 会遍历继承的可枚举属性注: delete删除不存在的属性返回true (严格模式下报错)
以下情况 delete语句无效(非严格模式返回false, 严格模式下报错)
JSON.stringify的副作用

JSON.stringify的副作用,蓝色表示属性值发生了转换,红色表示属性丢失《JavaScript权威指南第6版》对JSON方法序列化的定义如下

Object.setPrototypeOf(b, a) 设置对象b的原型为a, 即 b.proto === aObject.getProtutypeOf(b) 获取对象b的原型var obj = {}Object() 或 new Object()Object.create()
a = Object.create(b) 即 a.__proto__ = ba = Object.create(b, prototype) 即 a.__proto__ = b a.XX = prototype.XXObject.create(null) 创建一个干净的对象,没有Object的实例方法Promise.any()方法使用Object.is 能识别 0和-0 及NaNObject.assgin(target, source1, source2, ...) 用于对象的合并,将原对象的所有自身可枚举属性复制到目标对象。它的特征如下
Object.assign({}, null, undefined, 12n, 23, false, Symbol.for('a'))JSON.stringify一样将get存取器转换为值Object.getProtypeOf、Object.setProtorypeOfObject.keys()、Object.values()、Object.entries()Object.fromEntries() 它是Object.entries()的逆操作JavaScript数组有以下特征
注: 数组也是一种特殊的对象,数组的索引即对象属性key
数组的两个特殊行为
稀疏数组是指数组的索引不连续, 即数组length值大于元素个数。
什么情况下会产生稀疏数组?
jsvar a = Array(5);
var b = [,,3];
var c = []; c.length = 5;
// 以上都会产生稀疏数组
// 注es6明确将数组空位明确为undefined 故es6 方法不会产生稀疏数组
var d = Array.from({length: 2});
[...['a',,'b']]; // [ "a", undefined, "b" ]
稀疏数组有什么影响呢?
forEach(), filter(), reduce(), every() 和some()都会跳过空位map()会跳过空位,但会保留这个值join()和toString()会将空位视为undefined,而undefined和null会被处理成空字符串。因为数组也是对象,使用Object.seal()封锁或Object.freeze()冻结数组后,则不能通过修改length属性删除元素
数组操作7个方法 会改变当前对象
push 尾部插入元素pop 尾部删除元素unshift 顶部插入元素shift 顶部删除元素splice 可以删除添加元素,返回删除的元素reverse 翻转sort 排序数组遍历
forEachmapfilterevery / somereduce / reduceRightindexOf / lastIndexOf其他数组方法
join 转字符串slice 复制数组因为数组也是对象,也可以使用对象的方法 如toString valueOf
静态方法
Array.from 将一个可遍历的对象转换为数组, 它还提供了类似map的功能(第二个参数是个函数,它又有两个参数 item, index)Array.of 弥补Array()因参数不同创建数组实例的行为差异实例方法
copeWithin()、Array.prototype.copyWithin(target, start = 0, end = this.length) 将索引[start, end)的元素复制到索引为target开始的元素上, 只能复制一次。find()、findIndex()、findLast()、findLastIndex()fill(item, start=0, end=this.length) 填充元素, 第二个第三个参数可以指定填充开始结束位置 [start, end)keys()、values()、entries()includes()flat()、flatMap() 将嵌套的数组拉平at() 让数组支持负的索引, 也可以用于字符串toReserved()、toSorted()、toSpliced()、with() 这些方法 不改变原数组而是返回一个原数组的copy, 目前处于提案状态group()、groupToMap() 对数组进行分组 返回一个对象或mapArray.prototype.[method].call使用数组原型的方法Array.from() or 扩展运算符 转换为数组对象类数组大都实现了 Iterator 接口,因此可以使用扩展运算符、解构赋值等。
关于模块化请移步这里《JavaScript模块化》
函数定义的两种方法
| - | 示例 | 特性 |
|---|---|---|
| 函数声明语句 | function(){} | 声明提前、函数体提前 |
| 函数定义语句 | var a = function(){} | 声明提前 |
Function Object Number Boolean使用new和不使用new效果一样
name属性length属性 受默认值、rest参数影响 用途:方法重载运行时的属性
arguments 获取函数参数
arguments.callee属性 返回它所对应的原函数
arguments.callee.caller属性 它指向当前正在执行函数的函数 非标准js// eval 在当前作用域内执行
eval('var a = 2');
a; // 2
// 严格模式下会创建eval作用域,以避免影响到外部作用域
(function f() {
'use strict';
eval('var foo = 123');
console.log(foo); // ReferenceError: foo is not defined
})()
jsvar a = 1;
function f() {
var a = 2;
var e = eval;
e('console.log(a)');
}
f() // 1
jsfunction myNew(Ctor, ...args) {
// 1. 创建一个新的空对象
// 2. 将这个空对象的__proto__指向构造函数的原型
const obj = Object.create(Ctor.prototype)
// 3. 将this指向空对象
const result = Ctor.apply(obj, args);
// 对构造函数返回值做判断,然后返回对应的值
return result instanceof Object ? result : obj;
}
jsFunction.prototype.myCall = function(context, ...args) {
context.that = this;
const result = context.that(...args);
delete context.that
return result;
}
Function.prototype.myApply = function(context, args) {
context.that = this;
const result = context.that(...args);
delete context.that
return result;
}
js// 简单版本
Function.prototype.myBind = function(context, ...args) {
const that = this;
return function() {
return that.call(context, ...args, ...arguments);
}
}
// 支持new的版本
Function.prototype.myBind2 = function(context, ...args) {
const that = this;
const bound = function() {
// that.call(new.target ? this : context, ...args, ...arguments);
return that.call(this instanceof bound ? this : context, ...args, ...arguments);
}
bound.prototype = this.prototype;
return bound;
}
js// 柯里化
function curring(fn, m) {
return function(n) {
return fn.call(this, m, n)
}
}
this对象或者说this指向外层代码块, 对箭头函数执行bind 无效,super、new.target、argumentsnew、yieldthis指向外层(可能是全局作用域)this的时候 如dom事件监听本文作者:郭敬文
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!