前端面试常考代码题

节流与防抖

防抖

高频触发 只执行最后一次 常应用于用户进行搜索输入节约请求资源,window触发resize事件时进行防抖只触发一次。

const debounce = (fn, time) => {
  let timeout = null;
  return function() {
    clearTimeout(timeout)
    timeout = setTimeout(() => {
      fn.apply(this, arguments);
    }, time);
  }
};

节流

高频触发 n秒内函数只会执行一次 常应用于鼠标不断点击触发、监听滚动事件。

// 时间戳版本
function throttle(fn, wait) {    
    let previous = 0;// 上一次执行时间
    return function(...args) {
        let now = +new Date();// 当前时间
        if (now - previous > wait) {
            previous = now;
            fn.apply(this, args);
        }
    }
}
// 定时器版本
function throttle(fn, wait) {
    let timer = null;
    return function(...args) {
        if (!timer) {
            timer = setTimeout(() => {
                fn.apply(this, args);
                timer = null;
            }, wait)
        }
    }
}

浅拷贝 深拷贝

浅拷贝

  1. =
let a=[0,1,2,3,4],
    b=a;
  1. object.asign
var obj = { a: {a: "hello", b: 21} };
var initalObj = Object.assign({}, obj);

深拷贝

// 普通版
function cloneDeep(source) {
    // 如果输入的为基本类型,直接返回
    if (!(typeof source === 'object' && source !== null)) {
        return source;
    }
    // 判断输入的为数组函数对象,进行相应的构建
    const target = Array.isArray(source) ? [] : {};
    for (let key in source) {
        // 判断是否是自身属性
        if (Object.prototype.hasOwnProperty.call(source, key)) {
            if (typeof source === 'object' && source !== null) {
                target[key] = cloneDeep2(source[key]);
            } else {
                target[key] = source[key];
            }
        }
    }
    return target;
}
// 解决循环引用问题
function cloneDeep(source){
    let visitedMap = new Map();
    function clone (source) {
        // 如果输入的不是对象,直接返回
        if (!(typeof source === 'object' && source !== null)) return source;
        if (visitedMap.get(source)) return visitedMap.get(source);
        // 判断输入的为数组函数对象,进行相应的构建
        const target = Array.isArray(source) ? [] : {};
        visitedMap.set(source, target);
        for (let key in source) {
            // 判断是否是自身属性
            if (Object.prototype.hasOwnProperty.call(source, key)) {
                if (typeof source === 'object' && source !== null) {
                    target[key] = cloneDeep(source[key]);
                } else {
                    target[key] = source[key];
                }
            }
        }
        return target;
    }
    return clone(source);
}

flat

function flatDeep(arr){// 打平一层
	return [].concat(...arr);
}

function flatDeep(arr, d = 1) {// 打平多层
   return d > 0 ? arr.reduce((acc, val) => acc.concat(Array.isArray(val) ? 
   			flatDeep(val, d - 1) : val), []) : arr.slice();
};

排序(升序)

冒泡排序

从头开始比较每一对相邻元素 如果第一个比第二个大 就交换他们的位置 执行完一轮 最后一个元素就是最大元素 忽略上一次找到的最大元素 再执行上述循环 直到元素全部有序

function bubbleSort(array){
    // 在经过几轮之后 数组尾部已经有序 那么我们可以在每一轮记录最后一次交换的位置 减少比较次数
    for (let end = array.length - 1; end > 0 ; end--) {
        /*
        sortedIndex的初始值在数组完全有序的时候有用 假设数组是有序数组 第一轮进来sortedIndex = 1
        不进入下面的for循环(因为数组有序) 然后end = 1 这一轮执行完毕 begin = 2 < end = 1 结束
         */
        let sortedIndex = 1;//从哪个索引开始 后面的元素全部有序
        for (let begin = 1; begin <= end; begin++) {
            if (array[begin] < array[begin - 1]){//右边的小 交换
                let tmp = array[begin];
                array[begin] = array[begin - 1];
                array[begin - 1] = tmp;
                sortedIndex = begin;
            }
        }
        end = sortedIndex;
    }
}

选择排序

从序列中找出最大的元素 然后与最末端的元素交换位置 执行完一轮 最末端的元素就是最大元素 忽略末端最大的元素 循环执行以上查找交换步骤

function SelectionSort() {
    for (let end = array.length - 1; end > 0 ; end--) {
        let maxIndex = 0;
        for (let begin = 1; begin <= end; begin++) {
            if (array[maxIndex] <= array[begin]){
                maxIndex = begin;
            }
        }
        let tmp = array[maxIndex];
        array[maxIndex] = array[end];
        array[end] = tmp;
    }
}
let array = [9,8,7,6,5,4,3,2,1];
SelectionSort();
console.log(array);

插入排序

function InsertionSort() {
    for (let begin = 1; begin < array.length; begin++) {
        insert(begin,search(begin));
    }
}
function insert(source, dest) {//将source位置的元素插入到dest位置
    let v = array[source];
    for (let i = source; i > dest; i--) {
        array[i] = array[i - 1];
    }
    array[dest] = v;
}
function search(index){
    let begin = 0;
    let end = index;
    while (begin < end){
        let mid = (begin + end) >> 1;
        if (array[index] < array[mid]){
            end = mid;
        }else {
            begin = mid + 1;
        }
    }
    return begin;//begin = end
}
let array = [9,8,7,6,5,4,3,2,1];
InsertionSort();
console.log(array);

