JS必敲

instanceof操作符的实现

作用

instanceof的作用是查看构造函数是否在实例对象的原型链中。

使用

console.log([] instanceof Array); // true

实现

  • 思路:获取实例化对象中的原型Object.getPrototypeOf(),判断是否和给定的原型一致。
function instanceofFunction(left, right) {
    let proto = Object.getPrototypeOf(left);
    let targetPrototype = right.prototype;
    while (proto) {
        if (proto == targetPrototype) return true;
        proto = Object.getPrototypeOf(proto);
    }
    return false;
}

Function.prototype.apply()函数的实现

作用

apply()函数的作用是改变函数中的上下文(this);原函数中的接收的参数使用数组或类数组接收。

使用

let person1 = {
  name: "person1",
  age: 18,
  getName: function() {
    return this.name;
  }
}

let person2 = {
  name: "person2",
  age: 19
}

console.log(person1.getName()) // person1
console.log(person1.getName.apply(person2)) // person2

实现

  • 思路:先确定上下文,然后给上下文添加属性;执行当前的函数,执行完后删除添加的属性,返回结果。
Object.prototype.apply2 = function(obj, args = []) {
    // 判断当前是否执行一个函数
    if (typeof this !== "function") {
        throw new Error("Type Error")
    }
    // 上下文确定, 基本类型不能添加属性
    obj = obj || window;
    if (typeof obj !== 'object' && typeof obj !== 'function') {
        obj = window;
    }
    // 给修改后的上下文添加一个属性,该属性的值为当前的执行函数
    const symbol = Symbol('apply2');
    obj[symbol] = this;
    const res = obj[symbol](...args)
    // 复原:删除第二步添加的属性
    delete obj[symbol]
    // 返回结果
    return res;
}

Function.prototype.call()函数的实现

作用

call()函数的作用是改变函数中的上下文(this);原函数中的接收的参数使用多个参数进行接收

使用

let person1 = {
  name: "person1",
  age: 18,
  getName: function() {
    return this.name;
  }
}

let person2 = {
  name: "person2",
  age: 19
}

console.log(person1.getName()) // person1
console.log(person1.getName.call(person2)) // person2

实现

  • 思路:先确定上下文,然后给上下文添加属性;执行当前的函数,执行完后删除添加的属性,返回结果。
Object.prototype.call2 = function(obj, ...param) {
    // 判断当前是否执行一个函数
    if (typeof this !== "function") {
        throw new Error("not function");
    }
    // 上下文确定, 基本类型不能添加属性
    obj = obj || window;
    if (typeof obj !== "object" && typeof obj !== "function") {
        obj = window;
    }
    // 给修改后的上下文添加一个属性,该属性的值为当前的执行函数
    const tempProperty = Symbol.for("temp");
    obj[tempProperty] = this;
    const res = obj[tempProperty](...param);
    // 复原:删除第二步添加的属性
    delete obj[tempProperty];
    // 返回结果
    return res;
}

Function.prototype.bind()函数的实现

作用

bind()函数的作用将某个函数绑定到对象上,函数的第一个参数是函数中的上下文(this),后面的参数是函数需要的参数,与call()函数相似。bind()函数并不立即执行。

使用

var name = "window";
function getName() {
    return this.name;
}
let person1 = {
    name: "person1",
    age: 18,
}
let getName2 = getName.bind(person1);

console.log(getName()) // window
console.log(getName2()) // person1

实现

  • 思路:使用apply/call进行实现。需要注意的是,bind函数不是立即执行,需要返回一个函数。
Object.prototype.bind2 = function(context, ...param) {
    // 判断当前是否执行一个函数
    if (typeof this !== "function") {
        throw new Error("not a function")
    }
    
    let self = this;
    // bind返回一个函数
    return function() {
        // 使用apply进行函数实现
        return self.apply(context, param);
    }
}

深拷贝和浅拷贝

  • 深拷贝:表示拷贝之后,两个对象没有关系。
  • 浅拷贝:表示拷贝之后,对象中基本类型的属性之间是没有关系的,对象中引用类型的属性之间是相互关联的。

深拷贝

  • 思路:判断是基本类型、对象和函数等,进行递归创建。
function deepCopy(origin) {
  let result = origin instanceof Array ? [] : {};
  for ( let key in origin ) {
    if (typeof origin[key] == "object") {
      result[key] = deepCopy(origin[key]);
    } else {
      result[key] = origin[key];
    }
  }
  return result;
}

浅拷贝

