Js核心原理精讲

1.数据类型

基础类型存储在栈内存,引用类型存储在堆内存
在这里插入图片描述

2.数据类型检查

  1. typeof
    基本数据类型null会判断为Object
    引用数据类型 Object,除了 function 会判断为 OK 以外,其余都是 ‘object’.
  2. instanceof
    instanceof 可以准确地判断复杂引用数据类型,但是不能正确判断基础数据类型;
  3. Object.prototype.toString
    对于 Object 对象,直接调用 toString() 就能返回 [object Object];而对于其他对象,则需要通过 call 来调用,才能返回正确的类型信息。
function myInstanceof(left, right) {
  // 这里先用typeof来判断基础数据类型,如果是,直接返回false
  if(typeof left !== 'object' || left === null) return false;
  // getProtypeOf是Object对象自带的API,能够拿到参数的原型对象
  let proto = Object.getPrototypeOf(left);
  while(proto) {                  //循环往下寻找,直到找到相同的原型对象
    if(proto === right.prototype) return true;//找到相同原型对象,返回true
    proto = Object.getPrototypeof(proto);
    }
  return false;
}
// 验证一下自己实现的myInstanceof是否OK
console.log(myInstanceof(new Number(123), Number));    // true
console.log(myInstanceof(123, Number));                // false

全局通用类型判断方法

function getType(obj){
  let type  = typeof obj;
  if (type !== "object") {    // 先进行typeof判断,如果是基础数据类型,直接返回
    return type;
  }
  // 对于typeof返回结果是object的,再进行如下的判断,正则返回结果
  return Object.prototype.toString.call(obj).replace(/^\[object (\S+)\]$/, '$1');  // 注意正则中间有个空格
}

3.深浅拷贝

浅拷贝

object.assign、扩展运算符、concat、slice
手写:

const shallowClone = (target) => {
  if (typeof target === 'object' && target !== null) {
    const cloneTarget = Array.isArray(target) ? []: {};
    for (let prop in target) {
      if (target.hasOwnProperty(prop)) {  
          cloneTarget[prop] = target[prop];
      }
    }
    return cloneTarget;
  } else {
    return target;
  }
}

深拷贝

1.JSON.parse(JSON.stringfy())
缺点:

  1. 拷贝的对象的值中如果有函数、undefined、symbol 这几种类型,经过 JSON.stringify 序列化之后的字符串中这个键值对会消失;
  2. 拷贝 Date 引用类型会变成字符串;
  3. 无法拷贝不可枚举的属性;
  4. 无法拷贝对象的原型链;
  5. 拷贝 RegExp 引用类型会变成空对象;
  6. 对象中含有 NaN、Infinity 以及 -Infinity,JSON 序列化的结果会变成 null;
  7. 无法拷贝对象的循环引用,即对象成环 (obj[key] = obj)。

2.手写递归

let deepClone = (target) => {
    if (typeof target === 'object' && target !== null) {
        let cloneTarget = Array.isArray(target) ? [] : {};
        for (let key in target) {
            if (target.hasOwnProperty(key)) {
                cloneTarget[key] = deepClone(target[key]);
            }
        }
        return cloneTarget;
    } else {
        return target;
    }
}

缺点:

  1. 不能拷贝不可遍历属性
  2. 对Date、RegExp、Error、Function 这样的引用类型并不能正确地拷贝;
  3. 循环引用没有解决

3.递归进阶版

  1. 针对能够遍历对象的不可枚举属性以及 Symbol 类型,我们可以使用 Reflect.ownKeys 方法;
  2. 当参数为 Date、RegExp 类型,则直接生成一个新的实例返回;
  3. 利用 Object 的 getOwnPropertyDescriptors 方法可以获得对象的所有属性,以及对应的特性,顺便结合 Object 的 create 方法创建一个新对象,并继承传入原对象的原型链;
  4. 利用 WeakMap 类型作为 Hash 表,因为 WeakMap 是弱引用类型,可以有效防止内存泄漏(你可以关注一下 Map 和 weakMap 的关键区别,这里要用 weakMap),作为检测循环引用很有帮助,如果存在循环,则引用直接返回 WeakMap 存储的值。
const isComplexDataType = obj => (typeof obj === 'object' || typeof obj === 'function') && (obj !== null)