快速排序

function pivotIndex(begin, end){//构造出[begin,end)范围的轴点元素 返回值是轴点元素的最终位置
    //随机选择一个元素和begin位置的元素交换
    let r = Math.floor(Math.random() * (end - begin));
    let temp = array[begin];
    array[begin] = array[begin + r];
    array[begin + r] = temp;
    // swap(begin, begin + r);
    let pivot = array[begin];//备份begin位置的元素
    end--;//end指向最后一个元素
    while (begin < end){
        while (begin < end){
            if (pivot < array[end]){//右边元素>轴点元素
                end--;
            }else {//右边元素<=轴点元素
                array[begin++] = array[end];
                break;
            }
        }
        while (begin < end){
            if (pivot > array[begin]){//左边元素<轴点元素
                begin++;
            }else {//左边元素>=轴点元素
                array[end--] = array[begin];
                break;
            }
        }
    }
    array[begin] = pivot;//将轴点元素放入最终位置
    return begin;//返回轴点元素的位置
}
let array = [9,8,7,6,5,4,3,2,1]
Quicksort(0,9);
console.log(array);

归并排序

function MergeSort (begin, end) {// [)
    if (end - begin < 2) return;
    let mid = (begin + end) >> 1;
    sort(begin, mid);
    sort(mid, end);
    merge(begin, mid, end);
}
function merge (begin, mid, end) {//将[begin,mid)和[mid,end)范围的序列合并成一个有序序列
    let li = 0, le = mid - begin;// li是左边数组的第一个索引 le是左边数组的最后一个索引
    let ri = mid, re = end;// ri是右边数组的第一个索引 re是右边数组的最后一个索引
    let ai = begin;// ai是合并数组的最后一个元素的索引+1
    let leftArray = [];
    for (let i = li; i < le; i++) {//备份左边数组
        leftArray[i] = array[begin + i];
    }
    while (li < le) {//如果左边还没结束
        if (ri < re && array[ri] < leftArray[li]) {
            array[ai++] = array[ri++];
        } else {//右边为空 则把左边剩余的元素依次移动到array中
            array[ai++] = leftArray[li++];
        }
    }
}
let array = [9,8,7,6,5,4,3,2,1]
MergeSort(0,9);
console.log(array);

instanceof

原理:只要右边变量的prototype在左边变量的原型链上即可

function new_instance_of(leftVaule, rightVaule) {
    let rightProto = rightVaule.prototype; // 取右表达式的 prototype 值
    leftVaule = leftVaule.__proto__; // 取左表达式的__proto__值
    while (true) {
        if (leftVaule === null) {
            return false;   
        }
        if (leftVaule === rightProto) {
            return true;   
        }
        leftVaule = leftVaule.__proto__
    }
}

promise实现sleep

function sleep(time) {
  return new Promise(resolve => setTimeout(resolve, time))
}

Ajax

const xhr = new XMLHttpRequest();
xhr.open('GET','http://127.0.0.1:8000/server');
xhr.send();
//有5个取值:0(表示未初始化 readystate最开始属性值就是0) 1(表示open方法调用完毕) 2(表示send方法调用完毕) 3(服务端返回了部分结果) 4(服务端返回了全部结果)
xhr.onreadystatechange = function (){
	if (xhr.readyState === 4){//判断(服务器返回了所有结果)
    	if (xhr.status >= 200 && xhr.status < 300){
    		//响应成功 处理结果 服务端返回的结果:行 头 空行 体
    		// console.log(xhr.status);//状态码
    		// console.log(xhr.statusText);//状态字符串
    		// console.log(xhr.getAllResponseHeaders());//所有响应头
    		// console.log(xhr.response);//响应体
    		result.innerHTML = xhr.response;//设置result的文本
        }
   	}
}

将AJAX封装成promise

/*** ajax数据请求接口
  	@param {string} type 请求方式 {"get(默认)" | "GET" | "post" | "POST"}
 	@param {string} url 请求接口地址
  	@param {Object} data 请求时后台所需参数
  	@param {bool} async 是否异步(true)或同步(false)请求{true(默认) | false}	  	
  	@example: $ajax({
  					type: "post", 
  					url: "", 
  					data: {}
  				}).then(function(ret){
  				
  				}).then(function(err){
  				
  				});
*/
var ajax = function({type, url, data, async}){
	let ajax;		
	ajax = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
	type = !type ? "get" : type;
	data = !data ? {} : data;
	async != false ? !async ? async = true : async = async : '';
	return new Promise(function(resolve,reject){
		let type = type.toUpperCase();
		// get 跟post  需要分别写不同的代码
		if (type === "GET") {// get请求
			if (data) {// 如果有值
				url += '?';										
				if( typeof data === 'object' ){ // 如果有值 从send发送
					var convertResult = "" ;
					for(var c in data){
						convertResult += c + "=" + data[c] + "&";
					}						
					url += convertResult.substring(0,convertResult.length-1);
				}else{
					url += data;
				}
			}
			ajax.open(type, url, async); // 设置 方法 以及 url
			ajax.send();// send即可
		}
		else if(type === "POST"){// post请求
			ajax.open(type, url); // post请求 url 是不需要改变
			// 需要设置请求报文
			ajax.setRequestHeader("Content-type","application/x-www-form-urlencoded"); 
			if(data){ // 判断data send发送数据
				if( typeof data === 'object' ){// 如果有值 从send发送
					var convertResult = "" ;
					for(var c in data){
					  convertResult += c + "=" + data[c] + "&";
					}
					convertResult = convertResult.substring(0,convertResult.length-1);
					ajax.send(convertResult);
				}else{
					ajax.send(data);
				}
			} else{
				ajax.send(); // 木有值 直接发送即可
			}
		}
		// 注册事件
		ajax.onreadystatechange = function () {
			// 在事件中 获取数据 并修改界面显示
			if (ajax.readyState == 4){
				if(ajax.status===200){ // 返回值: ajax.responseText;
					if(ajax.response && typeof ajax.response !== 'object'){
						resolve(JSON.parse(ajax.response));							
					}else{
						resolve(ajax.response);
					}
				}else{
					reject(ajax.status);
				}
			}
		}
	});		
}