for... in ...
function simpleCopy(origin) {
  let result = {};
  for (let key in origin) {
    result[key] = origin[key]
  }
  return result;
}

Object.assign()
function simpleCopy(origin) {
  return Object.assign({}, origin);
}

展开语法
function simpleCopy(origin) {
  return {...origin}
}

性能优化

防抖、节流都需要闭包进行防重复设置。【不要开启多个setTimeout】
防抖、节流主要使用到的是闭包的特性:能够保持外部作用域的变量,并且能够内部函数可以使用外部函数的变量。

防抖

function fangdou() {
    let timeId = null;
    let fn = arguments[0];
    let time = arguments[1];
    return function() {
        let context = this;
        let arg = arguments;
        clearTimeout(timeId);
        timeId = setTimeout(function() {
            // 传递参数
            fn.call(context, ...arg)
        }, time)
    }
}

节流

n秒内只有执行一次。注意触发条件

function jieliu() {
    let timeId = null;
    let fn = arguments[0];
    let time = arguments[1];
    return function() {
        let context = this;S
        let arg = arguments;
        if (!timeId) {
            timeId = setTimeout(function() {
                timeId = null;
                fn.apply(context, arg)
            }, time)
        }
    }
}

封装一个网络请求

function Ajax(url, method, asyncValue = true) {
    return new Promise((resolve, reject) => {
        let xhr = new XMLHttpRequest();
        xhr.open(method, url, asyncValue);
        xhr.onreadystatechange = function() {
            if (xhr.readyState == 4) {
                if (xhr.status == 200) {
                    resolve(xhr.responseText)
                } else {
                    reject()
                }
            }
        }
        xhr.send()
    })
}

数组

forEach方法

  • 参数:
    forEach方法有两个参数,第一个是回调函数;第二个是上下文。其中回调函数中的参数依次是:数组中的值,数组的下标、整个数组
  • 返回值:
    undefined
使用
let array = [1,2,3,4,5];
array.forEach(function(item, index, arr) {
  console.log(item);
})
实现
Array.prototype.forEach1 = function() {
    // 表示当前的array
    let array = this;
    // 获取回调函数
    let fn = arguments[0];
    let context = arguments[1] || window;
    // 遍历数组
    for(let i=0; i<array.length; i++) {
        fn.call(context, array[i], i, array);
    }
}

map方法

  • 参数:
    map方法有两个参数,第一个是回调函数;第二个是上下文。其中回调函数中的参数依次是:数组中的值,数组的下标、整个数组

  • 返回值:
    回调函数中返回值组成的数组列表。

使用
let array = [1,2,3,4,5];
let result = array.map(function(item, index, array) {
    console.log(item)
    return item;
})

console.log(result) // [1, 2, 3, 4, 5]
实现
Array.prototype.map1 = function() {
    // 表示当前的array
    let array = this;
    // 获取回调函数
    let fn = arguments[0];
    let context = arguments[1];
    // map的返回值
    let result = []
    // 遍历数组
    for (let i = 0; i < array.length; i++) {
        result.push(fn.call(context, array[i], i, array));
    }
    return result;
}

every方法

  • 参数:
    every方法有两个参数,第一个是回调函数;第二个是上下文。其中回调函数中的参数依次是:数组中的值,数组的下标、整个数组。

  • 返回值:
    回调函数中返回值全为true,返回true;出现一个false就返回false。

使用
let array = [1,2,3,4,5];
let result = array.every(function(item, index, array) {
    console.log(item)
    return true;
})

console.log(result) // 1,2,3,4,5  true
实现
Array.prototype.every1 = function() {
    // 表示当前的array
    let array = this;
    // 获取回调函数
    let fn = arguments[0];
    let context = arguments[1];
    // 遍历数组,如果有false出现,则退出
    for (let i = 0; i < array.length; i++) {
        if (!fn.call(context, array[i], i, array)){
            return false;
        }
    }
    return true;
}

some方法

  • 参数:
    some方法有两个参数,第一个是回调函数;第二个是上下文。其中回调函数中的参数依次是:数组中的值,数组的下标、整个数组。

  • 返回值:
    回调函数中返回值全为false,返回false;出现一个true就返回true。

使用
let array = [1,2,3,4,5];
let result = array.some(function(item, index, array) {
    console.log(item)
    return true;
})

console.log(result) // 1  true
实现
Array.prototype.some1 = function() {
    // 表示当前的array
    let array = this;
    // 获取回调函数
    let fn = arguments[0];
    let context = arguments[1];
    // 遍历数组,如果有false出现,则退出
    for (let i = 0; i < array.length; i++) {
        if (fn.call(context, array[i], i, array)) {
            return true;
        }
    }
    return false;
}

