美文网首页
JavaScript基础知识小记

JavaScript基础知识小记

作者: 灯下草虫鸣314 | 来源:发表于2020-09-09 19:02 被阅读0次

前言

最近发现自己许多JavaScript基础知识掌握不牢固。趁空闲时间,整理一下JavaScript的基础知识。

正题

数据类型

JavaScript一共有8中数据类型,其中7中是基本数据类型:undefined、null、Boolean、Number、String、Symbol、BigInt(es10新增)。还有一种引用类型Object(其中包括Function、Date、Array等)。
基本数据类型在内存中直接存储其值
引用数据类型在内存中存储的是其地址指针

js的内置对象

全局的对象( global objects )或称标准内置对象,不要和 "全局对象(global object)" 混淆。这里说的全局的对象是说在
全局作用域里的对象。全局作用域中的其他对象可以由用户的脚本创建或由宿主程序提供。
标准内置对象的分类
(1)值属性,这些全局属性返回一个简单值,这些值没有自己的属性和方法。
例如 Infinity、NaN、undefined、null 字面量
(2)函数属性,全局函数可以直接调用,不需要在调用时指定所属对象,执行结束后会将结果直接返回给调用者。
例如 eval()、parseFloat()、parseInt() 等
(3)基本对象,基本对象是定义或使用其他对象的基础。基本对象包括一般对象、函数对象和错误对象。
例如 Object、Function、Boolean、Symbol、Error 等
(4)数字和日期对象,用来表示数字、日期和执行数学计算的对象。
例如 Number、Math、Date
(5)字符串,用来表示和操作字符串的对象。
例如 String、RegExp
(6)可索引的集合对象,这些对象表示按照索引值来排序的数据集合,包括数组和类型数组,以及类数组结构的对象。例如 Array
(7)使用键的集合对象,这些集合对象在存储数据时会使用到键,支持按照插入顺序来迭代元素。
例如 Map、Set、WeakMap、WeakSet
(8)矢量集合,SIMD 矢量集合中的数据会被组织为一个数据序列。
例如 SIMD 等
(9)结构化数据,这些对象用来表示和操作结构化的缓冲区数据,或使用 JSON 编码的数据。
例如 JSON 等
(10)控制抽象对象
例如 Promise、Generator 等
(11)反射
例如 Reflect、Proxy
(12)国际化,为了支持多语言处理而加入 ECMAScript 的对象。
例如 Intl、Intl.Collator 等
(13)WebAssembly
(14)其他

寄生式组合继承

所谓寄生组合式继承,即通过借用构造函数来继承属性,通过原型链的形式来继承方法。

function Person(name) {
 this.name = name;
}

Person.prototype.sayName = function() {
 console.log("My name is " + this.name + ".");
};

function Student(name, grade) {
 Person.call(this, name);
 this.grade = grade;
}

Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;

Student.prototype.sayMyGrade = function() {
 console.log("My grade is " + this.grade + ".");
};

ES6模块和CommonJS模块的差异

  • CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。CommonJS 模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。ES6 模块的运行机制与 CommonJS 不一样。JS 引擎对脚本静态分析的时候,遇到模块加载命令 import,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。
  • CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。CommonJS 模块就是对象,即在输入时是先加载整个模块,生成一个对象,然后再从这个对象上面读取方法,这种加载称为“运行时加载”。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段才会生成。

深拷贝(考虑日期、正则等特殊对象解决循环引用)

function deepClone(obj, hash = new WeakMap()) {
  if (obj === null) return obj; // 如果是null或者undefined我就不进行拷贝操作
  if (obj instanceof Date) return new Date(obj);
  if (obj instanceof RegExp) return new RegExp(obj);
  // 可能是对象或者普通的值  如果是函数的话是不需要深拷贝
  if (typeof obj !== "object") return obj;
  // 是对象的话就要进行深拷贝
  if (hash.get(obj)) return hash.get(obj);
  let cloneObj = new obj.constructor();
  // 找到的是所属类原型上的constructor,而原型上的 constructor指向的是当前类本身
  hash.set(obj, cloneObj);
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      // 实现一个递归拷贝
      cloneObj[key] = deepClone(obj[key], hash);
    }
  }
  return cloneObj;
}
let obj = { name: 1, address: { x: 100 } };
obj.o = obj; // 对象存在循环引用的情况
let d = deepClone(obj);
obj.address.x = 200;
console.log(d);

防抖函数

function debounce(func, wait = 1000){
   let timer = null
   return function(...params){
       clearTimeout(timer)
       timer = setTimeout(()=>{
           timer = null
           func.call(this, ...params)
       },wait)
   }   
}
const a = debounce(function(flag){
   console.log(flag)
},1000)
a(1)
a(2)
a(3)
a(4)

