JavaScript知识简记(一)

60 阅读4分钟

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);
        })
      })
    })
  }
}