promise

class Promise {
    //构造方法
    constructor(executor) {
        //添加状态属性与结果值属性
        this.PromiseState = 'pending';
        this.PromiseResult = null;
        // 定义callback属性,保存pending状态的回调函数
        this.callbacks = [];
        //保存实例对象的this值
        const that = this;

        //自定义resolve函数,名字不一定用resolve
        function resolve(data) {
            //判断状态是否修改过
            if (that.PromiseState !== 'pending') return;
            //改变状态属性
            that.PromiseState = 'fulfilled';  // 或者 resolve
            //改变结果值属性
            that.PromiseResult = data;
            //异步任务成功后执行回调函数
            setTimeout(() => {
                that.callbacks.forEach(item => {
                    item.onResolved(data);
                })
            });
        }

        //自定义reject函数
        function reject(data) {
            //判断状态是否修改过,改过就直接返回
            if (that.PromiseState !== 'pending') return;
            //改变状态属性
            that.PromiseState = 'rejected';
            //改变结果值属性
            that.PromiseResult = data;
            //异步任务失败后执行回调函数
            setTimeout(() => {
                that.callbacks.forEach(item => {
                    item.onRejected(data);
                })
            });
        }

        try {
            //同步调用【执行器函数】
            executor(resolve, reject);
        } catch (e) {
            //更改Promise对象为失败
            reject(e);
        }
    }

//then方法封装
    then(onResolved, onRejected) {
        const that = this;
        //判断回调参数是否存在
        if (typeof onRejected !== 'function') {
            onRejected = reason => {
                throw reason;
            }
        }
        if (typeof onResolved !== 'function') {
            onResolved = value => value;
        }
        return new Promise((resolve, reject) => {
            //封装重复的部分
            function callback(type) {
                try {
                    //将结果值传入
                    let result = type(that.PromiseResult);
                    //判断
                    if (result instanceof Promise) {
                        //如果是Promise对象
                        result.then(v => {
                            resolve(v);
                        }, r => {
                            reject(r);
                        })
                    } else {
                        //结果对象状态为【成功】
                        resolve(result);
                    }
                } catch (e) {
                    reject(e);
                }
            }

            //如果Promise状态为fulfilled回调这个函数
            if (this.PromiseState === 'fulfilled') {
                setTimeout(() => {
                    callback(onResolved);
                });
            }
            //如果Promise状态为rejected回调这个函数
            if (this.PromiseState === 'rejected') {
                setTimeout(() => {
                    callback(onRejected);
                });
            }
            //如果Promise状态为pending,保存回调函数
            if (this.PromiseState === 'pending') {
                this.callbacks.push({
                    onResolved: function () {
                        callback(onResolved);
                    },
                    onRejected: function () {
                        callback(onRejected);
                    }
                })
            }
        })
    }

//catch 方法
    catch(onRejected) {
        return this.then(undefined, onRejected);
    }

//resolve方法 不属于示例对象 它属于类 也就是我们这个构造函数Promise 所以我们需要用static作为关键字对他进行描述 表明这是个静态成员 它属于类而不属于实例对象
    static resolve(value) {
        //返回promise对象
        return new Promise((resolve, reject) => {
            if (value instanceof Promise) {
                value.then(v => {
                    resolve(v);
                }, r => {
                    reject(r);
                })
            } else {
                resolve(value);
            }
        })
    }

//reject方法
    static reject(reason) {
        return new Promise((resolve, reject) => {
            reject(reason);
        });
    }
}

promise.all

function all(promises) {
	return new Promise((resolve, reject) => {
		let count = 0;//添加变量
        let arr = [];// 存放成功结果数组
        for (let i = 0; i < promises.length; i++) {//遍历全部
        	promises[i].then(v => {
            	count++;//能进到证明其为成功
 				arr[i] = v;//保存成功结果
				if (count === promises.length) {//如果全部成功
                	resolve(arr);//状态为成功
                }
			}, r => {
				reject(r);//能进到证明其为失败
            });
		}
   });
}

promise.race

function race(promises) {
	return new Promise((resolve, reject) => {
    	for (let i = 0; i < promises.length; i++) {//遍历全部
        	promises[i].then(v => {//能进到证明其为成功
            	resolve(v);//状态为成功
            }, r => {//能进到证明其为失败
				reject(r);
            })
        }
		});
    }
}

数组去重

Set(ES6中最常用)

无法去掉“{}”空对象