filter方法

  • 参数:
    filter方法有两个参数,第一个是回调函数;第二个是上下文。其中回调函数中的参数依次是:数组中的值,数组的下标、整个数组。

  • 返回值:
    回调函数中返回值true,返回值的数组中有此值;返回false,返回值的数组中没有此值;。

filter是浅拷贝。

使用
let array = [[1,2,3], 2, 3, 4, 5]

let result = array.filter(function(item, index, array) {
    console.log(item)
    if (index === 0) {
        return true;
    }
    return false;
})

result[0][0] = 0;

console.log(result) // [[0,2,3]]
console.log(array)// [[0,2,3], 2, 3, 4, 5]

实现
Array.prototype.filter1 = function() {
    // 表示当前的array
    let array = this;
    // 获取回调函数
    let fn = arguments[0];
    let context = arguments[1];
    // 返回值的数组
    let result = []
    // 遍历数组,如果有false出现,则退出
    for (let i = 0; i < array.length; i++) {
        if (fn.call(context, array[i], i, array)) {
            result.push(array[i]);
        }
    }
    return result;
}

reduce方法

  • 参数:
    filter方法有两个参数,第一个是回调函数;第二个是初始值。其中回调函数中的参数依次是:前一个返回的值,当前值、当前下标、整个数组。

  • 返回值:
    回调函数中返回值true,返回值的数组中有此值;返回false,返回值的数组中没有此值;。

reduce没有指定的上下文这个参数

使用
let array = [1, 2, 3, 4, 5]

let result = array.reduce(function(pre, cur, index, array) {
    console.log(cur)
    return pre + cur;
}, 0)

console.log(result) // 15

实现
Array.prototype.reduce1 = function() {
    // 表示当前的array
    let array = this;
    // 获取回调函数
    let fn = arguments[0];
    let init = arguments[1];
    // 判断是否有初始值
    let start = 0;
    if (init == undefined) {
        init = array[start++];
    }
    while (start < array.length) {
        init = fn(init, array[start], start++, array);
    }
    return init;
}

Promise的实现

Promise.all的实现

作用:多个异步调用函数,并且多个异步调用函数的参数和结果都无必然联系。多个异步函数的执行只关注成功或失败结果。
每一个都返回成功,才返回成功;否则,将第一个失败作为整个失败的结果。
参数:

  • 数组,可以是基本类型,也可以是Promise对象
    返回值
  • Promise对象或Promise对象数组
实现
Promise.all = function(params) {
    return new Promise((resolve, reject) => {
        // 先判断参数是否是数组
        if (Array.isArray(params)) {
            return reject("params must be an array");
        }
        let length = params.length;
        let count = 0; // 记录当前接收到的个数
        let result = new Array(length); // 与参数的位置一一对应
        // 进行一个一个执行,
        for (let i=0; i<length; i++) {
            // 使用Promise.resolve防止不是一个Promise对象
            Promise.resolve(params[i]).then((value) => {
                // 与参数的位置一一对应
                result[i] = value; 
                count++;
                if (count === length) {
                    return resolve(result);
                }
            }, reason => {
                return reject(reason);
            })
        }
    })
}

Event Bus的实现

Event Bus是消息传递的一种方式,基于一个消息中心,订阅和发布消息的格式,成为发布订阅者模式。

使用

  • $on("name", fn):订阅消息,name表示订阅的消息名称, fn表示订阅的消息
  • $emit("name", args):发布消息,name表示发布的消息名称,args表示发布的消息
实现
class EventBus {
    constructor() {
        this.callback = {};
    }
    $on(name, fn) {
        if (!this.callback.hasOwnProperty(name)) {
            this.callback[name] = [];
        }
        this.callback[name].push(fn);
    }
    $emit(name, args) {
        if (this.callback.hasOwnProperty(name)) {
            this.callback.forEach(function(item, index) {
                item(args)
            })
        }
    }
}

解析XML文档

function parseXML(str) {
    let regXML = /^</; // 判断是否xml格式
    let reg = /<([\w|\W]+)>([\w|\W]*)<\/\1>/g; // 解析xml格式
    let obj = {}; // 如果是xml格式,返回object
    str = str.trim(); // 删除两边的空白符
    // xml文档的解析
    if (regXML.test(str)) {
        let result;
        while (result = reg.exec(str)) {
            obj[result[1]] = parseXML(result[2]);
        }
        return obj;
    } else {
        return str;
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容