JavaScript
JavaScript
原型、构造函数、实例
- 原型(
prototype):一个简单的对象,用于实现对象的属性继承。 - 构造函数:可以通过
new来新建一个对象的函数。 - 实例:通过构造函数和
new创建出来的对象,便是实例。实例通过__proto__指向原型,通过constructor指向构造函数。
// 实例
const instance = new Object()
// instance(实例).__proto__ === Object.prototype(原型)
// Object.prototype(原型).constructor === Object(构造函数)
// Object(构造函数).prototype === Object.prototype(原型)

原型链
原型链是由原型对象组成,每个对象都有__proto__属性,指向了创建该对象的构造函数的原型,__proto__将对象连接起来组成了原型链。是一个用来实现继承和共享属性的有限的对象链。
- 属性查找机制:当查找对象的属性时,如果实例对象自身不存在该属性,则沿着原型链往上一级查找,找到时则输出,不存在时,则继续沿着原型链往上一级查找,直至最顶级的原型对象
Object.prototype,如还是没找到,则输出undefined; - 属性修改机制:只会修改实例对象本身的属性,如果不存在,则进行添加改属性,如果需要修改原型的属性时,则可以用:
b.prototype.x = 2;但是这样会造成所有继承于该对象的实例的属性发生改变。
Class基本语法和继承
简介
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return `(${this.x},${this.y})`
}
}
constructor 方法
constructor() 方法是类的默认方法,通过new 命令生成对象实例时,自动调用该方法。一个类必须有constructor()方法,如果没有显示定义,一个空的constructor()方法会被默认添加。constructor()方法默认返回实例对象(即this),完全可以指定返回另外一个对象。
类必须使用new 调用,否则会报错。
注意点
严格模式
类和模块的内部,默认就是严格模式。只要你写的代码在类或模块之中,就是严格模式。
不存在提升
类不存在变量提升。
name属性由于本质上,
ES6的类只是ES5的构造函数的一层包装,所以函数的需要特性都被class继承,包括name属性。Generator方法如果某个方法之前加上星号(
*),就表示该方法是一个Generator函数。this的指向类的方法内部如果含有
this,他默认指向类的实例。
静态方法
类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用。
父类的静态方法,可以被子类继承。
静态方法也是可以从super对象上调用的。
class Foo {
static classMethod() {
return 'hello';
}
}
class Bar extends Foo {
static classMethod() {
return super.classMethod() + ', too';
}
}
Bar.classMethod() // "hello, too"
实例属性的新写法
实例属性除了定义在constructor()方法里面的this上面,也可以定义在类的最顶层。此时,不需要再实例属性前面加上this。
class IncreasingCounter {
_count = 0;
increment() {
this._count++;
}
}
Class的继承
Class可以通过extends关键字实现继承,让子类继承父类的属性和方法。
class Point {
}
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y); // 调用父类的constructor(x, y)
this.color = color;
}
}
ES6规定,子类必须在constructor()方法中调用super()。因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,添加子类自己的实例属性和方法。如果不调用super()方法,子类就得不到自己的this对象。
对象的拷贝
- 浅拷贝:以赋值的形式拷贝引用对象,仍指向同一个地址,修改时原对象也会受到影响
Object.assign- 展开运算符(
...)
- 深拷贝:完全拷贝一个新对象,修改时原对象不再受到任何影响
JSON.parse(JSON.stringify(obj)):性能最快- 具有循环引用的对象时,报错
- 当值为函数、
undefined或Symbol时,无法拷贝
- 递归进行逐一赋值
new运算符的执行过程
- 新生成一个对象
- 链接到原型:
obj.__proto__ = Con.prototype - 绑定
this:apply - 返回新对象(如果构造函数有自己的return时,则返回该值)
Promise 对象
含义
异步编程的一种解决方案。简单来说,就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。
从语法上来说,Promise 是一个对象,从它可以获取异步操作的消息。
Promise 对象有以下两个特点:
- 对象的状态不受外界影响。
Promise对象代表一个异步操作,有三种状态:pending进行中、fulfilled已完成和rejected已失败。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。 - 一旦状态改变,就不会再变,任何时候都可以得到折耳结果。
Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。
有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。
基本用法
ES6规定,Promise对象是一个构造函数,用来生成Promise实例。
const promise = new Promise((resolve, reject) => {
// ... some code
if (/* 异步操作成功 */) {
resolve(value);
} else {
reject(error);
}
})
Promise.prototype.then()
Promise 实例具有 then方法,也就是说,then方法是定义在原型对象 Promose.prototype上的。它的作用是为Promise实例添加状态改变时的回调函数。
Promise.prototype.catch()
Promise.prototype.catch()方法是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数。
Promise.prototype.finally()
finally()方法用于指定不管Promise对象最后的状态如何,都会执行的操作。
promise
.then(result => {...})
.catch(error => {...})
.finally(() => {...})
Promise.all()
const p = Promise.all([p1, p2, p3])
总结:全部子实例成功才成功,一个子实例失败就失败。
Promise.race()
const p = Promise.all([p1, p2, p3])
总结:race是赛跑机制,看最先子实例是成功还是失败。
Promise.allSettled()
const p = Promise.all([p1, p2, p3])
总结:所有子实例执行完成,无论成功或失败。
Promise.any()
const p = Promise.all([p1, p2, p3])
总结:有一个子实例成功就成功,全部子实例失败才失败。
JavaScript执行机制
关于JavaScript
JavaScript是一门单线程语言,在最新的HTML5中提出了web-Worker,但JavaScript是单线程这一核心仍未改变。
JavaScript事件循环