function unique (arr) {
  return Array.from(new Set(arr))
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, 
			NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
//[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {}, {}]

for嵌套for,然后splice去重

双层循环,外层循环元素,内层循环时比较值。值相同时,则删去这个值。
NaN和{}没有去重,两个null直接消失了

function unique(arr){            
	for(var i=0; i<arr.length; i++){
    	for(var j=i+1; j<arr.length; j++){
        	if(arr[i]==arr[j]){         //第一个等同于第二个,splice方法删除第二个
            	arr.splice(j,1);
                j--;
            }
        }
    }
	return arr;
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, 
			NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
//[1, "true", 15, false, undefined, NaN, NaN, "NaN", "a", {…}, {…}]
//NaN和{}没有去重,两个null直接消失了

indexOf

新建一个空的结果数组,for 循环原数组,判断结果数组是否存在当前元素,如果有相同的值则跳过,不相同则push进数组。
NaN、{}没有去重

function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error!')
        return
    }
    var array = [];
    for (var i = 0; i < arr.length; i++) {
        if (array .indexOf(arr[i]) === -1) {
            array .push(arr[i])
        }
    }
    return array;
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, 
		NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
//[1, "true", true, 15, false, undefined, null, NaN, NaN, "NaN", 0, "a", {…}, {…}]  //NaN、{}没有去重

sort()

利用sort()排序方法,然后根据排序后的结果进行遍历及相邻元素比对。
NaN、{}没有去重

function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error!')
        return;
    }
    arr = arr.sort()
    var arrry= [arr[0]];
    for (var i = 1; i < arr.length; i++) {
        if (arr[i] !== arr[i-1]) {
            arrry.push(arr[i]);
        }
    }
    return arrry;
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, 
     		NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
//[0, 1, 15, "NaN", NaN, NaN, {…}, {…}, "a", false, null, true, "true", undefined]
//NaN、{}没有去重

includes

{}没有去重

function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error!')
        return
    }
    var array =[];
    for(var i = 0; i < arr.length; i++) {
            if( !array.includes( arr[i]) ) {//includes 检测数组是否有某个值
                    array.push(arr[i]);
              }
    }
    return array
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, 
			NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
//[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {…}, {…}]
//{}没有去重

hasOwnProperty(都能去重 完美)

利用hasOwnProperty 判断是否存在对象属性