let deepClonePlus = (target, hash = new WeakMap()) => {
    if (target instanceof Date) return new Date(target);
    if (target instanceof RegExp) return new RegExp(target);

    if (hash.has(target)) return hash.get(target);

    let allDesc = Object.getOwnPropertyDescriptors(target);
    let cloneTarget = Object.create(Object.getPrototypeOf(target), allDesc);

    hash.set(target, cloneTarget);
    for (let key of Reflect.ownKeys(target)) {
        cloneTarget[key] = (isComplexDataType(target[key]) && typeof target[key] !== 'function') ? deepClonePlus(target[key], hash) : target[key];
    }
    return cloneTarget;
}

4. 6种继承方式

在这里插入图片描述

5. new、apply、call、bind

function _new(ctor, ...args) {
    if(typeof ctor !== 'function'){
        throw "ctor must be a function"
    }
    let obj = {};
    obj.__proto__ = ctor.prototype;//访问构造函数原型所在原型链上属性
    let res = ctor.apply(obj,[...args]);//访问私有属性并返回值
    return res instanceof Object ? res:obj;
}
Function.prototype._call = function(ctx, ...args) {
    ctx = ctx || window;
    ctx.fn = this;
    let res = ctx.fn(...args);
    delete ctx.fn;
    return res;
}
Function.prototype.apply = function (ctx, args) {
    ctx = ctx || window;
    ctx.fn = this;
    let res = ctx.fn(...args);
    delete ctx.fn;
    return res;
}
Function.prototype.bind = function (ctx, ...args) {
    if (typeof this !== 'function') {
        throw new Error("this must be s function");
    }
    let self = this;
    let fbound = function () {
    //如果当前函数的this指向的是构造函数中的this 则判定为new 操作
        self.apply(this instanceof self ? this : ctx, args.concat(Array.prototype.slice.call(arguments)))
    }
    fbound.prototype = this.prototype;
    return fbound;
}

6.闭包

闭包其实就是一个可以访问其他函数内部变量的函数。即一个定义在函数内部的函数,或者直接说闭包是个内嵌函数也可以。

作用域

指变量能够被访问到的范围:全局作用域、函数作用域、块级作用域

作用域链

当访问一个变量时,代码解释器会首先在当前的作用域查找,如果没找到,就去父级作用域去查找,直到找到该变量或者不存在父级作用域中,这样的链路就是作用域链。

7.手写JSON.stringfy

额,放弃

8.数组API总结

数组构造器

ES5:Array()
ES6:Array.of 、Array.from

数组判断

ES5:

var a = [];
// 1.基于instanceof
a instanceof Array;
// 2.基于constructor
a.constructor === Array;
// 3.基于Object.prototype.isPrototypeOf
Array.prototype.isPrototypeOf(a);
// 4.基于getPrototypeOf
Object.getPrototypeOf(a) === Array.prototype;
// 5.基于Object.prototype.toString
Object.prototype.toString.apply(a) === '[object Array]';

ES6:Array.isArray

基本方法

在这里插入图片描述

9.类数组

  1. 函数里面的参数对象 arguments;
  2. 用 getElementsByTagName/ClassName/Name 获得的 HTMLCollection;
  3. 用 querySelector 获得的 NodeList。
    在这里插入图片描述
    callee是arguments的属性

10.数组扁平化

在这里插入图片描述

普通递归

function flatten1(arr) {
    let res = [];
    for (let i = 0; i < arr.length; i++) {
        if (Array.isArray(arr[i])) {
            res = res.concat(flatten1(arr[i]));
        } else {
            res.push(arr[i]);
        }
    }
    return res;
}

reduce

function flatten2(arr) {
    let res = arr.reduce((pre, cur) => {
        return pre.concat(Array.isArray(cur) ? flatten2(cur) : cur);
    }, [])
    return res;
}

扩展运算符

function flatten3(arr) {
    while (arr.some(item => Array.isArray(item))) {
        arr = [].concat(...arr);
    }
    return arr;
}

toString + split

不过都变成了string ,需要转换

function flatten4(arr){
    arr = arr.toString().split(",").map(item=>parseInt(item));
    return arr;
}

ES6 的 Flat

