新增的数组API
静态方法
- Array.of(...args): 使用指定的数组项创建一个新数组
- Array.from(arg): 通过给定的类数组 或 可迭代对象 创建一个新的数组。
实例方法
- find(callback): 用于查找满足条件的第一个元素
- findIndex(callback):用于查找满足条件的第一个元素的下标
- fill(data):用指定的数据填充满数组所有的内容
- copyWithin(target, start?, end?): 在数组内部完成复制
- includes(data):判断数组中是否包含某个值,使用Object.is匹配
执行上下文
函数执行上下文:一个函数运行之前,创建的一块内存空间,空间中包含有该函数执行所需要的数据,为该函数执行提供支持。
执行上下文栈:call stack,所有执行上下文组成的内存空间。
栈:一种数据结构,先进后出,后进先出。
全局执行上下文:所有JS代码执行之前,都必须有该环境。
JS引擎始终执行的是栈顶的上下文。
执行上下文中的内容
- this指向
1). 直接调用函数,this指向全局对象 2). 在函数外,this指向全局对象 3). 通过对象调用或new一个函数,this指向调用的对象或新对象
- VO 变量对象
Variable Object:VO 中记录了该环境中所有声明的参数、变量和函数
Global Object: GO,全局执行上下文中的VO
Active Object:AO,当前正在执行的上下文中的VO
1). 确定所有形参值以及特殊变量arguments 2). 确定函数中通过var声明的变量,将它们的值设置为undefined,如果VO中已有该名称,则直接忽略。 3). 确定函数中通过字面量声明的函数,将它们的值设置为指向函数对象,如果VO中已存在该名称,则覆盖。
当一个上下文中的代码执行的时候,如果上下文中不存在某个属性,则会从之前的上下文寻找。
作用域链
- VO中包含一个额外的属性,该属性指向创建该VO的函数本身
- 每个函数在创建时,会有一个隐藏属性
[[scope]],它指向创建该函数时的AO - 当访问一个变量时,会先查找自身VO中是否存在,如果不存在,则依次查找
[[scope]]属性。
某些浏览器会优化作用域链,函数的[[scope]]中仅保留需要用到的数据。
柯里化函数
function curry (func) {
var args = Array.prototype.slice.call(arguments, 1);
var _this = this;
return function () {
var curArgs = Array.from(arguments); // 返回新函数后,再调用新函数时将传递的参数变成数组
var totalArgs = args.concat(curArgs); // 拼接每次传递的参数数组
// 每个函数身上都有一个length属性, 代表的是所需参数的个数
if (totalArgs.length >= func.length) {
// 参数数组的长度满足原函数本身所需的参数个数
return func.apply(null, totalArgs);
} else {
// 参数数组长度不满足原函数所需参数个数时
totalArgs.unshift(func);
return _this.curry.apply(_this, totalArgs);
}
};
};
事件循环
异步:某些函数不会立即执行,需要等到某个时机成熟后才会执行,该函数叫做异步函数。
浏览器的线程:
- JS执行引擎:负责执行JS代码
- 渲染线程:负责渲染页面
- 计时器线程:负责计时
- 事件监听线程:负责监听事件
- http网络线程:负责网络通信
事件队列:一块内存空间,用于存放执行时机到达的异步函数。当JS引擎空闲(执行栈没有可执行的上下文),它会从事件队列中拿出第一个函数执行。
事件循环:event loop,是指函数在执行栈、宿主线程、事件队列中的循环移动。
原型和原型链
- 所有对象都是通过
new 函数创建 - 所有的函数也是对象
- 函数中可以有属性
- 所有对象都是引用类型
原型 prototype
所有函数都有一个属性:prototype,称之为函数原型
默认情况下,prototype是一个普通的Object对象
默认情况下,prototype中有一个属性,constructor,它也是一个对象,它指向构造函数本身。
隐式原型 proto
所有的对象都有一个属性:__proto__,称之为隐式原型
默认情况下,隐式原型指向创建该对象的函数的原型。
当访问一个对象的成员时:
- 看该对象自身是否拥有该成员,如果有直接使用
- 在原型链中依次查找是否拥有该成员,如果有直接使用
猴子补丁:在函数原型中加入成员,以增强起对象的功能,猴子补丁会导致原型污染,使用需谨慎。
原型链
特殊点:
- Function的__proto__指向自身的prototype
- Object的prototype的__proto__指向null
函数是通过new Function创建的
链条的全貌
每个函数都有原型对象
普通对象是通过new 函数创建的
隐式原型的指向
原型中的constructor指向函数本身
原型链的应用
基础方法
W3C不推荐直接使用系统成员__proto__
Object.getPrototypeOf(对象)
获取对象的隐式原型
Object.prototype.isPrototypeOf(对象)
判断当前对象(this)是否在指定对象的原型链上
对象 instanceof 函数
判断函数的原型是否在对象的原型链上
Object.create(对象)
创建一个新对象,其隐式原型指向指定的对象
Object.prototype.hasOwnProperty(属性名)
判断一个对象自身是否拥有某个属性
应用
类数组转换为真数组
Array.prototype.slice.call(类数组);
实现继承
默认情况下,所有构造函数的父类都是Object
圣杯模式
Promise
const MyPromise = (() => {
const PENDING = "pending",
RESOLVED = "resolved",
REJECTED = "rejected",
PromiseValue = Symbol("PromiseValue"), //状态数据
PromiseStatus = Symbol("PromiseStatus"),
thenables = Symbol("thenables"), //thenable
catchables = Symbol("catchables"), //catchables
changeStatus = Symbol("changeStatus"),//当前状态
settleHandle = Symbol("settleHandle"), //后续处理的通用函数
linkPromise = Symbol("linkPromise"); //创建串联的Promise
return class MyPromise {
/**
* 改变当前Promise的状态
* @param {*} newStatus
* @param {*} newValue
* @param {*} queue 执行的作业队列
*/
[changeStatus](newStatus, newValue, queue) {
if (this[PromiseStatus] !== PENDING) {
//状态无法变更
return;
}
this[PromiseStatus] = newStatus;
this[PromiseValue] = newValue;
//执行相应队列中的函数
queue.forEach(handler => handler(newValue));
}
/**
*
* @param {*} executor 未决阶段(pending状态)下的处理函数
*/
constructor(executor) {
this[PromiseStatus] = PENDING;
this[PromiseValue] = undefined;
this[thenables] = []; //后续处理函数的数组 -> resolved
this[catchables] = []; //后续处理函数的数组 -> rejected
const resolve = data => {
this[changeStatus](RESOLVED, data, this[thenables]);
}
const reject = reason => {
this[changeStatus](REJECTED, reason, this[catchables]);
}
try {
executor(resolve, reject)
}
catch (err) {
reject(err);
}
}
/**
* 处理 后续处理函数
* @param {*} handler 后续处理函数
* @param {*} immediatelyStatus 需要立即执行的状态
* @param {*} queue 作业队列
*/
[settleHandle](handler, immediatelyStatus, queue) {
if (typeof handler !== "function") {
return;
}
if (this[PromiseStatus] === immediatelyStatus) {
//直接运行
setTimeout(() => {
handler(this[PromiseValue]);
}, 0);
}
else {
queue.push(handler);
}
}
[linkPromise](thenable, catchable) {
function exec(data, handler, resolve, reject) {
try {
const result = handler(data); //得到当前Promise的处理结果
if (result instanceof MyPromise) {
result.then(d => {
resolve(d)
}, err => {
reject(err);
})
}
else {
resolve(result);
}
}
catch (err) {
reject(err);
}
}
return new MyPromise((resolve, reject) => {
this[settleHandle](data => {
exec(data, thenable, resolve, reject);
}, RESOLVED, this[thenables])
this[settleHandle](reason => {
exec(reason, catchable, resolve, reject);
}, REJECTED, this[catchables])
})
}
then(thenable, catchable) {
return this[linkPromise](thenable, catchable);
}
catch(catchable) {
return this[linkPromise](undefined, catchable);
}
static all(proms) {
return new Promise((resolve, reject) => {
const results = proms.map(p => {
const obj = {
result: undefined,
isResolved: false
}
p.then(data => {
obj.result = data;
obj.isResolved = true;
//判断是否全部完成
const unResolved = results.filter(r => !r.isResolved)
if (unResolved.length === 0) {
//全部完成
resolve(results.map(r => r.result));
}
}, reason => {
reject(reason);
})
return obj;
})
})
}
static race(proms) {
return new Promise((resolve, reject) => {
proms.forEach(p => {
p.then(data => {
resolve(data);
}, err => {
reject(err);
})
})
})
}
static resolve(data) {
if (data instanceof MyPromise) {
return data;
}
else {
return new MyPromise(resolve => {
resolve(data);
})
}
}
static reject(reason) {
return new MyPromise((resolve, reject) => {
reject(reason);
})
}
}
})();