function unique(arr) {
    var obj = {};
    return arr.filter(function(item, index, arr){
        return obj.hasOwnProperty(typeof item + item) ? false : 
        		(obj[typeof item + item] = true)
    })
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, 
			NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
//[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {…}]   
//所有的都去重了

filter

不能去{}

function unique(arr) {
  return arr.filter(function(item, index, arr) {
    //当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
    return arr.indexOf(item, 0) === index;
  });
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, 
			NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
//[1, "true", true, 15, false, undefined, null, "NaN", 0, "a", {…}, {…}]

递归去重

function unique(arr) {
        var array= arr;
        var len = array.length;

    array.sort(function(a,b){   //排序后更加方便去重
        return a - b;
    })

    function loop(index){
        if(index >= 1){
            if(array[index] === array[index-1]){
                array.splice(index,1);
            }
            loop(index - 1);    //递归loop,然后数组去重
        }
    }
    loop(len-1);
    return array;
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, 
			NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
//[1, "a", "true", true, 15, false, 1, {…}, null, NaN, NaN, "NaN", 0, "a", {…}, undefined]

Map

创建一个空Map数据结构,遍历需要去重的数组,把数组的每一个元素作为key存到Map中。由于Map中不会出现相同的key值,所以最终得到的就是去重后的结果。

function arrayNonRepeatfy(arr) {
  let map = new Map();
  let array = new Array();  // 数组用于返回结果
  for (let i = 0; i < arr.length; i++) {
    if(map .has(arr[i])) {  // 如果有该key值
      map .set(arr[i], true); 
    } else { 
      map .set(arr[i], false);   // 如果没有该key值
      array .push(arr[i]);
    }
  } 
  return array ;
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, 
			NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
//[1, "a", "true", true, 15, false, 1, {…}, null, NaN, NaN, "NaN", 0, "a", {…}, undefined]

reduce+includes

function unique(arr){
    return arr.reduce((prev,cur) => prev.includes(cur) ? prev : [...prev,cur],[]);
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, 
			NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr));
// [1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {…}, {…}]

[…new Set(arr)]

[...new Set(arr)] 

filter

在这里插入图片描述
filter(callback()[,thisArg])所以arguments[1]取的是thisArg
callback(element[, index[, array]])所以fn.call(arguments[1], arr[i], i, arr);传入arr[i], i, arr

Array.prototype.myFilter = function (fn, thisArg) {
    if (typeof fn !== 'function') {
        throw new TypeError(`${fn} is not a function`);
    }
    const arr = this;// 获取该数组
    const len = this.length >>> 0;// 获取该数组长度
    const temp = [];// 新建一个新的数组用于放置该内容
    for (let i = 0; i < len; i++) { // 对数组中每个值进行处理
        const result = fn.call(thisArg, arr[i], i, arr);// 处理时注意this指向
        result && temp.push(arr[i]);
    }
    return temp;
}

map

在这里插入图片描述

Array.prototype.myMap = function(fn, thisArg) {
    if (typeof fn !== 'function') {// 判断输入的第一个参数是不是函数
        throw new TypeError(fn + 'is not a function');
    } 
    const arr = this;// 获取需要处理的数组内容
    const len = arr.length;
    const temp = new Array(len);// 新建一个空数组用于装载新的内容
    for (let i = 0; i < len; i++) {// 对数组中每个值进行处理
        let result = fn.call(thisArg, arr[i], i, arr);// 获取第二个参数,改变this指向
        temp[i] = result;
    }
    return temp;// 返回新的结果
}

foreach

Array.prototype.forEach = function(callback, thisArg) {
  if (this == null) {
    throw new TypeError('this is null or not defined');
  }
  if (typeof callback !== "function") {
    throw new TypeError(callback + ' is not a function');
  }
	const arr = this;// 获取需要处理的数组内容
    const len = arr.length;
    for (let i = 0; i < len; i++) {// 对数组中每个值进行处理
		callback.call(thisArg, arr[i], i, arr);// 获取第二个参数,改变this指向
    }
}

new

function New (Fn, ...arg) {
    // 一个新的对象被创建
    const result = {};
    // 该对象的__proto__属性指向该构造函数的原型
    if (Fn.prototype !== null) {
        Object.setPrototypeOf(result, Fn.prototype);
    }
    // 将执行上下文(this)绑定到新创建的对象中
    const returnResult = Fn.apply(result, arg);
    // 如果构造函数有返回值,那么这个返回值将取代第一步中新创建的对象。否则返回该对象
    if ((typeof returnResult === "object" || typeof returnResult === "function") 
    	&& returnResult !== null) {
        return returnResult;
    }
    return result;
}

create

在这里插入图片描述

Object.ObjectCreate = (proto, propertiesObject)=> {
    // 对输入进行检测
    if (typeof proto !== 'object' && typeof proto !== 'function' && proto !== null) {
        throw new Error(`Object prototype may only be an Object or null:${proto}`);
    }
    // 新建一个对象
    const result = {};
    // 将该对象的原型设置为proto
    Object.setPrototypeOf(result, proto);
    // 将属性赋值给该对象
    Object.defineProperties(result, propertiesObject);
    // 返回该对象
    return result;
}

assign

在这里插入图片描述

function ObjectAssign(target, ...sources) {
    // 对第一个参数的判断,不能为undefined和null
    if (target === undefined || target === null) {
        throw new TypeError('cannot convert first argument to object');
    }

    // 将第一个参数转换为对象(不是对象转换为对象)
    const targetObj = Object(target);
    // 将源对象(source)自身的所有可枚举属性复制到目标对象(target)
    for (let i = 0; i < sources.length; i++) {
        let source = sources[i];
        // 对于undefined和null在源角色中不会报错,会直接跳过
        if (source !== undefined && source !== null) {
            // 将源角色转换成对象
            // 需要将源角色自身的可枚举属性(包含Symbol值的属性)进行复制
            // Reflect.ownKeys(obj)  返回一个数组,包含对象自身的所有属性,
            // 不管属性名是Symbol还是字符串,也不管是否可枚举
            const keysArray = Reflect.ownKeys(Object(source));
            for (let nextIndex = 0; nextIndex < keysArray.length; nextIndex ++) {
                const nextKey = keysArray[nextIndex];
                // 去除不可枚举属性
                const desc = Object.getOwnPropertyDescriptor(source, nextKey);
                if (desc !== undefined && desc.enumerable) {
                    // 后面的属性会覆盖前面的属性
                    targetObj[nextKey] = source[nextKey];
                }
            }
        }
    }

    return targetObj;
}
// 由于挂载到Object的assign是不可枚举的,直接挂载上去是可枚举的,所以采用这种方式
if (typeof Object.myAssign !== 'function') {
    Object.defineProperty(Object, "myAssign", {
        value : ObjectAssign,
        writable: true,
        enumerable: false,
        configurable: true
    });
}

reduce

在这里插入图片描述

Array.prototype.myReduce = function(fn) {
    if (typeof fn !== 'function') {
        throw new TypeError(`${fn} is not a function`);
    }

    const arr = this;
    const len = arr.length >>> 0;
    let value;// 最终返回的值
    let k = 0;// 当前索引

    if (arguments.length >= 2) {
        value = arguments[1];
    } else {
        // 当数组为稀疏数组时,判断数组当前是否有元素,如果没有索引加一
        while (k < len && !( k in arr)) {
            k++;
        }
        if (k >= len) {// 如果数组为空且初始值不存在则报错
            throw new TypeError('Reduce of empty array with no initial value');
        }
        value = arr[k++];
    }
    while (k < len) {
        if (k in arr) {
            value = fn(value, arr[k], k, arr);
        }
        k++;
    }

    return value;
}

call bind apply

call

Function.prototype.myCall = function (){// call是Function.prototype上的一个方法
    let thisArg = arguments[0];// 第一个参数是绑定的this
    let isStrict = (function () {return this === undefined}());// 是否是严格模式
    if (!isStrict) {// 如果this不是对象 通过构造函数包装成对象
        let thisArgType = typeof thisArg;
        if (thisArgType === 'number') thisArg = new Number(thisArg);
        else if (thisArgType === 'string') thisArg = new String(thisArg);
        else if (thisArgType === 'boolean') thisArg = new Boolean(thisArg);
    }
    let invokeParams = [...arguments].slice(1);// 截取剩余参数 第一个参数是this 上面已截取
    // 执行目标函数
    let invokeFun = this;// 这里的this就是目标函数 因为myCall作为方法被调用 this指向调用对象 即目标函数
    // 在严格模式下this指向undefined、没有传第一个参数、第一个参数是null或undefined 此时将目标函数当成普通函数执行并返回结果
    if(thisArg === null || thisArg === undefined) return invokeFun(...invokeParams);
    // 如果传了第一个参数且第一个参数是个对象 让目标函数成为thisArg对象的成员方法 然后调用它
    // 可以直接把目标函数赋值给thisArg对象的某个属性 但是以防thisArg对象上本来就有这个属性
    // 我们这一赋值 就覆盖了thisArg对象上原本的属性 所以在thisArg对象上创建一个唯一的属性保存目标函数
    let uniquePropName = Symbol(thisArg);
    thisArg[uniquePropName] = invokeFun;
    return thisArg[uniquePropName](...invokeParams);// 返回目标函数的执行结果
}

apply

Function.prototype.myApply = function (thisArg, params){// apply是Function.prototype上的一个方法
    let isStrict = (function () {return this === undefined}());// 是否是严格模式
    if (!isStrict) {// 如果this不是对象 通过构造函数包装成对象
        let thisArgType = typeof thisArg;
        if (thisArgType === 'number') thisArg = new Number(thisArg);
        else if (thisArgType === 'string') thisArg = new String(thisArg);
        else if (thisArgType === 'boolean') thisArg = new Boolean(thisArg);
    }
    let invokeParams = Array.isArray(params) ? params :[];// 处理第二个参数
    // 执行目标函数
    let invokeFun = this;// 这里的this就是目标函数 因为myCall作为方法被调用 this指向调用对象 即目标函数
    // 在严格模式下this指向undefined、第一个参数是null或undefined 此时将目标函数当成普通函数执行并返回结果
    if(thisArg === null || thisArg === undefined) return invokeFun(...invokeParams);
    // 如果传了第一个参数且第一个参数是个对象 让目标函数成为thisArg对象的成员方法 然后调用它
    // 可以直接把目标函数赋值给thisArg对象的某个属性 但是以防thisArg对象上本来就有这个属性
    // 我们这一赋值 就覆盖了thisArg对象上原本的属性 所以在thisArg对象上创建一个唯一的属性保存目标函数
    let uniquePropName = Symbol(thisArg);
    thisArg[uniquePropName] = invokeFun;
    return thisArg[uniquePropName](...invokeParams);
}

bind

Function.prototype.myBind = function() {
  var boundTargetFunc = this;
  if (typeof boundTargetFunc !== 'function') {
    throw new Error('绑定的目标必须是函数')
  }
  var boundThis = arguments[0];
  var boundParams = [].slice.call(arguments, 1);
  function fBound () {
    var restParams = [].slice.call(arguments);
    var allParams = boundParams.concat(restParams)
    return boundTargetFunc.apply(this instanceof fBound ? this : boundThis, allParams)
  }
  fBound.prototype = Object.create(boundTargetFunc.prototype || Function.prototype)
  return fBound
}

async

在这里插入图片描述

function myAsync(genF) {
    // 返回值是Promise
    return new Promise((resolve, reject) => {
        const gen = genF();
        function step(nextF) {
            let next;
            try {
                // 执行该函数,获取一个有着value和done两个属性的对象
                next = nextF();
            } catch (e) {
                // 出现异常则将该Promise变为rejected状态
                reject(e);
            }

            // 判断是否到达末尾,Generator函数到达末尾则将该Promise变为fulfilled状态
            if (next.done) {
                return resolve(next.value);
            }

            // 没到达末尾,则利用Promise封装该value,直到执行完毕,反复调用step函数,实现自动执行
            Promise.resolve(next.value).then((v) => {
                step(() => gen.next(v))
            }, (e) => {
                step(() => gen.throw(e))
            })
        }

        step(() => gen.next(undefined));
    })
}

JSONP

function jsonp(url, onsuccess, onerror, charset) {
    var hash = Math.random().toString().slice(2);
    window['jsonp' + hash] = function(data) {
        if(onsuccess && typeof onsuccess == 'function') {
            onsuccess(data);
        }
    }

    var script = createScript(url + "?callback=jsonp" + hash, charset); // 动态产检一个script标签

    //监听加载成功的事件,获取数据,这个位置用了两个事件onload和onreadystatechange是为了兼容IE,因为IE9之前不支持onload事件,只支持onreadystatechange事件
    script.onload = script.onreadystatechange = function() {
        //若不存在readyState事件则证明不是IE浏览器,可以直接执行,若是的话,必须等到状态变为loaded或complete才可以执行
        if(!this.readyState || this.readyState == 'loaded' || this.readyState == 'complete') {
            script.onload = script.onreadystatechange = null;
            // 移除该script的DOM对象
            if(script.parentNode) {
                script.parentNode.removeChild(script);
            }

            //删除函数或变量
            window['jsonp' + hash] = null;
        }
    }

    script.onerror = function () {
        if(onerror && typeof onerror == 'function') {
            onerror();
        }
    }

    document.getElementsByTagName('head')[0].appendChild(script);//往html中增加这个标签,目的是把请求发送出去
}

function createScript(url, charset) {
    var script = document.createElement('script');
    script.setAttribute('type', 'text/javascript');
    charset && script.setAttribute('charset', charset);
    script.setAttribute('src', url);
    script.async = true;
}

setTimeout实现setInterval

function setInterval(fn, interval){
    var timeOut = function(){
        setTimeout(timeOut, interval);
        fn.apply(null);
    }
    timeOut();
}

写代码获取url的GET参数

方法一:采用正则表达式

function getQueryString(name) {
    let reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");// 正则语句
    let r = window.location.search.substr(1).match(reg);// 获取url的参数部分,用正则匹配
    if (r != null) {
        return decodeURIComponent(r[2]); // 解码得到的参数
    };
    return null;
 }

方法二:split拆分法

function GetRequest() {
   const url = location.search; //获取url中"?"符后的字串
   let theRequest = new Object();
   if (url.indexOf("?") != -1) { // 判断是否是正确的参数部分
      let str = url.substr(1); // 截取参数部分
      strs = str.split("&");  // 以‘&’为分割符获取参数数组
      for(let i = 0; i < strs.length; i ++) {
         theRequest[strs[i].split("=")[0]]=unescape(strs[i].split("=")[1]);
      }
   }
   return theRequest;
}

split拆分法(易于理解,代码中规)

function getQueryVariable(variable){
       let query = window.location.search.substring(1); // 获取url的参数部分
       let vars = query.split("&"); // 以‘&’为分割符获取参数数组
       for (let i=0;i<vars.length;i++) { // 遍历数组获取参数
               let pair = vars[i].split("=");
               if(pair[0] == variable){return pair[1];}
       }
       return(false);
}
function getUrlParam(sUrl, sKey) {
    let sParam = sUrl.split('?')[1].split('#')[0]; // 获取到含有地址的字符串
    let pArr = sParam.split('&'); // 分解字符串
    if (sKey) { // 筛选查找
        var newArr = [];
        pArr.forEach(function (elm, ind) {
            if (elm.split('=')[0] == sKey) { // 满足条件添加进新数组
                newArr.push(elm.split('=')[1]);
            }
        });
        if (newArr.length == 1) {
            return  newArr[0]; // 单个数据返回字符串
        }else if (newArr.length == 0) {
            return ""; // 不满足条件返回空字符串
        }else {
            return newArr; // 返回新数组
        }
    } else { // 无筛选条件
        if (!sParam) { // 字符串为空返回空对象
            return {}; 
        }else {
            var newObj = {};
            pArr.forEach(function (elm, ind) {
                if (!(elm.split('=')[0] in newObj)) {
                    newObj[elm.split('=')[0]] = []
                }
                newObj[elm.split('=')[0]].push(elm.split('=')[1]) // 满足条件添加到对应的键值对中
            });
            return newObj // 返回新对象
        }

    };
}

将一个字符串转换为驼峰形式

//方式一:操作字符串数组
function transformStr2Hump1(str) {
    if(str == null) {
        return "";
    }
    var strArr = str.split('-');
    for(var i = 1; i < strArr.length; i++) {
        strArr[i] = strArr[i].charAt(0).toUpperCase() + strArr[i].substring(1);
    }
    return strArr.join('');
}

//方式二:操作字符数组
function transformStr2Hump2(str) {
    if(str == null) {
        return "";
    }
    var strArr  =str.split('');
    for(var i = 0; i < strArr.length; i++) {
        if(strArr[i] == "-"){
            //删除-
            strArr.splice(i, 1);
            //将该处改为大写
            if(i < strArr.length) {
                strArr[i] = strArr[i].toUpperCase();
            }
        }
    }
    return strArr.join("");
}

//方式三:利用正则
function transformStr2Hump3(str) {
    if(str == null) {
        return "";
    }
    var reg = /-(\w)/g;//匹配字母或数字或下划线或汉字
    return str.replace(reg, function($0, $1) {
        return $1.toUpperCase();
    })
}

继承

//组合式继承
function ClassW(sColor){
    this.color = sColor;
}
ClassW.prototype.sayColor = function(){
    alert(this.color);
}
 
function ClassJ(sColor, sName){
    ClassW.call(this, sColor);
    this.name = sName;
}
ClassJ.prototype = new ClassW();
//校正构造函数
ClassJ.prototype.constructor = ClassJ;
ClassJ.prototype.sayName = function () {
    alert(this.name);
};
//寄生组合式
//ES6
class Parent{
    constructor(name){
        this.name = name;
    }
    getName(){
        return this.name;
    }
}
class Child extends Parent{
    constructor(name, age){
        super(name);
        this.age = age;
    }
    getAge(){
        return this.age;
    }
}

原型链继承

实现方式:将子类的原型链指向父类的对象实例

function Parent(){
  this.name = "parent";
  this.list = ['a'];
}
Parent.prototype.sayHi = function(){
  console.log('hi');
}
function Child(){

}
Child.prototype = new Parent();
var child = new Child();
console.log(child.name);
child.sayHi();

原理:子类实例child的__proto__指向Child的原型链prototype,而Child.prototype指向Parent类的对象实例,该父类对象实例的__proto__指向Parent.prototype,所以Child可继承Parent的构造函数属性、方法和原型链属性、方法
优点:可继承构造函数的属性,父类构造函数的属性,父类原型的属性
缺点:无法向父类构造函数传参;且所有实例共享父类实例的属性,若父类共有属性为引用类型,一个子类实例更改父类构造函数共有属性时会导致继承的共有属性发生变化;实例如下:

var a = new Child();
var b = new Child();
a.list.push('b');
console.log(b.list); // ['a','b']

构造函数继承

实现方式:在子类构造函数中使用call或者apply劫持父类构造函数方法,并传入参数
原理:使用call或者apply更改子类函数的作用域,使this执行父类构造函数,子类因此可以继承父类共有属性
优点:可解决原型链继承的缺点
缺点:不可继承父类的原型链方法,构造函数不可复用

function Parent(name, id){
  this.id = id;
  this.name = name;
  this.printName = function(){
    console.log(this.name);
  }
}
Parent.prototype.sayName = function(){
  console.log(this.name);
};
function Child(name, id){
  Parent.call(this, name, id);
  // Parent.apply(this, arguments);
}
var child = new Child("jin", "1");
child.printName(); // jin
child.sayName() // Error

组合继承

原理:综合使用构造函数继承和原型链继承

function Parent(name, id){
  this.id = id;
  this.name = name;
  this.list = ['a'];
  this.printName = function(){
    console.log(this.name);
  }
}
Parent.prototype.sayName = function(){
  console.log(this.name);
};
function Child(name, id){
  Parent.call(this, name, id);
  // Parent.apply(this, arguments);
}
Child.prototype = new Parent();
var child = new Child("jin", "1");
child.printName(); // jin
child.sayName() // jin

var a = new Child();
var b = new Child();
a.list.push('b');
console.log(b.list); // ['a']

优点:可继承父类原型上的属性,且可传参;每个新实例引入的构造函数是私有的
缺点:会执行两次父类的构造函数,消耗较大内存,子类的构造函数会代替原型上的那个父类构造函数

原型式继承

原理:类似Object.create,用一个函数包装一个对象,然后返回这个函数的调用,这个函数就变成了个可以随意增添属性的实例或对象,结果是将子对象的__proto__指向父对象
缺点:共享引用类型

var parent = {
  names: ['a']
}
function copy(object) {
  function F() {}
  F.prototype = object;    
  return new F();
}
var child = copy(parent);

寄生式继承

原理:二次封装原型式继承,并拓展

function createObject(obj) {
  var o = copy(obj);
  o.getNames = function() {
    console.log(this.names);
    return this.names;
  }
  return o;
}

优点:可添加新的属性和方法

寄生组合式继承

原理:改进组合继承,利用寄生式继承的思想继承原型

function inheritPrototype(subClass, superClass) {
  // 复制一份父类的原型
  var p = copy(superClass.prototype);
  // 修正构造函数
  p.constructor = subClass;
  // 设置子类原型
  subClass.prototype = p;
}

function Parent(name, id){
  this.id = id;
  this.name = name;
  this.list = ['a'];
  this.printName = function(){
    console.log(this.name);
  }
}
Parent.prototype.sayName = function(){
  console.log(this.name);
};
function Child(name, id){
  Parent.call(this, name, id);
  // Parent.apply(this, arguments);
}
inheritPrototype(Child, Parent);

边框生成四边形

正方形

#main {
  width: 0px;
  height: 0px;
  border-bottom: 200px solid red;
  border-left: 200px solid black;
  border-right: 200px solid blue;
  border-top: 200px solid pink;
}

在这里插入图片描述

矩形

#main {
  width: 0px;
  height: 0px;
  border-bottom: 200px solid red;
  border-left: 100px solid red;
  border-right: 100px solid red;
  border-top: 200px solid red;
}

在这里插入图片描述

平行四边形

*{
   margin: 0;
}
#wrapper {
	  position: relative;
}
.public {
   width: 0px;
   height: 0px;
  border-bottom: 200px solid red;
  border-left: 200px solid transparent;
  border-right: 200px solid transparent;
  border-top: 200px solid transparent;
  position: absolute;
}
.move {
  transform: rotate(180deg);
  top: 200px;
  left: 200px;
}