- 同步任务和异步任务分别进入不同的执行“场所”。同步的进入主线程,异步的进入
Event Table并注册函数。 - 当指定的事情完成时,
Event Table会将这个函数移入Event Queue。 - 主线程内的任务执行完毕为空,会去
Event Queue读取对应的函数,进入主线程执行。 - 上述过程会不断重复,也就是常说的
Event Loop(事件循环)。
除了广义的同步任务和异步任务,我们对任务有更精细的定义:
macro-task(宏任务):包括整体代码script、setTimeout、setIntervalmicro-task(微任务):Promise、process.nextTick、Ajax
不同类型的任务会进入对应的Event Queue,比如setTimeout和setInterval会进入相同的Event Queue。
事件循环、宏任务、微任务的关系:

Array
map:遍历数组,返回回调返回值组成的新数组forEach:无法break,可以用try / catch中throw new Error来停止filter:过滤some:有一项返回true,则整体为trueevery:有一项返回false,则整体为falsejoin:通过指定连接符生成字符串push / pop:末尾推入和弹出,改变原数组,push返回数组长度,pop返回原数组最后一项;unshift / shift:头部推入和弹出,改变原数组,unshift返回数组长度,shift返回原数组第一项;sort(fn) / reverse:排序与反转,改变原数组concat:连接数组,不影响原数组,浅拷贝slice(start, end):返回截取后的新数组,不改变原数组splice(start, number, value...):返回删除元素组成的数组,value为插入项,改变原数组indexOf / lastIndexOf(value, fromIndex):查找数组项,返回对应的下标reduce / reduceRight(fn(prev, cur), defaultPrev):两两执行,prev为上次化简函数的return值,cur为当前值- 当传入
defaultPrev时,从第一项开始 - 当未传入时,则为第二项
- 当传入
script标签中defer和async的区别
script:会阻碍HTML解析,只有下载好并执行完脚本才会继续解析HTML。script async:解析HTML过程中进行脚本的异步下载,下载成功立马执行,有可能会阻断HTML的解析。script defer:完全不会阻碍HTML的解析,解析完成之后再按照顺序执行脚本。
defer和async特性仅适用于外部脚本。如果
<script>脚本没有src,则会忽略defer和async特性。
总结
async和defer共同点:加载脚本都不会阻塞页面的渲染。
区别:
| 顺序 | DOMContentLoaded | |
|---|---|---|
async | 加载优先顺序。脚本在文档中的顺序不重要---先加载完成的先执行 | 不相关。可能在文档加载完成前加载并执行完毕。如果脚本很小或来自于缓存,同时文档足够长,就会发生这种情况。 |
defer | 文档顺序(它们在文档中的顺序) | 在文档加载和解析完成之后(如果需要,则会等待),即在DOMContentLoaded之前执行。 |
在实际开发中,defer用于需要整个DOM的脚本和脚本的相对执行顺序很重要的时候。async用于独立脚本,例如计数器或广告,这些脚本的相对执行顺序无关紧要。
如果你使用的是
defer或async,那么用户将在脚本加载完成之前先看到页面。在这种情况下,某些图形组件可能尚未初始化完成。因此,请记得添加一个“正在加载”的提示,并禁用尚不可用的按钮。以让用户可以清楚地看到,他现在可以在页面上做什么,以及还有什么是正在准备中的。
从输入URL到页面展现的过程
DNS解析:将域名解析成IP地址TCP连接:TCP三次握手- 发送
HTTP请求 - 服务器处理请求并返回
HTTP报文 - 浏览器解析渲染页面
- 断开连接:
TCP四次挥手
URL
统一资源定位符,用于定位互联网资源,俗称网址。
scheme://host.domain.port/path/filename?abc=123#456
scheme:定义因特网服务的类型。常见的协议有http、https、ftp、file。其中最常见的类型是http,而https则是进行加密的网络传输。
host:定义域主机(http的默认主机是www)
domain:定义因特网域名,比如baidu.com
port:定义主机上的端口号(http的默认端口号是80)
path:定义服务器上的路径(若省略,则文档必须位于网站的根目录中)
filename:定义文档/资源的名称
query:即查询参数
fragment:即#后的hash值,一般用来定位到某个位置
