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;
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容