function flatten5(arr){
    return arr.flat(Infinity);
}

JSON + 正则

function flatten6(arr) {
    let str = JSON.stringify(arr);
    str = str.replace(/(\[|\])/g, '');
    str = '[' + str + ']';
    return JSON.parse(str); 
}

11.数组排序

在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。
在这里插入图片描述

12.sort

  1. 当 n<=10 时,采用插入排序;
  2. 当 n>10 时,采用三路快速排序;
  3. 10<n <=1000,采用中位数作为哨兵元素;
  4. n>1000,每隔 200~215 个元素挑出一个元素,放到一个新数组中,然后对它排序,找到中间位置的数,以此作为中位数。

13. 手写数组方法

数组方法V8 源码地址
popV8 源码 pop 的实现
push

14.JS异步方案

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

15. EventEmitter

设计模式:发布订阅模式
扩展:观察者模式、工厂模式、构造函数模式、单例模式

简陋版手写

class EventEmitter {
    constructor() {
        this._events = {};
    }

    on(eventName, listener) {
        if (!this._events[eventName]) {
            this._events[eventName] = [listener];
        } else {
            this._events[eventName].push(listener);
        }
    }

    emit(eventName, ...args) {
        this._events[eventName] && this._events[eventName].forEach(f => {
            f.apply(this, args);
        });
    }

    off(eventName, listener) {
        if (this._events[eventName]) {
            this._events[eventName] = this._events[eventName].filter(f => {
                return f !== listener;
            })
        }
    }

    once(eventName, listener) {
        const fn = (...args) => {
            listener.apply(this, args);
            this.remove(eventName, fn);
        }
        this.on(eventName, fn);
    }
}

升级版手写

在这里插入代码片

16.Promise

手写Promise

简陋版

无法实现then的链式调用

const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
function Promise(executor) {
    let _this = this;
    this.state = PENDING;
    this.value = undefined;
    this.result = undefined;

    this.onFulfilled = [];
    this.onRejected = [];
    function resolve(value) {
        if (_this.state === PENDING) {
            _this.state = FULFILLED;
            _this.value = value;
            _this.onFulfilled.forEach(fn => fn(value));
        }
    }

    function reject(result) {
        if (_this.state === PENDING) {
            _this.state = REJECTED;
            _this.result = result;
            _this.onRejected.forEach(fn => fn(result));
        }
    }

    try {
        executor(resolve, reject);
    } catch (e) {
        reject(e);
    }
}

Promise.prototype.then = function (onFulfilled, onRejected){
    if (this.state === FULFILLED) {
        typeof onFulfilled === 'function' && onFulfilled(this.value);
    }
    if (this.state === REJECTED) {
        typeof onRejected === 'function' && onRejected(this.result);
    }
    if(this.state === PENDING){
        typeof onFulfilled === 'function' && this.onFulfilled.push(onFulfilled);
        typeof onRejected === 'function' && this.onRejected.push(onRejected)
    }
}

var p = new Promise((resolve, reject)=>{
    setTimeout(()=>{
        resolve(4)
    }, 1000)
})
p.then((res)=>{
    //4 res
    console.log(res, 'res')
})
p.then((res1)=>{
    //4 res1
    console.log(res1, 'res1')
})
升级标准版

略长,最后看吧