使用reduce实现数组扁平化

const arr = [123,[123,12432,[12321,1232123,12321421,[1232123],12321,[12321],[12321]],12321],1232123]
function myFlat (arr){
    return arr.reduce((pre, cur) => {
        return pre.concat(Array.isArray(cur) ? myFlat(cur) : cur);
    }, []);
}
console.log(myFlat(arr))
// [123, 123, 12432, 12321, 1232123, 12321421, 1232123, 12321, 12321, 12321, 12321, 1232123]

实现call

Function.prototype._call = function (context, ...args){
    //  null,undefined,和不传时,context为 window
    context = context == null ? window : context;
    // 必须保证 context 是一个对象类型
    let contextType = typeof context;
    if (!/^(object|function)$/i.test(contextType)) {
        // context = new context.constructor(context); // 不适用于 Symbol/BigInt
        context = Object(context);
    }
  
    let result;
    context['fn'] = this; // 把函数作为对象的某个成员值
    result = context['fn'](...args); // 把函数执行,此时函数中的this就是context
    delete context['fn']; // 设置完成员属性后,删除
    return result;
}

实现apply

Function.prototype._apply = function (context, args){
    //  null,undefined,和不传时,context为 window
    context = context == null ? window : context;
    // 必须保证 context 是一个对象类型
    let contextType = typeof context;
    if (!/^(object|function)$/i.test(contextType)) {
        // context = new context.constructor(context); // 不适用于 Symbol/BigInt
        context = Object(context);
    }
  
    let result;
    context['fn'] = this; // 把函数作为对象的某个成员值
    result = context['fn'](...args); // 把函数执行,此时函数中的this就是context
    delete context['fn']; // 设置完成员属性后,删除
    return result;
}

实现bind

Function.prototype._bind = function(context, ...params){
    const _this = this;
    return function(...args){
        _this.call(context, ...params.concat(args))
    }
}

实现map

Array.prototype._map = function(callback, context){
  const arr = this;
  const res = []
  for(let i = 0; i< arr.length; i++){
    res.push(callback.call(context, arr[i],i ,arr))
  }
  return res
}

这里有一个有趣的面试题

// 返回什么?
['100','200','300','400'].map(Number) 
// 返回什么?
['100','200','300','400'].map(parseInt) 
// 为什么呢?

实现filter

Array.prototype._filter = function(callback, context){
  const arr = this;
  const res = []
  for(let i = 0 ; i< arr.length; i++){
    if(callback.call(context,arr[i], i ,arr)){
      res.push(arr[i])
    }
  }
  return res
}

实现reduce

Array.prototype._reduce = function(callback, inital){
  const arr = this;
  let prev = inital
  let initalKey= 0
  if(!inital){
    for(let i = 0;i<arr.length;i++){
      if(arr[i]){
        initalKey = i
        prev = arr[i]
        break
      }
    }
  }
  for(let i = initalKey; i< arr.length; i++){
    prev = callback.call(undefined, prev, arr[i], i, arr)
  }
  return prev
}

实现promise.all

Promise._all = function(promiseList){
  return new Promise((resolve, reject) => {
    let flag = 0
    const result = []
    promiseList.forEach(promise => {
      promise.then(res => {
        result.push(res)
        flag++
        if(flag === promiseList.length){
          resolve(result)
        }
      }).catch(err => {
        reject(err)
      })
    })
  })
}

这里有一个有趣的面试题
要求手写一个并发数为3的promise.all

Promise._allWithLimit3 = function(promiseList){
  return new Promise((resolve, reject)=>{
    const len = promiseList.length
    const taskList = promiseList.splice(0,3)
    const otherList = promiseList.splice(0)
    const result = []
    let total = 0;
    taskList.forEach(promise=>{
      singleTaskRun(promise)
    })
    function singleTaskRun (promise){
      promise.then(res=>{
        result.push(res)
        total++
        if(otherList.length > 0){
          const task = otherList.shift()
          singleTaskRun(task)
        } 
        if(total === len){
          resolve(result)
        }
      }).catch(err=>{
        reject(err)
      })
    }
  })
}
// 测试代码
let p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("1");
  }, 1000);
});
let p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("2");
  }, 1500);
});
let p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("3");
  }, 2000);
});
let p4 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("4");
  }, 2500);
});
let p5 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("5");
  }, 5000);
});

let all = Promise._allWithLimit3([p1, p3, p2, p4, p5]);
all.then(
  data => {
    console.log("data", data); 
  }
).catch(err => {
  console.log('err',err)
})

相关文章

网友评论

      本文标题:JavaScript基础知识小记

      本文链接:https://www.haomeiwen.com/subject/aphyektx.html