new操作符

new操作符做了这些事:

  • 它创建了一个全新的对象。
  • 它会被执行[[Prototype]](也就是__proto__)链接。
  • 它使this指向新创建的对象。。
  • 通过new创建的每个对象将最终被[[Prototype]]链接到这个函数的prototype对象上。
  • 如果函数没有返回对象类型Object(包含Functoin, Array, Date, RegExg, Error),那么new表达式中的函数调用将返回该对象引用。
function new(fn){
    let res = {};
    // 所有的引用类型(数组、对象、函数),`__ proto __ 属性值指向它的构造函数的" __ prototype __ "属性值`
    if(fn.prototype !== null){
        res.__proto__ = fn.prototype;
    }
    let ret = fn.apply(res,Array.prototype.call(arguments,1));
    if((typeof ret === "object" || typeof ret === "function") && ret !== null)
    {
        return ret;
    }
    return res;
}

let obj = new(A,1,2);
//等价于
let obj = new A(1,2);

实现一个 instanceOf

function instanceOf(left,right) {

    let proto = left.__proto__;
    let prototype = right.prototype
    while(true) {
        if(proto === null) return false
        if(proto === prototype) return true
        proto = proto.__proto__;
    }
}

数组去重

//去掉数组中重复性的数据
function trimSameItem(arr){
    var newArr = [];
    for (var i = 0; i < arr.length; i++) {
        //方法一
        // if(newArr.indexOf(arr[i]) === -1){
        //     newArr.push(arr[i]);
        // }
        //方法二
        if(!newArr.includes(arr[i])){
            newArr.push(arr[i]);
        }
    }
    return newArr;
}
var arr = [8,11,20,5,20,8,0,2,4,0,8,19];
console.log(trimSameItem(arr));

// 方法 三
function unique(arr) {
    var res = arr.filter(function(item, index, array) {
        return array.indexOf(item) === index
    })
    return res
}


数组扁平化

数组扁平化就是将 [1, [2, [3]]] 这种多层的数组拍平成一层 [1, 2, 3]。使用 Array.prototype.flat 可以直接将多层数组拍平成一层:

[1, [2, [3]]].flat(2)  // [1, 2, 3]

现在就是要实现 flat 这种效果。

// ES5 实现:递归
function flatten(arr) {
    var result = [];
    for (var i = 0, len = arr.length; i < len; i++) {
        if (Array.isArray(arr[i])) {
            result = result.concat(flatten(arr[i]))
        } else {
            result.push(arr[i])
        }
    }
    return result;
}
// ES6 实现
function flatten(arr) {
    while (arr.some(item => Array.isArray(item))) {
        arr = [].concat(...arr);
    }
    return arr;
}

反转数组

//反转数组
function turnArray(arr) {
    for (var i = 0; i < arr.length/2; i++) {
        var temp = arr[i];
        arr[i] = arr[arr.length-1-i];
        arr[arr.length-1-i] = temp;
    }
    return arr;
}
var arr = [1,23,7,3,4,5,6,7,8,9];
console.log(turnArray(arr));

 

转换字符串为驼峰命名

//转换字符串为驼峰命名
function toTuoFeng(params) {
    if(!params){
        console.log('参数不存在');
        return;
    }
    var arr = foo.split('-');
    // console.log(arr[1].charAt(0).toUpperCase()+arr[1].substr(1,arr[1].length-1));
    for (var i = 1; i < arr.length; i++) {
        arr[i] = arr[i].charAt(0).toUpperCase()+arr[i].substr(1,arr[i].length-1);
    }

    return arr.join('');
}
var foo = 'get-element-by-id';
var zhuan = toTuoFeng(foo);
console.log(zhuan);
 

冒泡排序


//冒泡排序
var arr = [5,4,9,23,7,2,1];

//论数 arr.length-1
for (var i = 0; i < arr.length-1; i++) {
   console.log(i);
   for (var j = 0; j < arr.length-1-i; j++) {
       //j>j+1二者对调位置
       if(arr[j]>arr[j+1]){
            var temp = arr[j+1];
            arr[j+1] = arr[j];
            arr[j] = temp;
       }
    //倒序
    //    if(arr[j]<arr[j+1]){
    //         temp = arr[j+1];
    //         arr[j+1] = arr[j];
    //         arr[j] = temp;
    //    }

   }
}
console.log(arr);
 

实现 Call

call语法:

fun.call(thisArg, arg1, arg2, ...),调用一个函数, 其具有一个指定的this值和分别地提供的参数(参数的列表)。

call核心:

  • 将函数设为对象的属性
  • 执行&删除这个函数
  • 指定this到函数并传入给定参数执行函数
  • 如果不传入参数,默认指向为 window
  • this 可能传入 null;
  • 传入不固定个数的参数;
  • 函数可能有返回值;
Function.prototype.call2 = function(content = window) {
    content.fn = this;
    let args = [...arguments].slice(1);
    let result = content.fn(...args);
    delete content.fn;
    return result;
}
let foo = {
    value: 1
}
function bar(name, age) {
    console.log(name)
    console.log(age)
    console.log(this.value);
}
bar.call2(foo, 'black', '18') // black 18 1

 

apply的模拟实现

apply()的实现和call()类似,只是参数形式不同。

Function.prototype.apply2 = function(context = window) {
    context.fn = this
    let result;
    // 判断是否有第二个参数
    if(arguments[1]) {
        result = context.fn(...arguments[1])
    } else {
        result = context.fn()
    }
    delete context.fn
    return result
}

 

bind() 的模拟实现