在这里插入图片描述

边框生成三角形

锐角三角形

#main {
  width: 0px;
  height: 0px;
  border-bottom: 200px solid red;
  border-left: 200px solid black;
  border-right: 200px solid blue;
  border-top: 200px solid pink;
}

在这里插入图片描述
从图可以看出left,right,top,bottom都是占着一个三角形的情况,那么当我们需要某个三角形时我们只需要让其他三个三角形隐藏起来不就可以了,我们可以用transparent属性值来隐藏border

等腰三角形

#main {
  width: 0px;
  height: 0px;
  border-bottom: 200px solid red;
  border-left: 200px solid transparent;
  border-right: 200px solid transparent;
  border-top: 200px solid transparent;
}

在这里插入图片描述

直角三角形

#main {
  width: 0px;
  height: 0px;
  border-bottom: 200px solid red;
  border-left: 200px solid red;
  border-right: 200px solid transparent;
  border-top: 200px solid transparent;
}

在这里插入图片描述

格式化时间戳

function formatDate(date, fmt) {
  if (/(y+)/.test(fmt)) {
      /*
     RegExp.$1是RegExp的一个属性,指的是与正则表达式匹配的第一个子匹配(以括号为标志)字符串 即yyyy
     RegExp.$1.length子匹配的长度 也就是获取你传入了几个y
     date.getFullYear()获取年2021
     假如传入了4个y 2021截取0个字符就是2021 替换掉yyyy
     */
    fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));
  }
  let o = {
    'M+': date.getMonth() + 1,
    'd+': date.getDate(),
    'h+': date.getHours(),
    'm+': date.getMinutes(),
    's+': date.getSeconds(),
    'q+': Math.floor((this.getMonth()+3)/3), //季度
    'S': this.getMilliseconds()             //毫秒
  };
  for (let k in o) {
    if (new RegExp(`(${k})`).test(fmt)) {
      let str = o[k] + '';
      fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? str : padLeftZero(str));
    }
  }
  return fmt;
};
// 不足两位用0补齐 若传入4 '00' + str就是004 截取两位就是04 若传入23 '00' + str就是0023 截取两位就是23
function padLeftZero (str) {
  return ('00' + str).substr(str.length);
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值