var/let/const
var存在变量提升,允许重复声明的问题,var定义后会在整个函数的范围可被使用.而let和const解决了上面的问题,在变量声明前不允许访问该变量,这叫暂时性死区.
let a={
A:1
}
a.B=a={A:2}
//结果为{A:1,B:{A:2}}
从左往右定义,从右往左赋值,所以先在{A:1}中定义了B属性,然后再将a执行{A:2},把{A:2}赋值给B属性.
call/apply/bind
第一个参数都是this,第二个参数都是传参,call(this,arg1,arg2)和apply(this,[arg1,arg2])立即执行,bind(this,arg1,arg2)不立即执行.手写实现如下:
// 实现call方法
Function.prototype.myCall=function(that,...args){
const fn=Symbol('mycall');
that[fn]=this;
const result=that[fn](...args);
delete that[fn]
return result;
}
// 实现bind方法
Function.prototype.mybind=function(that,...args){
const outFn=this;
const outThis=that;
let fn= function(...args2){
return outFn.apply(new.target?this:outThis,[...args,...args2]);
};
fn.prototype=Object.create(outFn.prototype);
return fn;
}
call实现关键:通过将函数暂时绑定到目标对象上,从而实现this指针的改变,函数同时也可以当作对象,通过fn.call(),可以从call中使用this获取此时的函数fn.
bind实现关键:返回一个新的函数,对于new.target判断是否通过new使用函数来确定使用绑定哪个this指针,还要注意原型链的正确继承.
原型/原型链
对于一个对象obj和其构造函数Fn,obj的原型对象是Fn.prototype,而函数Fn作为对象时的原型为Function.prototype.注意:对象只能通过_proto_或者Object.getPrototypeOf()获取原型,一般的对象没有prototype,只有函数有.
实现instanceOf:
// 实现instanceOf方法
function myinstanceOf(obj1,obj2){
if(obj1===null||typeof obj1 !=='object')
return true;
let ptr=obj1;
while(ptr){
if(ptr===obj2.prototype)
return true;
ptr=Object.getPrototypeOf(ptr);
}
return false;
}
继承方式
1.通过原型链直接继承,关键是使用new Parent()来调用构造函数,继承原型链并产生对象,缺点是共享原型以及不能向构造函数传递参数。
function Parent(){
this.name='a';
}
function Child(){}
Child.prototype=new Parent();
let obj=new Child();
console.log(obj.name)
2.构造函数继承,为了解决不能向构造函数传参的问题,可以通过直接调用构造函数,缺点是没有继承原型链。
function Parent(name){
this.name=name;
}
function Child(name,age){
Parent.call(this,name);
this.age='12';
}
3.组合继承,为了解决原型继承问题,可以通过直接new Parent设置为原型对象,缺点是调用多次构造函数。
function Parent(name){
this.name=name;
}
function Child(name,age){
Parent.call(this,name);
this.age='12';
}
Child.prototype=new Parent();
Child.prototype.constructor=Child;
4.寄生继承,为了解决调用多次构造函数的问题,可以通过使用Object.create实现原型链继承。
function Parent(name){
this.name=name;
}
function Child(name,age){
Parent.call(this,name);
this.age='12';
}
Child.prototype=Object.create(Parent.prototype)
Child.prototype.constructor=Child;
5.class继承,本质基于寄生继承。
class Parent{
constructor(name){
this.name=name;
}
}
class Child extends Parent{
constructor(name,age){
super(name);
this.age=age;
}
}
this指针
1.箭头函数没有this指针,而是继承定义时所在的作用域,而块级作用域(主要对let/const等有用)不存在this,因此只有函数作用域和全局作用域来决定。
2.全局this一般指向windows,严格模式undefined;
3.普通函数的this由调用这个函数的对象决定,全局调用可以省略不写。
4.事件的this执行元素本身。
闭包
闭包是指函数与词法作用域的组合,即函数在其定义的作用域外执行也能访问该作用域中的变量。
1.创建私有变量/方法
function createCounter(){
let count=0;
return {
addCount:function(num){
count+=num;
},
decCount:function(num){
count-=num;
},
print(){
console.log(count);
}
}}
const counter=createCounter();
counter.addCount(3);
counter.print();
console.log(counter.count);
2.函数柯里化,即比如sum(1)(2)(3)这种调用方式。
function curry(fn){
const len=fn.length;
const cbFn=function(...args){
if(args.length<len){
return function(...args2){
return cbFn(...args,...args2);
}
}else{
return fn.apply(this,args)
}
}
return cbFn;
}
function add(a,b,c){
return a+b+c;
}
const curriedAdd=curry(add);
console.log(curriedAdd(1)(2)(3));
console.log(curriedAdd(1,2)(3));
3.记忆化函数,使用闭包内的变量作为cache表。
Promise
Promise是异步编程的解决方案,有pending,fufilled,rejected三种状态,可用于解决回调地狱。
手写promise:
1.then方法,在状态为fufilled/rejected时直接调用onFufilled/onRejected函数即可,而状态为pending时分别把onFufilled/onRejected函数放进响应的队列中,对于执行onFufilled/onRejected方法得到的结果需要使用resolvePromise方法进行处理。
2.resolvePromise方法处理:循环引用,直接reject;普通值,直接resolve;promise/thenable对象有then方法,调用该then方法进行递归使用。
3.resolve方法关键变更状态为Fufilled,并执行Fufilled队列函数。reject方法同理。
4.finally方法的关键是在promise链上插入Promise.resolve(callback)操作,并将原来的值继续传递下去,因此finally不代表promise链的终止,而是前面执行这后无论失败与否必须执行的中间操作。
5.promise.all方法是对于promise数组中所有promise成功才算成功,任何一个失败都算失败。
6.promise.race方法是第一个成功/失败的promise来决定成果。
7.promise.allSettled方法是无论promise成功或者失败,最后都返回一个记录所有成功/失败结果的数组。
8.promise.any方法是返回第一个成功的结果,全部失败才算失败。以下是promise完整实现:
class MyPromise {
constructor(callback) {
this.FUFILLED = "FUFILLED";
this.REJECTED = "REJECTED";
this.PENDING = "PENDING";
this.status = this.PENDING;
this.onFufilledQueue = [];
this.onRejectedQueue = [];
this.value = null;
this.reason = null;
const resolve = (value) => {
if (this.status === this.PENDING) {
this.status = this.FUFILLED;
this.value = value;
this.onFufilledQueue.forEach((fn) => fn());
}
};
const reject = (reason) => {
if (this.status === this.PENDING) {
this.status = this.REJECTED;
this.reason = reason;
this.onRejectedQueue.forEach((fn) => fn());
}
};
try {
callback(resolve, reject);
} catch (reason) {
reject(reason);
}
}
then(onFufilled, onRejected) {
onFufilled =
typeof onFufilled === "function" ? onFufilled : (value) => value;
onRejected =
typeof onRejected === "function"
? onRejected
: (reason) => {
throw Error(reason);
};
const promise2 = new MyPromise((resolve, reject) => {
if (this.status === this.FUFILLED) {
setTimeout(() => {
try {
const x = onFufilled(resolve, reject);
this.resolvePromise(promise2, x, resolve, reject);
} catch (reason) {
reject(reason);
}
});
} else if (this.status === this.REJECTED) {
setTimeout(() => {
try {
const x = onRejected(resolve, reject);
this.resolvePromise(promise2, x, resolve, reject);
} catch (reason) {
reject(reason);
}
});
} else if (this.status === this.PENDING) {
this.onFufilledQueue.push(() => {
setTimeout(() => {
try {
const x = onFufilled(resolve, reject);
this.resolvePromise(promise2, x, resolve, reject);
} catch (reason) {
reject(reason);
}
});
});
this.onRejectedQueue.push(() => {
setTimeout(() => {
try {
const x = onRejected(resolve, reject);
this.resolvePromise(promise2, x, resolve, reject);
} catch (reason) {
reject(reason);
}
});
});
}
});
}
resolvePromise(promise2, x, resolve, reject) {
if (x === promise2) return reject("循环引用");
let called = false;
if (x !== null && (typeof x === "function" || typeof x === "object")) {
try {
const then = x.then;
if (typeof then === "function") {
then.call(
x,
(y) => {
if (called) return;
called = true;
this.resolvePromise(promise2, y, resolve, reject);
},
(z) => {
if (called) return;
called = true;
this.resolvePromise(promise2, z, resolve, reject);
}
);
} else {
resolve(x);
}
} catch (reason) {
if (called) return;
called = true;
reject(reason);
}
} else {
resolve(x);
}
}
catch(onRejected) {
this.then(null, onRejected);
}
finally(callback) {
return this.then(
(value) => MyPromise.resolve(callback).then(() => value),
(reason) =>
MyPromise.resolve(callback).then(() => {
throw reason;
})
);
}
// resolve静态方法 最大用处就是将一个对象包装成promise对象
static resolve(value) {
if (value instanceof MyPromise) return value;
else
return new MyPromise((resolve) => {
resolve(value);
});
}
static reject(reason) {
return new MyPromise((_, reject) => reject(reason));
}
static all(promiseArr) {
return new MyPromise((resolve, reject) => {
if (!(promiseArr instanceof Array)) throw new Error("参数必须为数组!");
const len = promiseArr.length;
let count = 0;
let allResults = new Array(len);
promiseArr.forEach((promiseReq, index) => {
MyPromise.resolve(promiseReq).then(
(result) => {
allResults[index] == result;
count++;
if (count >= len) resolve(allResults);
},
(reason) => reject(reason)
);
});
});
}
static race(promiseArr) {
if (!Array.isArray(promiseArr)) throw Error("参数必须为数组!");
return new MyPromise((resolve, reject) => {
promiseArr.forEach((promiseReq) => {
MyPromise.resolve(promiseReq).then(resolve,reject)
});
});
}
static allSettled(promiseArr){
if(!Array.isArray(promiseArr)) throw Error('参数只能为数组!');
return new MyPromise((resolve,reject)=>{
let count=0;
let result=[];
const len=promiseArr.length;
promiseArr.forEach(((promiseReq,index)=>{
MyPromise.resolve(promiseReq).then((result)=>{
result[index]={
status:'FUFILLED',
value: result
};
count++;
if(count==len) resolve(result);
},(reason)=>{
result[index]={
status:'Rejected',
value:reason
}
count++;
if(count==len) reject(result);
})
}))
})
}
static any(promiseArr){
if(!Array.isArray(promiseArr)) throw Error('参数必须为数组!');
return new MyPromise((resolve,reject)=>{
let count=0;
const len=promiseArr.length;
let result=[];
promiseArr.forEach((promiseReq,index)=>{
MyPromise.resolve(promiseReq).then((result)=>{
resolve(result);
},(reason)=>{
result[i]=reason;
count++;
if(count==len) reject(result);
})
})
})
}
}