本文将系统梳理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
对象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.caller 3. arguments 不再有魔术行为4. 标识符 eval 和arguments 当做关键字,它们的值是不能修改,也不能声明为变量5. eval 限制在当前作用域6.尾调用优化只在严格模式下开启 |
对this的影响 | 1. 全局代码块、函数调用(不是方法调用)this默认指向window 2. call /apply 传入null /undefined 保持原样不被转换为window |
注: 无声明直接赋值(严格模式已禁用)
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
或函数参数默认值 来安全的获取undefined
typeof null
等于 "object"
null == undefined
null !== undefined
null --> 0
undefined --> NaN
null
是原型链的终点 undefined
会触发函数默认值数值的表示
js0b1010; // 二进制表示
0o12; // 八进制
10; // 十进制
0xa; // 十六进制
// 科学计数法 [digits][.digits][(E|e)[+|-]digits]
1e+1
为什么0.1+0.2 > 0.3
typeof
可以判断基本类型和函数类型Object.prototype.toString
可以判断对象类型和数组类型
Reflect.toString
instanceof
jstypeof 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
""
falsy
NaN
null
undefined
+
字符串连接,a + b
如果a
是字符串那么b
会转换成字符串再于a
进行运算+
-
*
/
(加号排除字符串连接的情况){}
(任意对象) 转字符串
{}
(任意对象) 转数值
Symbol类型
BigInt
BigInt()
Number()
String()
Boolean()
Object.definePrototype
,Object.defineProperties
可以设置对象属性Object.getOwnPrototypeDescriptor()
可以获取某个属性的描述对象控制对象状态的方法
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.getOwnPrototypeNames() | ✅ | ✅ | - | - |
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__ = b
a = Object.create(b, prototype)
即 a.__proto__ = b
a.prototype = prototype
Object.create(null)
创建一个干净的对象,没有Object的实例方法Promise.any()
方法使用Object.is
能识别 0
和-0
及NaN
Object.assgin(target, source1, source2, ...)
用于对象的合并,将原对象的所有自身可枚举属性复制到目标对象。它的特征如下
Object.assign({}, null, undefined, 12n, 23, false, Symbol.for('a'))
JSON.stringify
一样将get存取器
转换为值Object.getProtypeOf
、Object.setProtorypeOf
Object.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
排序数组遍历
forEach
map
filter
every / some
reduce / reduceRight
indexOf
/ lastIndexOf
其他数组方法
join
转字符串slice
复制数组因为数组也是对象,也可以使用对象的方法 如toString
valueOf
静态方法
Array.from
将一个可遍历的对象转换为数组, 它还提供了类似map的功能(第二个参数)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() {
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);
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
、arguments
new
、yield
this
指向外层(可能是全局作用域)this
的时候 如dom事件监听本文作者:郭敬文
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!