《JavaScript高级程序设计》学习笔记
1. 什么是JavaScript
javascript历史回顾
Mocha(后改名为LiveScript) =》 改名为JavaScript =》 ECMAScript作为实现JavaScript的标准
JavaScript是什么
完整的JavaScript的实现包括:核心(ECMAScript),文档对象模型(DOM),浏览器对象模型(BOM)
ECMAScript定义:语法,类型,语句,关键字,保留字,操作符,全局对象
DOM:Document Object Model 文档对象模型是一个API,用于在HTML中使用扩展的XML DOM通过创建表示文档的树,让开发者可以随心所欲地控制网页的内容和结构。使用DOM API可以轻松地删除、添加、替换、修改节点
BOM: 浏览器对象模型,用于 支持访问和操作浏览器的窗口
JavaScript与ECMAScript的关系
JavaScript的不同版本
2. HTML中的JavaScript
使用script元素
该元素有8个属性
XHTML中的变化:
在HTML中,解析script元素会应用相应特殊规则,XHTML中则没有这些规则。这意味着a<b中的小于号会被解析成一个标签的开始,并且由于作为的标签开始的小于号后面不能有空格,这会导致语法错误。
解决方法有两种:
1.把所有小于号都换成对应的HTML实体形式(<)
2.把所有的代码都包含到一个CDATA块中
<![CDATA[ ]]>
在兼容XHTML的浏览器中,这样可以解决问题。不兼容的话必须用注释来抵消。
行内脚本与外部脚本的比较
文档模式对JavaScript有什么影响
确保JavaScript不可用时的用户体验
使用noscript元素
<noscript>
<p>
This page requires a JavaScript-enabled browser.
</p>
</noscript>
3. 语言基础
1. 语法
区分大小写
标识符,就是变量、函数、属性或函数参数的名称
- 第一个字符必须是字母、下划线(_)或美元符号
- 剩下的其他字符可以是字母、下划线、美元符号或数字
- 使用小驼峰命名
注释:
- 单行 //
- 多行 /* */
严格模式 添加预处理指令 "use strict"
语句
2. 关键字与保留字
不做为标识符或属性名
3. 变量
变量是松散类型的,意思是变量可以用于保存任何类型的数据
有三个关键字可以声明变量:var const let
1. var关键字:
1.var声明作用域
函数内部声明变量使用var,成为局部变量;不使用成为全局变量
2.var声明提升
2. let声明:
let声明的范围是块作用域
var声明的范围是函数作用域
1.暂时性死区
2.全局声明
使用let在全局作用域中声明的变量不会成为window对象的属性。
3.条件声明
4.for循环中的let声明
3. const声明
行为与let基本相同,唯一区别是它声明变量必须同时初始化变量,且尝试修改const声明的变量会导致运行时错误。简言之是用来声明常量的。声明引用类型,引用指针不能修改,改变指针指向的对象是不会改变指针引用的,因此对象的属性可修改。
声明风格及最佳实践
- 不使用var
- const优先,let次之
4. 数据类型
- 简单数据类型:Undefined、Null、Boolean、Number、String、Symbol
- 复杂数据类型:Object
- typeof 操作符
Undefined类型:当使用var或let声明了变量但没有初始化时,就相当与给变量赋了undefined值。
增加这个特殊值的目的是为了正式明确空对象指针(null)和未初始化变量的区别
Null类型:只有一个值null。逻辑上讲,null值表示 一个空指针对象,所以typeof null "object"
在定义将来要保存对象值的变量时,建议使用null来初始化,这样只要检查这个变量的值是不是null就可以知道这个变量是否在后来重新赋予了一个对象的引用
Boolean类型:
区分大小写
Boolean()转型函数可以在任意类型的数据上调用,而且始终返回一个布尔值
if等流控制语句会自动执行其他类型值到布尔值的转换
5. Number类型: 1. 浮点值,存在舍入错误,永远不要测试某个特定的浮点值 2. 值的范围: - 最小值 Number.MIN_VALUE - 最大值 Number.MAX_VALUE - 负无穷大 Number.NEGATIVE_INFINITY -Infinity - 正无穷大 Number.POSITIVE_INFINITY Infinity 3. NaN 意思是“不是数值”(Not a Number),用来表示本来要返回数值的操作失败了(而不是抛出错误)![image-20210218161417699](../../images/image-20210218161417699.png) isNaN()
数值转换:
有3个函数可以将非数值转换为数值
Number()
parseInt()
parseFloat()
String类型:String(字符串)数据类型表示零或多个16 位Unicode 字符序列。字符串可以使用双引号(")、单引号(')或反引号(`)标示。
字符字面量:用于表示非打印字符或有其他用途的字符
字符串的特点:不可变
转换为字符串:
toString()方法可见于数值、布尔值、对象和字符串值。
String()转型函数,它始终会返回表示相应类型值的字符串。
- 模板字面量:
` `
字符串插值
模板字面量标签函数
原始字符串:获取原始的模板字面量内容
console.log(String.raw`\u00A9`); // \u00A9 console.log(`\u00A9`); // ©
Symbol类型:符号是原始值,且符号实例是唯一、不可变的。符号的用途是确保对象属性使用唯一标识符,不会发生属性冲突的危险。
- 符号的基本用法:使用Symbol()函数初始化
- 使用全局符号注册表
- 使用符号作为属性
- 常用的内置符号
Symbol.asyncIterator等等
Object类型:ECMAScript的对象其实就是一组数据和功能的集合
5. 操作符
1. 一元操作符
2. 位操作符
3. 布尔操作符
4. 乘性操作符
5. 指数操作符
6. 加性操作符
7. 关系操作符
8. 相等操作符
9. 条件操作符
10. 赋值操作符
11. 逗号操作符
6. 语句
也称为流控制语句
1. `if语句`
2. `do-while语句`
3. `while语句`
4. `for语句`
for-in语句
:是一种严格的迭代语句,用于枚举对象中的非符号键属性for-of语句
:是一种严格的迭代语句,用于遍历可迭代对象的属性标签语句
break和continue语句
with语句
:用途是将代码作用域设置为特定的对象switch语句
:首先,switch语句可以用于所有数据类型,因此可以使用字符串甚至对象。其次,条件的值不需要是常量,也可以是变量或表达式。
7. 函数
4. 变量、作用域与内存
1. 原始值与引用值
原始值是最简单的数据,引用值是由多个值构成的对象。
保存原始值的变量是按值访问的。
保存引用值的变量是按引用访问的。
动态属性
复制值
传递参数
确定类型 instanceof
console.log(person instanceof Object); // 变量person 是Object 吗? console.log(colors instanceof Array); // 变量colors 是Array 吗? console.log(pattern instanceof RegExp); // 变量pattern 是RegExp 吗?
2. 执行上下文与作用域
变量或函数的上下文决定了它们可以访问哪些数据,以及它们的行为。
- 每个上下文都有一个关联的变量对象,而这个上下文中定义的所有变量和函数都存在于这个对象上。
- 全局上下文是最外层的上下文。在浏览器中,全局上下文是window对象。
- 作用域链增强
- 变量声明
- 使用var的函数作用域声明:变量会被自动添加到最接近的上下文。
- 使用let的块级作用域声明
- 使用const的常量声明
- 标识符查找
3. 垃圾回收
JavaScript是使用垃圾回收的语言,也就是说执行环境负责在代码执行时管理内存。
垃圾回收程序必须跟踪记录哪个变量还会使用,以及哪个变量不会使用,以便回收内存。
浏览器发展史上,用到过两种主要的标记策略:标记清理和引用计数。
1. 标记清理
2. 引用计数
3. 性能
内存管理
1. 通过const和let声明提升性能 2. 隐藏类和删除操作 3. 内存泄露 4. 静态分配与对象池
5. 基本引用类型
引用值是某个特定引用类型的实例。
在ECMAScript中,引用类型是把数据和功能组织到一起的结构
1. Date
new Date()
Date.now()
Date.parse()
Date.UTC()
1. 继承的方法
toLocaleString() - 2/1/2019 12:00:00 AM
toString() - Thu Feb 1 2019 00:00:00 GMT-0800 (Pacific Standard Time)
valueOf() // Date 类型的valueOf()方法根本就不返回字符串,这个方法被重写后返回的是日期的毫秒表示。
console.log(date1 < date2); // true
console.log(date1 > date2); // false
2. 日期格式化方法
3. 日期/时间组件方法
2. RegExp
正则表达式
1. RegExp实例属性
2. RegExp实例方法
3. RegExp构造函数属性
4. 模式局限
3. 原始值包装类型
ECMAScript提供了3种特殊的引用类型:Boolean、Number和String
每当用到某个原始值的方法或属性时,后台都会创建一个相应原始包装类型的对象,从而暴露原始值的各种方法。
访问s1时,是以读模式访问的,也就是要从内存中读取变量保存的值。在以读模式访问字符串值的任何时候,后天都会执行以下3步:
- 1. 创建一个String类型的实例
- 调用实例上的特定方法
- 销毁实例
转型函数和构造函数并不一样,转型函数返回的是要转的类型,构造函数返回的是对象类型
3个用于将数值格式化为字符串的方法:
- toFixed()方法返回包含指定小数点位数的数值字符串。
- toExponential(),返回以科学记数法(也称为指数记数法)表
示的数值字符串。接收一个参数,表示结果中小数的位数。 - toPrecision()方法会根据情况返回最合理的输出结果,可能是固定长度,也可能是科学记数法
形式。这个方法接收一个参数,表示结果中数字的总位数(不包含指数)。
ES6 新增了Number.isInteger()方法,用于辨别一个数值是否保存为整数。
String:
JavaScript字符:
JavaScript 字符串由16 位码元(code unit)组成。对多数字符来说,每16位码元对应一个字符.
1. length 属性表示字符串包含多少16 位码元 2. charAt()方法返回给定索引位置的字符 3. charCodeAt()方法可以查看指定码元的字符编码 4. fromCharCode()方法用于根据给定的UTF-16 码元创建字符串中的字符
normalize()方法
?
字符串操作方法:
1. concat(),用于将一个或多个字符串拼接成一个新字符串。 2. 3个从字符串中提取子字符串的方法:slice()、substr()和substring()。
字符串位置方法:
1. indexOf()方法从字符串开头开始查找子字符串 2. lastIndexOf()方法从字符串末尾开始查找子字符串
字符串包含方法
1. 3 个用于判断字符串中是否包含另一个字符串的方法:startsWith()、 endsWith()和includes()。
trim()方法:这个方法会创建字符串的一个副本,删除前、后所有空格符,再返回结果。
1. trimLeft()和trimRight()方法分别用于从字符串开始和末尾清理空格符。
repeat()方法:这个方法接收一个整数参数,表示要将字符串复制多少次,然后返回拼接所有副本后的结果。
padStart()和padEnd()方法:
let stringValue = "foo"; console.log(stringValue.padStart(6)); // " foo" console.log(stringValue.padStart(9, ".")); // "......foo" console.log(stringValue.padEnd(6)); // "foo " console.log(stringValue.padEnd(9, ".")); // "foo......"
字符串迭代与解构:字符串的原型上暴露了一个@@iterator 方法,表示可以迭代字符串的每个字符。
字符串大小写转换
字符串模式匹配方法:
- match()方法
- search()方法
- replace()方法
- split()方法
localeCompare()方法:
这个方法比较两个字符串,返回如下3 个值中的一个。
- 如果按照字母表顺序,字符串应该排在字符串参数前头,则返回负值。(通常是-1,具体还要看与实际值相关的实现。)
- 如果字符串与字符串参数相等,则返回0。
- 如果按照字母表顺序,字符串应该排在字符串参数后头,则返回正值。(通常是1,具体还要看与实际值相关的实现。)
HTML方法
4. 单例内置对象
任何由ECMAScript 实现提供、与宿主环境无关,并在ECMAScript程序开始执行时就存在的对象
- Global
- URL编码方法
- eval()方法
- Global对象属性
window对象
虽然ECMA-262 没有规定直接访问Global 对象的方式,但浏览器将window 对象实现为Global
对象的代理。因此,所有全局作用域中声明的变量和函数都变成了window 的属性。or
let global = function() { return this; }(); 这段代码创建一个立即调用的函数表达式,返回了this 的值。如前所述,当一个函数在没有明确 (通过成为某个对象的方法,或者通过call()/apply())指定this 值的情况下执行时,this 值等于 Global 对象。因此,调用一个简单返回this 的函数是在任何执行上下文中获取Global 对象的通用 方式
Math:ECMAScript 提供了Math 对象作为保存数学公式、信息和计算的地方。
Math对象属性
2. min()和max()方法 3. 舍入方法 1. Math.ceil()方法始终向上舍入为最接近的整数。 2. Math.floor()方法始终向下舍入为最接近的整数。 3. Math.round()方法执行四舍五入。 4. Math.fround()方法返回数值最接近的单精度(32 位)浮点值表示。 4. random()方法:Math.random()方法返回一个0~1 范围内的随机数,其中包含0 但不包含1。 5. 其他方法
6. 集合引用类型
1. Object
创建实例的两种方式:
- 使用new操作符和Object构造函数
- 使用对象字面量表示法。 {}
对象属性的存取:
- 点语法
- 中括号:
- 使用中括号时,要在括号内使用属性名的字符串形式
- 使用中括号时,可以使用变量访问属性
- 不能使用点语法访问时,使用中括号
2. Array
创建数组
- new
- 数组字面量
创建数组的静态方法:
- Array.from() 用于将类数组结构转换为数组实例 - Array.of() 用于将一组参数转换为数组实例
数组空位
避免使用空位,可显式地用undefined值代替
数组索引
使用length向数组末尾添加元素
colors[colors.length] = 'hello';
检测数组
Array.isArray()
迭代器方法
keys()返回数组索引的迭代器
values()返回数组元素的迭代器
entries()返回索引/值对的迭代器
使用ES6的解构可以非常容易地在循环中拆分键/值对
const a = ['foo','bar','baz','qux']; for(const [idx, element] of a.entries()){ alert(idx); alert(element); }
复制和填充方法
- 批量复制方法copyWithin() 会按照指定范围复制数组中的部分内容
- 填充数组方法fill() 向一个已有数组中插入全部或部分相同的值
转换方法
所有对象都有toLocaleString(),toString()和valueOf()方法
栈方法
使用pop()和push(),可以把数组当成队列来使用
队列方法
使用shift()和push(),可以把数组当成队列来使用
unshift():在数组开头添加任意多个值
排序方法
reverse()
sort() 可以接受一个自定义的比较函数
操作方法
- concat() 末尾加
- splice() 插入:
- 删除 splice(要删除的第一个元素的位置, 要删除的元素数量)
- 插入 splice(开始位置,要删除的元素数量0,要插入的元素)
- 替换 splice(开始位置,要删除的元素数量,要插入的元素)
搜索和位置方法
按严格相等搜索
indexOf() lastIndexOf() includes()
按断言函数搜索
断言函数接收3 个参数:元素、索引和数组本身
find()和findIndex()方法使用了断言函数。这两个方法都从数组的最小索引开始。find()返回第一个匹配的元素,findIndex()返回第一个匹配元素的索引。
迭代方法
5个迭代方法,每个方法接受两个参数:以每一项为参数运行的函数,以及可选的作为函数运行上下文的作用域对象(影响函数中this的值)。传给每个方法的函数接受3个参数:数组元素、元素索引和数组本身。
every():对数组每一项都运行传入的函数,如果对每一项函数都返回true,则这个方法返回true。
filter():对数组每一项都运行传入的函数,函数返回true 的项会组成数组之后返回。
forEach():对数组每一项都运行传入的函数,没有返回值。
map():对数组每一项都运行传入的函数,返回由每次函数调用的结果构成的数组。
some():对数组每一项都运行传入的函数,如果有一项函数返回true,则这个方法返回true。归并函数
reduce()和reduceRight()
3. 定型数组
目的是提升向原生库传输数据的效率
? 已了解,遇到了再回来看
4. Map
对性能要求较高使用Map
- new Map()
- get()
- has()
- delete()
- clear()
5. WeakMap
弱映射,已了解,遇到了再回来看
6. Set
基本API
1. new Set() 2. add() 3. has() 4. size 5. delete() clear()
顺序与迭代
keys() values() entries()
- 定义正式集合操作
7. WeakSet
weak 描述的是JavaScript垃圾回收程序对待“弱集合”中值的方式
8. 迭代与扩展操作
有4 种原生集合类型定义了默认迭代器:
Array
所有定型数组
Map
Set
扩展操作符
let arr1 = [1, 2, 3];
let arr2 = [...arr1];
console.log(arr1); // [1, 2, 3]
console.log(arr2); // [1, 2, 3]
console.log(arr1 === arr2); // false
7. 迭代器和生成器
在软件开发领域,“迭代”的意思是按照顺序反复多次执行一段程序,通常会有明确的终止条件。?
1. 理解迭代
2. 迭代器模式
3. 生成器
8. 对象、类与面向对象编程
1. 理解对象
属性的类型
内部特性描述属性的特征,标识为[[内部属性]],JavaScript不能直接访问
数据属性:包含一个保存数据值的位置。值从这个位置读写。有4个特性。
访问器属性:包含一个获取函数和一个设置函数。获取函数返回一个有效值;设置函数传入新值,决定对数据做出什么修改。有4个特性。
访问器典型的使用场景是,设置一个属性值导致一些其他变化发生。
Object.defineProperty(book, "year", { // book是对象,year是属性 //特性 }
year_中的下划线常用来表示该属性并不希望在对象方法的外部被访问。即表示私有属性
定义多个属性
Object.defineProperties()方法:通过多个描述符一次性定义多个属性。接收两个参数:要为之添加或修改属性的对象和另一个描述符对象。
读取属性的特性
Object.getOwnPropertyDescriptor()
合并对象
Object.assign():
- 接受一个目标对象和一个或多个源对象作为参数。 - `对每个源对象执行浅复制,浅复制意味着只会复制对象的引用`
对象标识及相等判定
Object.is()
增强的对象语法
ECMAScript6为定义和操作对象新增了很多极其有用的语法糖特性
属性值简写:只要使用 变量名(不再写冒号)就会自动被解释为同名的属性值。
let name = 'matt'; let person = { name }; let person1 = { name: name };
可计算属性
有了可计算属性,就可以在对象字面量中完成动态属性赋值。
中括号包围的对象属性键告诉运行时将其作为JavaScript表达式而不是字符串来求值。
const nameKey = 'name'; const ageKey = 'age'; const jobKey = 'job'; let person = { [nameKey]: 'Matt', [ageKey]: 23, [jobKey]: 'Software engineer' };
简写方法名
对象定义方法时简写:
let person = { sayName: function(name) { } }; let person1 = { sayName(name) { } } // 与可计算属性键相互兼容
与可计算属性键相互兼容
对象解构
- 可以在一条语句中使用嵌套数据实现一个或多个赋值操作。
- 简单地说,对象解构就是使用与对象匹配的结构来实现对象属性赋值。
let person = { name: 'Matt', age: 27 }; let {name, job='Software engineer'} = person; console.log(name); // Matt console.log(job); // Software engineer
注意:
null和undefined 不能被解构,否则会抛出错误。- 嵌套解构
- 部分解构
- 参数上下文匹配
2. 创建对象
概述
ES5,巧妙地运用原型式继承模拟类的继承
ES6,开始正式支持类和继承,实现封装了ES5构造函数加原型继承的语法糖
工厂模式
function createPerson(name, age, job) { let o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function(){ console.log(this.name); }; return o; }
这种工厂模式随然可以解决创建多个类似对象的问题,但没有解决对象标识问题(即新创建的对象是什么类型)
构造函数模式
function Person(name,age,job) { this.name = name; this.age = age; this.job = job; this.sayName = function(){ console.log(this.name); }; } let person1 = new Person('Nicholas',22,'j1'); let person2 = new Person('Greg',23,'j2'); person1.sayName(); // Nicholas person2.sayName(); // Greg
new 调用构造函数会执行如下操作:
1. 在内存中创建一个新对象 2. 这个新对象内部的[[Prototype]]特性被赋值为构造函数的prototype属性 3. 构造函数内部的this被赋值为这个新对象(即this指向新对象) 4. 执行构造函数内部的内部(给新对象添加属性) 5. 如果构造函数返回非空对象,则返回该对象;否则,返回刚创建的新对象
构造函数也是函数
- 构造函数与普通函数的唯一区别是调用方式不同
没明确this值时,this始终指向Global对象(在浏览器中就是window对象)
- 构造函数与普通函数的唯一区别是调用方式不同
构造函数的问题
其定义的方法会在每个实例上都创建一遍。将函数定义在外部可解决,但却会造成全局作用域的混乱。
原型模式
每个函数都会创建一个prototype属性,这个属性是一个对象,包含应该由特定引用类型的实例共享的属性和方法
这个对象就是通过调用构造函数创建的对象的原型
- 好处:
- 在它上面定义的属性和方法可以被对象实例共享
- 原来在构造函数中直接赋给对象实例的值,可以直接赋值给它们的原型
function Person(){} Person.prototype.name = 'Nicholas'; Person.prototype.age = 29; Person.prototype.sayName = function(){ console.log(this.name); }; let person1 = new Person(); person1.sayName(); // "Nicholas" let person2 = new Person(); person2.sayName(); // "Nicholas" console.log(person1.sayName == person2.sayName); // true
- 理解原型
- 原型层级
- 原型和in操作符
- 属性枚举顺序
对象迭代
将对象内容转换为序列化的格式:
- Oject.values() 接收一个对象,返回对象值的数组 - Object.entries() 接收一个对象,返回键/值对的数组
- 其他原型语法
- 原型的动态性
- 原生对象原型
3. 继承
原型链
每个构造函数都有一个原型对象,原型有一个属性指向构造函数,而实例有一个内部指针指向原型。
基本思想:通过原型继承多个引用类型的属性和方法
function SuperType() { this.property = true; } SuperType.prototype.getSuperValue = function() { return this.property; }; function SubType() { this.subproperty = false; } // 继承SuperType SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function () { return this.subproperty; }; let instance = new SubType(); console.log(instance.getSuperValue()); // true
默认原型 Object
原型与继承关系 instanceof 和isPrototyprOf()
关于方法
原型链的问题:引用值存在共享 ,子类型在实例化时不能给父类型的构造函数传参
盗用构造函数
基本思路:
在子类构造函数中调用父类构造函数
使用apply()和call()方法以新创建的对象为上下文执行构造函数。组合继承
原型式继承
寄生式继承
寄生式组合继承
4. 类
类定义
类声明和类表达式
类的构成
类构造函数
- 实例化
- 把类当成特殊函数
实例、原型和类成员
- 实例成员
- 原型方法与访问器
- 静态类方法
- 非函数原型和类成员
- 迭代器与生成器方法
继承
继承基础
构造函数、HomeObject和super()
抽象基类
继承内置类型
9. 代理与反射
ES6新增的代理和反射为开发者提供了拦截并向基本操作嵌入额外行为的能力
具体来说,可以给目标对象定义一个关联的代理对象,而这个代理对象可以作为抽象的目标对象来使用
在对目标对象的各种操作影响目标对象之前,可以在代理对象中对这些操作加以控制
- 代理是目标对象的抽象
1. 代理基础
创建空代理
const target = { id: 'target' }; const handler = {} const proxy = new Proxy(target, handler);
定义捕获器
捕获器是在处理程序对象中定义的“基本操作的拦截器”
每个捕获器都对应一种基本操作,可以直接或间接在代理对象上调用。
只有在代理对象上调用才会使用捕获器
捕获器参数和反射API
在捕获器中通过反射获取目标的方法,Reflect.get(...)
捕获器不变式
防止捕获器定义出现过于反常的行为
比如,如果目标对象有一个不可配置且不可写的数据属性,那么在捕获器返回一个与该属性不同的值时,会抛出TypeError
可撤销代理
撤销函数和代理对象是在实例化时同时生成的
const { proxy, revoke } = Proxy.revocable(target, handler); // 撤销 revoke();
实用反射API
- 反射API与对象API
- 反射API并不限于捕获处理程序
- 大多数反射API方法在Object类型上有对应的方法
- 状态标记
- 用一等函数替代操作符
- 安全地应用函数
- 反射API与对象API
代理另一个代理
代理的问题与不足
- 代理中的this
- 代理与内部槽位
2. 代理捕获器与反射方法
代理可以捕获13种不同的基本操作
几种不同的JavaScript操作会调用同一个捕获器处理程序
对于代理对象上执行的任何一种操作,只会有一个捕获处理程序被调用
get()捕获器会在获取属性值的操作种被调用,对应的反射API方法为Reflect.get()
返回值
返回值无限制
拦截的操作
- proxy.property
- proxy[property]
- Object.create(proxy)[property]
- Reflect.get(proxy,property,receiver)
捕获器处理程序参数
- target:目标对象
- property:引用的目标对象上的字符串键属性
- receiver:代理对象或继承代理对象的对象
捕获器不变式
set()捕获器会在设置属性值的操作中被调用。对应的放射API方法为Reflect.set()
has()捕获器会在in操作符中被调用。对应的反射API方法为Reflect.has()
defineProperty()捕获器会在Object.defineProperty()中被调用。对应的反射API方法为Reflect.defineProperty()
getOwnPropertyDesciptor()捕获器会在Object.getOwnPropertyDesciptor()中被调用。对应的反射API方法为Reflect.getOwnPropertyDesciptor()
deleteProperty()捕获器会在delete 操作符中被调用。对应的反射API 方法为Reflect.deleteProperty()。
ownKeys()捕获器会在Object.keys()及类似方法中被调用。对应的反射API 方法为Reflect.ownKeys()。
getPrototypeOf()捕获器会在Object.getPrototypeOf()中被调用。对应的反射API 方法为Reflect.getPrototypeOf()
setPrototypeOf()捕获器会在Object.setPrototypeOf()中被调用。对应的反射API 方法为Reflect.setPrototypeOf()。
isExtensible()捕获器会在Object.isExtensible()中被调用。对应的反射API 方法为Reflect.isExtensible()。
preventExtensions()捕获器会在Object.preventExtensions()中被调用。对应的反射API方法为Reflect.preventExtensions()。
apply()捕获器会在调用函数时中被调用。对应的反射API 方法为Reflect.apply()。
construct()捕获器会在new 操作符中被调用。对应的反射API 方法为Reflect.construct()。
3. 代理模式
使用代理可以在代码中实现一些有用的编程模式
跟踪属性访问
通过捕获get、set 和has 等操作,可以知道对象属性什么时候被访问、被查询。
隐藏属性
代理的内部实现对外部代码是不可见的,因此要隐藏目标对象上的属性也轻而易举
属性验证
可以根据所赋的值决定是允许还是拒绝赋值
函数与构造函数参数验证
跟保护和验证对象属性类似,也可对函数和构造函数参数进行审查。
数据绑定与可观察对象
通过代理可以把运行时中原本不相关的部分联系到一起。这样就可以实现各种模式,从而让不同的代码互操作。
10. 函数
1.箭头函数
箭头函数不能使用arguments、super和new.target,也不能作为构造函数。此外,箭头函数也没有prototype属性。
2. 函数名
因为函数名就是指向函数的指针,所以它们跟其他包含对象指针的变量具有相同的行为。这意味着一个函数可以有多个名称。
3. 理解参数
ES函数的参数在内部表现为一个数组。
可使用arguments[i]获取参数值。arguments对象是一个类数组对象。
箭头函数不能使用arguments访问,但是可以在包装函数中把它提供给箭头函数
4. 没有重载
参数是数组,没有函数签名,没有重载
5. 默认参数值
在函数定义的参数后面用=为参数赋一个默认值
6. 参数的扩展与收集
es6新增扩展操作符,可以非常简洁地操作和组合集合数据。
1. 扩展参数
2. 收集参数
7. 函数声明与函数表达式
8. 函数作为值
9. 函数内部
函数内部存在2个特殊的对象:arguments、this
1个特殊属性:new.target
arguments
arguments.callee 指向arguments对象所在函数的指针
this
在标准函数中,this引用的是把函数当成方法调用的上下文对象,这时候通常称其为this值(在网页的全局上下文中调用函数时,this指向windows)
在箭头函数中,this引用的是定义箭头函数的上下文
caller
这个属性引用的是调用当前函数的函数,或者如果是在全局作用域中调用的则为null
new.target
检测函数是否使用new关键字调用的new.target属性