function Promise(executor) {
    var self = this
    self.status = 'pending'
    self.onResolvedCallback = []
    self.onRejectedCallback = []
    function resolve(value) {
        if (value instanceof Promise) {
            return value.then(resolve, reject)
        }
        setTimeout(function () { // 异步执行所有的回调函数
            if (self.status === 'pending') {
                self.status = 'resolved'
                self.data = value
                for (var i = 0; i < self.onResolvedCallback.length; i++) {
                    self.onResolvedCallback[i](value)
                }
            }
        })
    }
    function reject(reason) {
        setTimeout(function () { // 异步执行所有的回调函数
            if (self.status === 'pending') {
                self.status = 'rejected'
                self.data = reason
                for (var i = 0; i < self.onRejectedCallback.length; i++) {
                    self.onRejectedCallback[i](reason)
                }
            }
        })
    }
    try {
        executor(resolve, reject)
    } catch (reason) {
        reject(reason)
    }
}
function resolvePromise(promise2, x, resolve, reject) {
    var then
    var thenCalledOrThrow = false
    if (promise2 === x) {
        return reject(new TypeError('Chaining cycle detected for promise!'))
    }
    if (x instanceof Promise) {
        if (x.status === 'pending') {
            x.then(function (v) {
                resolvePromise(promise2, v, resolve, reject)
            }, reject)
        } else {
            x.then(resolve, reject)
        }
        return
    }
    if ((x !== null) && ((typeof x === 'object') || (typeof x === 'function'))) {
        try {
            then = x.then
            if (typeof then === 'function') {
                then.call(x, function rs(y) {
                    if (thenCalledOrThrow) return
                    thenCalledOrThrow = true
                    return resolvePromise(promise2, y, resolve, reject)
                }, function rj(r) {
                    if (thenCalledOrThrow) return
                    thenCalledOrThrow = true
                    return reject(r)
                })
            } else {
                resolve(x)
            }
        } catch (e) {
            if (thenCalledOrThrow) return
            thenCalledOrThrow = true
            return reject(e)
        }
    } else {
        resolve(x)
    }
}
Promise.prototype.then = function (onResolved, onRejected) {
    var self = this
    var promise2
    onResolved = typeof onResolved === 'function' ? onResolved : function (v) {
        return v
    }
    onRejected = typeof onRejected === 'function' ? onRejected : function (r) {
        throw r
    }
    if (self.status === 'resolved') {
        return promise2 = new Promise(function (resolve, reject) {
            setTimeout(function () { // 异步执行onResolved
                try {
                    var x = onResolved(self.data)
                    resolvePromise(promise2, x, resolve, reject)
                } catch (reason) {
                    reject(reason)
                }
            })
        })
    }
    if (self.status === 'rejected') {
        return promise2 = new Promise(function (resolve, reject) {
            setTimeout(function () { // 异步执行onRejected
                try {
                    var x = onRejected(self.data)
                    resolvePromise(promise2, x, resolve, reject)
                } catch (reason) {
                    reject(reason)
                }
            })
        })
    }
    if (self.status === 'pending') {
        // 这里之所以没有异步执行,是因为这些函数必然会被resolve或reject调用,而resolve或reject函数里的内容已是异步执行,构造函数里的定义
        return promise2 = new Promise(function (resolve, reject) {
            self.onResolvedCallback.push(function (value) {
                try {
                    var x = onResolved(value)
                    resolvePromise(promise2, x, resolve, reject)
                } catch (r) {
                    reject(r)
                }
            })
            self.onRejectedCallback.push(function (reason) {
                try {
                    var x = onRejected(reason)
                    resolvePromise(promise2, x, resolve, reject)
                } catch (r) {
                    reject(r)
                }
            })
        })
    }
}
Promise.prototype.catch = function (onRejected) {
    return this.then(null, onRejected)
}

手写all、any、race

all
Promise.prototype.myAll = function (array) {
    if (!Array.isArray(array)) return new Error("argument must be an array!");
    let result = new Array(array.length), count = 0;
    return new Promise((resolve, reject) => {
        array.forEach((item, index) => {
            Promise.resolve(item)
                .then(data => {
                    result[index]=data;
                    count++;
                    if (count === array.length) {
                        resolve(result);
                    }
                }).catch(err => {
                    reject(err);
                })
        })
    })
}

var promise1 = Promise.resolve(3);
var promise2 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 100, 'foo');
});
var promise3 = 42;

Promise.prototype.myAll([promise1, promise2, promise3]).then(function (values) {
    console.log(values);
});

race
Promise.prototype.myRace = function (array) {
    if (!Array.isArray(array)) { return new Error('argument must be a Array') }

    return new Promise((resolve, reject) => {
        array.forEach(item => {
            Promise.resolve(item).then(data => {
                resolve(data)
            }).catch(err => {
                reject(err)
            })
        })
    })
}

var promise1 = new Promise(function(resolve, reject) {
    setTimeout(resolve, 500, 'one');
});
var promise2 = new Promise(function(resolve, reject) {
    setTimeout(resolve, 100, 'two');
});
Promise.race([promise1, promise2]).then(function(value) {
  console.log(value);
});
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值