原型:
__proto__
(隐式原型)属性,属性值是一个普通的对象prototype
(原型)属性,属性值是一个普通的对象__proto__
属性指向它构造函数的 prototype
原型链:
当访问一个对象的某个属性时,会先在这个对象本身属性上查找,如果没有找到,则会去它的 __proto__
隐式原型上查找,即它的构造函数的 prototype
,如果还没有找到就会再在构造函数的 prototype
的 __proto__
中查找,这样一层一层向上查找就会形成一个链式结构,我们称为原型链。
原型链判断:
Object.prototype.__proto__; //null
Function.prototype.__proto__; //Object.prototype
Object.__proto__; //Function.prototype
Object instanceof Function; //true
Function instanceof Object; //true
Function.prototype === Function.__proto__; //true
继承(inheritance)是面向对象软件技术当中的一个概念。如果一个类别 B
“继承自”另一个类别 A
,就把这个 B
称为“A
的子类”,而把 A
称为“B
的父类别”也可以称“A
是 B
的超类”
组合继承: 原型链继承和借用构造函数方法组合就是组合继承。用原型链实现对原型属性和方法的继承,用借用构造函数技术来实现实例属性的继承。
寄生组合式继承: 结合借用构造函数传递参数和寄生模式实现继承。这是最成熟的方法,也是现在库实现的方法
优点:
继承可以使得子类具有父类别的各种属性和方法,在子类别继承父类别的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类别的原有属性和方法,使其获得与父类别不同的功能
obj
obj
的隐式原型和构造函数通过原型链连接起来this
指向 obj
this
call
,apply
,bind
。三者的第一个参数都是 this
需要指向的对象,但在后续的参数上只有 apply
是接收一个数组,call
和 bind
用逗号分开;call
和 apply
直接调用,返回的是一个值,而 bind
不直接调用,返回的是一个函数形式,执行:foo.bind(obj)()
第一个 bind
,因为被改变 this
指向之后就不可以更改了。
let
、const
)class
)import
/ export
)Set
和 Map
数据结构。ES6
原生提供 Proxy
构造函数,用来生成 Proxy
实例promise
let,const 块级作用域,var 全局作用域
let,const 不允许重复声明,var 可以
let,const 不存在变量提升(暂时性死区),var 可以
const 不能修改且要有初始值,let,var 值可以修改
使用自执行匿名函数(IIFE
)实现;闭包
(function () {
var a = 1;
console.log(a); // 1
})();
console.log(a); // 会报错
闭包是指有权访问另一个函数作用域中变量的函数。
return
回一个函数(高阶函数)v-for
中使用 var
声明循环变量,输出每个变量IIFE
(自执行匿名函数)GC
即 Garbage Collection ,程序工作过程中会产生很多垃圾,这些垃圾是程序不用的内存或者是之前用过了,以后不会再用的内存空间,而 GC
就是负责回收垃圾的,因为他工作在引擎内部,所以对于我们前端来说,GC
过程是相对比较无感的,这一套引擎执行而对我们又相对无感的操作也就是常说的垃圾回收机制。
垃圾回收策略:
标记清除法大致过程就像下面这样(会引申一个整理策略知识点)
引用计数法是跟踪记录每个变量值被使用的次数
如果对象 A
和对象 B
互相引用,他们的引用数量都是 2,导致无法被回收。
// 节流 就像fps游戏的射速,就算一直按着鼠标射击,也只会在规定射速内射出子弹
function throttle(fn, timeout) {
let timer = null;
return function (...arg) {
if (timer) return;
timer = setTimeout(() => {
fn.apply(this, arg);
timer = null;
}, timeout);
};
}
// 防抖 就像坐电梯,十秒自动关门,一旦有人进来再重新倒计时
function debounce(fn, timeout) {
let timer = null;
return function (...arg) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, arg);
}, timeout);
};
}
使用场景:
this
,引用的上层作用域中 this
prototype
new
操作符arguments
,可以使用 rest
参数代替,形式为 …变量名this
永远不会变,call
、apply
、bind
也无法改变call
、apply
、bind
不会改变 this
指向)计算机是通过二进制的方式存储数据的,所以计算机计算 0.1+0.2 的时候,实际上是计算的两个数的二进制的和。这两个小数都是无限循环小数,所以相加不等于 0.3。
Object.prototype.toString.call(obj).slice(8, -1) === "Array";
Array.isArray(obj);
obj.__proto__ === Array.prototype;
obj instanceof Array;
Promise
是异步编程的一种解决方案,比传统的解决方案回调函数和事件更合理和更强大。一个 Promise
的当前状态必须为以下三种状态中的一种:等待态(Pending
)、执行态(Fulfilled
)和拒绝态(Rejected
),状态的改变只能是单向的,且变化后不可在改变。
promise.all() 和 promise.race() 方法都是将多个 Promise
实例,包装成一个新的 Promise
实例。区别在于 all
是都调用成功一起返回,race
返回调用最快的那个。
addTask(1000,"1");
addTask(500,"2");
addTask(300,"3");
addTask(400,"4");
的输出顺序是:2 3 1 4
整个的完整执行流程:
一开始1、2两个任务开始执行
500ms时,2任务执行完毕,输出2,任务3开始执行
800ms时,3任务执行完毕,输出3,任务4开始执行
1000ms时,1任务执行完毕,输出1,此时只剩下4任务在执行
1200ms时,4任务执行完毕,输出4
class Scheduler {
constructor(limit) {
this.queue = [];
this.maxCount = limit;
this.runCounts = 0;
}
add(time, order) {
const promiseCreator = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(order);
resolve();
}, time);
});
};
this.queue.push(promiseCreator);
}
taskStart() {
for (let i = 0; i < this.maxCount; i++) {
this.request();
}
}
request() {
if (!this.queue || !this.queue.length || this.runCounts >= this.maxCount) {
return;
}
this.runCounts++;
this.queue
.shift()()
.then(() => {
this.runCounts--;
this.request();
});
}
}
const scheduler = new Scheduler(2);
const addTask = (time, order) => {
scheduler.add(time, order);
};
addTask(1000, "1");
addTask(500, "2");
addTask(300, "3");
addTask(400, "4");
scheduler.taskStart();
dom
,更新动画的函数);由于是每帧执行一次,那结果就是每秒的执行次数与浏览器屏幕刷新次数一样,通常是每秒 60 次。timeout
,为了避免超时,有可能会打乱这个顺序。
其中蓝色代表 js 脚本网络加载时间,红色代表 js 脚本执行时间,绿色代表 html 解析。
defer
和 async
属性都是异步去加载外部的 JS 脚本文件,它们都不会阻塞页面的解析,其区别如下:
async
属性的标签,不能保证加载的顺序;多个带 defer
属性的标签,按照加载顺序执行;async
属性,表示后续文档的加载和执行与 js 脚本的加载和执行是并行进行的,即异步执行;defer
属性,加载后续文档的过程和 js 脚本的加载(此时仅加载不执行)是并行进行的(异步),js 脚本需要等到文档所有元素解析完成之后才执行,DOMContentLoaded
事件触发执行之前。生命周期:
作用:
不可以跳出循环,可以抛出错误跳出循环。