Javascript知识框架笔记
2021-05-14 17:04:01 10 举报
AI智能生成
Javascript知识点汇总(更新中)
作者其他创作
大纲/内容
进阶
Promise
Generator
await&async
Set & Map
call&apply&bind
执行机制
事件循环
分支主题
防抖和节流
模块化
DOM
跨域
闭包
深拷贝
基础
基本概念
数据类型
Undefined
undefined全局对象的一个属性。
全局作用域中的一个变量,初始值就是原始数据类型 undefined。
用 undefined 值表示任何变量在未赋值前的初始值。
undefined 是一个变量,为了防止篡改,使用 void 0 获取 undefined 值。
Null
值 null 特指对象的值未设置,定义了但是为空。
null 是 JS 关键字,放心使用 null 关键字获取 null 值。
Boolean
两个值 false 和 true,表示逻辑意义上的真和假。
String
基本字符串:字符串字面量(单/双引号定义)和直接调用 String 方法 (没有通过 new)。
ES6模板字符串:使用反引号表示。
字符串对象:通过 new 生成对象实例。
字符串长度,不是字符的个数,而是 UTF16编码 单元的个数。
Number
数字:JS不区分整数和浮点数值,所有的数字均用浮点数值表示。
Infinity无穷大,-Infinity负无穷大。JS中有 +0 和 -0 的区别,在除法中可以得到无穷大和负无穷大。
0.1 + 0.2 = 0.3 ?
Symbol
ES6 引入,用于表示独一无二的值。
使用 Symbol 函数创建(不需要 new)。
对象的属性名有两种类型:一种是普通字符串,另一种就是 Symbol 类型。
Symbol 做对象属性时,不需要担心属性名冲突问题。
Object
对象可以看做是属性的集合:数据属性和访问器属性,结构相同key-value,其中key可以是字符串或者Symbol类型。
类型转换
《重学前端》PS:装箱转换是把基本类型转为对应的对象。拆箱转换是对象类型都基本类型的转换。
进行显示类型转换后,使用 ‘===’ 进行比较。
String 和 Number 转换都遵循“先拆箱再转换” 的规则。拆箱转为基本类型,再从基本类型转为对应的 String 或者 Number。
变量
ES5
JS 没有块级作用域,函数中的 var 声明,其作用域是函数体的全部。
var 会导致 “变量提升” 现象,变量可以在声明之前使用,值为 undefined。
循环内变量过读共享问题。一个经典问题,如图:
ES6
let 重定义变量会报错。
必须在声明后使用变量,否定报错。不可访问是因为在临时死区中。
let 优化了变量提升的特性;let 声明的全局变量不是全局对象的属性。
let 提供了块级作用域。解决 var 引入的问题,每次迭代时都为 i 创建新的绑定。
const: 用于声明不可变变量,变量声明后必须赋值,否则语法错误。
思考
var 为什么会提升而 let 不会,能说说其原理吗?
作用域
什么是作用域?
作用域是一套规则,用于确定在何处如何查找变量。作用域解决的是变量存储在哪里以及如何程序如何找到变量的问题。
遍历嵌套作用域规则:在查找时会形成一个作用域链,从当前作用域开始查找,
找不到,就向上一级继续查找,直到全局作用域。找不到抛出 ReferenceError。
找不到,就向上一级继续查找,直到全局作用域。找不到抛出 ReferenceError。
两种变量查找方式:LHS 和 RHS。赋值操作的左侧进行LHS查询,右侧进行RHS查询。
- RHS查询:查找某个变量的值。LHS查询:找到变量本身,对其赋值;未找到创建一个并赋值。
- ES5 引入严格模式,禁止自动或者隐式创建全局变量。LHS查询找到变量全局本身对其赋值。找不到抛出 ReferenceError。
变量作用域
全局变量:全局作用域
局部变量:局部作用域。
函数中声明的变量是局部变量,作用域是局部的。函数参数也是局部变量。
局部变量的优先级高于全局变量
编译原理
分词/词法分析:将代码分解成词法单元。
解析/语法分析:将词法单元流转换为一个语法结构树——抽象语法树AST。
代码生成:将 AST转为可执行代码。
函数作用域
函数作用域:函数的全部变量可以在整个函数的范围内使用和复用。
变量在声明前可以用,这种特性称为 声明提前。
变量在声明前可以用,这种特性称为 声明提前。
ES5 只有全局作用域和函数作用域,没有块级作用域。
问题一:变量提升,声明会被视为存在于整个作用域范围内。
问题二:用于计数循环变量泄露为全局变量。
立即执行函数表达式IIFE。
块级作用域
ES6 块级作用域
let 为 JS 提供类块级作用域的能力。
let 进行的声明不会再块级作用域中进行提升。声明前不可以使用。
解决了 var 在循环中污染全局作用域的问题。
块级作用域中可以声明函数,行为类似 let 。
对象
什么是对象?
JS中对象是基本数据类型,作为属性的容器,用属性名和值的形式表示。
原型式继承是JS的核心特征。后面会详细学习继承。
- 属性名:字符串或者Symbol,不可以重名。
- 属性值:除了undefined值之外的任何类型。
原型式继承是JS的核心特征。后面会详细学习继承。
属性分类
属性分类:数据属性和访问属性。
数据属性特征:
数据属性特征:
- 可写:是否可以设置该属性值。
- 可枚举:是否可以通过for/in循环属性。
- 可配置:是否可以删除或修改属性。
- 数据值:属性的数值。
- 可配置:是否可以删除和修改属性。
- 可枚举:是否可以通过for/in循环属性。
- Get: 在读取属性时调用的函数。默认为undefined。
- Set:在设置属性时调用的函数,默认为undefined。
对象分类
对象分类:
- 内置对象:规范中定义的对象。例如:数组,函数,日期等。
- 宿主对象:宿主环境定义的对象。例如:Web浏览器 HTMLElement 对象。
- 自定义对象:运行中JS代码创建的对象。
对象操作
创建:字面量、关键字new和Object.create()函数创建。
字面量创建对象是原型默认是 Object.prototype。
字面量创建对象是原型默认是 Object.prototype。
访问和设置:点语法和[]语法
- 第一种:点语法 + 标识符,类似C中访问静态字段。
- 第二种:[] + 字符串,看起来像访问数组,区别在于通过字符串索引而不数字。这样的数组通常称为关联数组,也称为散列、映射或字典。JS中的对象都是关联数组。
删除:删除属性通过 delete 运算符。
- 只可以删除自有属性,不能删除继承属性。
- 不可删除可配置性为 false 的属性。
检测:判断属性是否存在于某个对象。常用方法:in运算符、hasOwnProperty()和propertyIsEnumerable()。
- in 运算符:左侧属性名右侧对象。检测对象的自有属性和继承属性,存在返回 true。PS:属性值为undefined时也返回 true。
- hasOwnProperty() : 检测属性是否是对象的自有属性。
- propertyIsEnumerable(): 检测是对象的自有属性并且可枚举时返回 true。
Setter&Getter: 与数据属性不同,访问属性使用 getter 和 setter 方法代替属性值。
- getter:读取属性的值。只有该方法表示只可以读。
- setter: 写入属性的值。只有该方法表示只可以写。
原型
原型:每个函数都会创建一个 prototype属性,一个对象包含实例共享的属性和方法。
- 通过字面量创建的对象默认连接到Object.prototype,这是JS的标配对象。
- 通过Object.create创建空对象,就需要将 Object.prototype 作为第一个参数。
- 在检索值的使用原型连接会被调用,如果本对象没有属性名,回去原型中查找,一级一级向上,直到 Object.prototype,还未找到结果就是 undefined 值。这个过程称为 委托。查找形成的链称为原型链。
对象创建
工厂模式
缺点:创建对象公共函数,无法识别对象类型。
构造函数模式
new操作符
通过 new 操作符,创建对象。图片是 New 操作执行逻辑。
缺点:定义的方法在每个实例上都会创建一遍
示例
创建一个 Person 函数,通过 new 操作符调用创建p1对象。
对象
p1的 __proto__属性指向原型。
原型模式
特点:共享原型上的属性和方法,引用类型会有问题。
Person构造函数通过 prototype 指向原型对象,原型对象内部存在 constructor 指向构造函数
,对象通过 __proto__指向 Person.prototype原型对象。注意:实例对象和构造函数之间没有联系。
,对象通过 __proto__指向 Person.prototype原型对象。注意:实例对象和构造函数之间没有联系。
函数
函数的定义
- 函数构成:function 关键字;函数名;括号中一组参数;{}函数主体。
- 其中:函数名可以省略;ES6引入默认参数;
- add 函数除了a,b两个参数,还有两个附加参数:this 和 arguments。
- this 的指向随调用者改变,后期详细学习。
- arguments 是一个类似数组的对象,有一个 length 属性。
每个函数对象创建时都配有一个 prototype 属性,它的值是一个拥有 constructor 属性的对象,
并且 constructor 属性的值为 函数的对象。
并且 constructor 属性的值为 函数的对象。
函数声明:必须有函数名;函数声明会被提升,可以提前调用函数。
函数表达式:函数名可以省略;变量提升了赋值没有提前,定义前无法调用函数。
JS 中函数就是对象,具有对象的特点。拥有一条连接到原型对象的连接,对象字面量原型是 Object.prototype,
函数对象原型是 Function.prototype,该原型对象本身的原型是 Object.prototype。
函数对象原型是 Function.prototype,该原型对象本身的原型是 Object.prototype。
函数是对象,可以保存在变量、对象和属性中;函数可以被当做参数传递,可以当做返回值,还可以有方法。
函数调用
函数调用时,除了实参外还拥有一个额外值 本次调用的上下文,就是 this 关键字的值。
函数的调用方式
- 作为函数:此方式调用,ES5非严格模式下,this 指向全局对象。严格模式下,调用上下文this为 undefined。
- 作为方法:作为对象的方法,调用上下文 this 指向该对象。
- 作为构造函数:创建一个连接到函数 prototype 成员的新对象,this 绑定到新对象上。
- 使用call()和apply()方法间接调用:可以显示指定调用所需的 this 值。
函数种类
函数种类
- 第一种,普通函数:用 function 关键字定义的函数。
- 第二种,箭头函数:用 => 运算符定义的函数。
- 第三种,方法,在 class 中定义的函数。
- 第四种,生成器函数:用 function * 定义的函数。
- 第五种,类:用 class 定义的类,实际上也是函数。
- 第六种,异步函数:普通函数,箭头函数和生成器函数加上 async 关键字。
你理解这些函数的执行上下文切换?
闭包
什么是闭包?
闭包是指有权访问另一个函数作用域中的变量的函数。
函数的执行依赖于变量作用域,这个作用域是函数定义决定的,而不是函数调用时决定的。函数对象可以通过作用域链关联起来。
当调用函数时闭包所指向的作用域和定义函数时的作用域不是同一个作用域时,会发生什么呢?
特性
输出结果:local scope
变量作用域规则:JS执行用到的作用域是函数定义时创建的。嵌套函数f() 定义在这个作用域里,
其中的变量 scope 一定是局部变量,不管何时执行 f(),这个绑定依然有效。
其中的变量 scope 一定是局部变量,不管何时执行 f(),这个绑定依然有效。
闭包的特性:可以捕获到局部变量,并一直保存下去。
继承
原型&原型链
每个对象都存在一个原型对象,通过 __proto__ 指针指向上一个原型,从中继承方法和属性。原型对象本身也有原型,一层层向上最终指向 null,这就是原型链。
继承方案
原型链
本质:重新原型对象,指定为新的类型的实例。
父类Person 和 子类Student,通过原型链继承需要设置 Student.prototype = new Person()
缺点
引用类型会被多个实例所共享,数据被被篡改
子类原型上的 constructor 方法被重写。Student.prototype.constructor != Student
必须在重置原型后向子类添加属性和方法,否则会被覆盖丢失。
无法向父类的构造函数传参
借用构造函数
本质:使用父类的构造函数增强子类实例,等同于复制父类的实例给子类。
子类 Student 中执行父类构造函数 SuperType.call(this),子类的每个实例都会将父类的属性复制一份。
缺点:
只能继承父类的实例属性和方法,无法继承原型的。
无法实现复用,每个子类实例都存在父类的副本
组合继承
原型链实现原型属性和方法的继承,借用构造函数实现实例属性的继承
缺点:调用两个父类的构造函数,子类的实例和原型上都存在相同的父类属性和方法
原型式继承
利用一个空对象作为中介,将某个对象直接赋值给空对象构造函数的原型。
缺点
原型链继承多个实例的引用类型属性指向相同,存在篡改的可能。
无法传递参数
寄生式继承
在原型式继承的基础上,增强对象,返回构造函数
缺点:同原型继承
寄生式组合模式
结合借用构造函数传递参数和寄生模式实现继承
只调用一个父类的构造函数,原型链保持不变
这是最成熟的方法,也是现在库实现的方法
继承关系图
ES6继承extends
extends继承的核心代码实现和上述的寄生组合式继承方式一样
0 条评论
下一页