Function.prototype.bind2 = function(content) {
    if(typeof this != "function") {
        throw Error("not a function")
    }
    // 若没问参数类型则从这开始写
    let fn = this;
    let args = [...arguments].slice(1);
    
    let resFn = function() {
        return fn.apply(this instanceof resFn ? this : content,args.concat(...arguments) )
    }
    function tmp() {}
    tmp.prototype = this.prototype;
    resFn.prototype = new tmp();
    
    return resFn;
}
 

实现一个JS函数柯里化

在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。

function curry(fn, args) {
    var length = fn.length;
    var args = args || [];
    return function(){
        newArgs = args.concat(Array.prototype.slice.call(arguments));
        if (newArgs.length < length) {
            return curry.call(this,fn,newArgs);
        }else{
            return fn.apply(this,newArgs);
        }
    }
}

function multiFn(a, b, c) {
    return a * b * c;
}

var multi = curry(multiFn);

multi(2)(3)(4);
multi(2,3,4);
multi(2)(3,4);
multi(2,3)(4);


// ES6 版本
const curry = (fn, arr = []) => (...args) => (
  arg => arg.length === fn.length
    ? fn(...arg)
    : curry(fn, arg)
)([...arr, ...args])

let curryTest=curry((a,b,c,d)=>a+b+c+d)
curryTest(1,2,3)(4) //返回10
curryTest(1,2)(4)(3) //返回10
curryTest(1,2)(3,4) //返回10

 

手写一个Promise

我们来过一遍Promise/A+规范:

  • 三种状态pending| fulfilled(resolved) | rejected
  • 当处于pending状态的时候,可以转移到fulfilled(resolved)或者rejected状态
  • 当处于fulfilled(resolved)状态或者rejected状态的时候,就不可变。
  1. 必须有一个then异步执行方法,then接受两个参数且必须返回一个promise:
var promise = new Promise((resolve,reject) => {
    if (操作成功) {
        resolve(value)
    } else {
        reject(error)
    }
})
promise.then(function (value) {
    // success
},function (value) {
    // failure
})
 

节流(Throttling)实现

触发高频事件,且 N 秒内只执行一次。

function throttle(fn, wait) {
	let prev = new Date();
	return function() { 
	    const args = arguments;
		const now = new Date();
		if (now - prev > wait) {
			fn.apply(this, args);
			prev = new Date();
		}
	}


 

防抖函数 有问题

触发高频事件 N 秒后只会执行一次,如果 N 秒内事件再次触发,则会重新计时。


// 防抖动函数

function debounce(func, wait=50, immediate) {
    var timeout;
    return function() {
        var context = this, args = arguments;
        var later = function() {
                timeout = null;
                if (!immediate) func.apply(context, args);
        };
        var callNow = immediate && !timeout;
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
        if (callNow) func.apply(context, args);
    };
};

 

深拷贝 有问题

function deepClone(obj) {
    if (typeof obj !== 'object') return;
    var newObj = obj instanceof Array ? [] : {};
    for (var key in obj) {
        if (obj.hasOwnProperty(key)) {
            newObj[key] = typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key];
        }
    }
    return newObj;
}


function deepCopy(obj){//有问题
    //判断是否是简单数据类型,
    if(typeof obj == "object"){
        //复杂数据类型
        var result = obj.constructor == Array ? [] : {};
        for(let i in obj){
            result[i] = typeof obj[i] == "object" ? deepCopy(obj[i]) : obj[i];
        }
    }else {
        //简单数据类型 直接 == 赋值
        var result = obj;
    }
    return result;
}
 

解析 URL 参数为对象

function parseParam(url) {
    const paramsStr = /.+\?(.+)$/.exec(url)[1]; // 将 ? 后面的字符串取出来
    const paramsArr = paramsStr.split('&'); // 将字符串以 & 分割后存到数组中
    let paramsObj = {};
    // 将 params 存到对象中
    paramsArr.forEach(param => {
        if (/=/.test(param)) { // 处理有 value 的参数
            let [key, val] = param.split('='); // 分割 key 和 value
            val = decodeURIComponent(val); // 解码
            val = /^\d+$/.test(val) ? parseFloat(val) : val; // 判断是否转为数字
    
            if (paramsObj.hasOwnProperty(key)) { // 如果对象有 key,则添加一个值
                paramsObj[key] = [].concat(paramsObj[key], val);
            } else { // 如果对象没有这个 key,创建 key 并设置值
                paramsObj[key] = val;
            }
        } else { // 处理没有 value 的参数
            paramsObj[param] = true;
        }
    })
    
    return paramsObj;
}
 

字符串模版


function render(template, data) {
    const reg = /\{\{(\w+)\}\}/; // 模板字符串正则
    if (reg.test(template)) { // 判断模板里是否有模板字符串
        const name = reg.exec(template)[1]; // 查找当前模板里第一个模板字符串的字段
        template = template.replace(reg, data[name]); // 将第一个模板字符串渲染
        return render(template, data); // 递归的渲染并返回渲染后的结构
    }
    return template; // 如果模板没有模板字符串直接返回
}
 

Ajax

const getJSON = function(url) {
    return new Promise((resolve, reject) => {
        const xhr = XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Mscrosoft.XMLHttp');
        xhr.open('GET', url, false);
        xhr.setRequestHeader('Accept', 'application/json');
        xhr.onreadystatechange = function() {
            if (xhr.readyState !== 4) return;
            if (xhr.status === 200 || xhr.status === 304) {
                resolve(xhr.responseText);
            } else {
                reject(new Error(xhr.responseText));
            }
        }
        xhr.send();
    })
}