手写call、apply、bind函数

call实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// 实现
Function.prototype.myCall = function(context) {
    if(typeof context === 'undefined' || context === null) {
        context = window
    }
    context.fn = this
    let args = [...arguments].slice(1)
    let result = context.fn(...args)
    delete context.fn
    return result
}

// 测试
function test() {
    console.log(this.name)
}
const obj = {name: 123}
test.myCall(obj)

apply实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 实现
Function.prototype.myApply = function(context) {
    if(typeof context === 'undefined' || context === null) {
        context = window
    }
    context.fn = this
    let args = arguments[1]
    let result 
    if(args) {
        result = context.fn(...args)
    } else {
        result = context.fn()
    }
    delete context.fn
    return result
}

// 测试
function test() {
    console.log(this.name)
}
const obj = {name: 456}
test.myApply(obj)

bind实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 实现
Function.prototype.myBind = function (context) {
    if (typeof this !== 'function') {
        throw new TypeError('Error')
    }
    let _this = this
    let args = [...arguments].slice(1)
    return function F() {
        if (this instanceof F) {
            return _this.apply(this, args.concat([...arguments]))
        }
        return _this.apply(context, args.concat([...arguments]))
    }
}

// 测试
function test() {
    console.log(this.name)
}
const obj = {
    name: 789
}
const p = test.myBind(obj)
p()

new

  1. 用new Object() 的方式新建了一个对象 obj
  2. 取出第一个参数,就是我们要传入的构造函数。此外因为 shift 会修改原数组,所以 arguments 会被去除第一个参数
  3. 将 obj 的原型指向构造函数,这样 obj 就可以访问到构造函数原型中的属性
  4. 使用 apply,改变构造函数 this 的指向到新建的对象,这样 obj 就可以访问到构造函数中的属性
  5. 判断返回的值是不是一个对象,如果是一个对象,我们就返回这个对象,如果没有,我们该返回什么就返回什么
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 实现
function objectFactory() {
    var obj = new Object(),
    Constructor = [].shift.call(arguments)
    obj.__proto__ = Constructor.prototype
    var ret = Constructor.apply(obj, arguments)
    return typeof ret === 'object' ? ret : obj
}

// 测试
function Otaku (name, age) {
    this.name = name;
    this.age = age;
    this.habit = 'Games';
}
Otaku.prototype.strength = 60;
Otaku.prototype.sayYourName = function () {
    console.log('I am ' + this.name);
}
var person1 = new Otaku('Kevin', '18')
var person2 = objectFactory(Otaku, 'Kevin', '18')
console.log(person1)
console.log(person2)

instanceof

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 实现
function my_instanceof(obj, constructor) {
    let constuctProto = constructor.prototype
    while(true) {
        if(obj.__proto__ === null) {
            return false
        }
        if(obj.__proto__ === constuctProto) {
            return true
        }
        obj = obj.__proto__
    }
}

// 测试
function Car(make, model, year) {
  this.make = make;
  this.model = model;
  this.year = year;
}
var mycar = new Car("Honda", "Accord", 1998);
console.log(my_instanceof(mycar, Car))

jsonp

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 实现
function jsonp(url, success) {
      // 生成一个随机函数名
      let fnName = "__jsonpFn" + Math.random().toString().replace(".", "");
      // 创建script标签
      let script = document.createElement('script')
      // 设置script标签的src属性(需要判断url中是否带有参数)
      script.src = url + (RegExp(/\?/).test(url) == true ? '&' : '?') + 'callback=' + fnName
      // 默认异步执行
      script.async = true
      // 设置script标签的type属性
      script.type = 'text/javascript'
      // 绑定函数
      window[fnName] = function (data) {
        success && success(data)
        // 调用回调函数后删除script标签
        delete window[fnName];
        document.body.removeChild(script);
      }
      // 将script标签加到页面中
      document.body.appendChild(script)
}

// 测试
jsonp(
    // 'https://api.asilu.com/weather/',
    'https://api.asilu.com/weather/?city="武汉"',
    function (data) {
        console.log(data)
    })

ajax

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
 let xhr = new XMLHttpRequest()
 xhr.open('get', url, true)
 xhr.onreadystatechange = function(){
    if(xhr.readyState === 4){
        console.log('请求完成')
        if(this.status >= 200 &&this.status<300){ 
            console.log('成功')
        }else{
            console.log('失败')
        }
    }
 }
 xhr.onerror = function(e) {
    console.log('连接失败')
 }
 xhr.send()

…扩展运算符

rest参数的形式为 ...变量名
用于获取函数调用时传入的参数. 顾名思义, rest参数表示的是除了明确指定的参数外,剩下的参数的集合, 它的类型是Array

1
2
3
4
5
function restFunc(...args)
{
   return args.length;
}
restFunc('This','is','rest','test'); // 输出4 参数的个数为4
1
2
3
4
5
6
7
8
function restFunc(firstArgs,...restArgs)
{
 // true   
 console.log(Array.isArray(restArgs)); 
 // 5 [6,7,8,9]   
 console.log(firstArgs,restArgs);     
}
restFunc(5,6,7,8,9);

spread运算符 … 扩展运算符 …可以用于 数组的构造,也可以用于调用函数时,将一个数组用作函数参数(就是把这个数组转化为参数的列表,所以也就成了一个函数的参数)

1
2
3
4
// 数组合并
var  arr1 = [1,3,5];
var  arr2 = [2,4,6];
[...arr1,...arr2]  // 输出 [1,3,5,2,4,6]
1
2
3
4
5
6
7
// 函数参数
function testFunc(x,y,z)
{
    return x + y + z;
}
var args = [1,2,3];
testFunc(...args);  //输出 6
1
2
3
4
5
6
7
// 浅拷贝
let a = {
  age: 1
}
let b = { ...a }
a.age = 2
console.log(b.age) // 1
1
2
3
4
// 字符串转成数组
var arr = [...'hello'] 
console.log(arr)
//["h", "e", "l", "l", "o"]

深度克隆函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
var deepClone = function(target, obj){
    for (var key in obj){
        if (obj.hasOwnProperty(key)){
            var val = obj[key];
            if (typeof val === "object") {
                target[key] = {};
                deepClone(target[key], val)
            }else {
                target[key] = val;
            }
        }
    }
    return target;
}

promise

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
const PROMISE_STATUS = {
    PENDING: 1,
    RESOLVED: 2,
    REJECTED: 3
};

let util = {
    isFunc(fn) {
        return typeof fn === "function";
    },
    isPromise(obj) {
        return obj && this.isFunc(obj.then);
    }
};

function MyPromise(exector) {
    this.status = PROMISE_STATUS.PENDING;
    this.val = undefined;
    this.reason = undefined;

    this.resolveCallback = undefined;
    this.rejectCallback = undefined;

    let resolve = val => {
        if (this.status === PROMISE_STATUS.PENDING) {
            this.status = PROMISE_STATUS.RESOLVED;
            this.val = val;

            if (util.isFunc(this.resolveCallback)) {
                this.resolveCallback();
            }
        }
    };

    let reject = reason => {
        if (this.status === PROMISE_STATUS.PENDING) {
            this.status = PROMISE_STATUS.REJECTED;
            this.reason = reason;
            if (util.isFunc(this.rejectCallback)) {
                this.rejectCallback();
            }
        }
    };

    // 在构造函数中立即执行exector,并由用户根据业务手动调用resolve或者reject
    exector(resolve, reject);
}

MyPromise.prototype.then = function(onFull, onReject) {
    return new MyPromise((resolve, reject) => {
        let success = () => {
            // 具体的返回值有Promise A+规范决定, 根据onFull 的结果处理后面一个then的调用,
            // 1. 如果onFull结果为报错,进入catch,调用reject
            // 2. 如果onFull结果为Promise,则根据promise的状态调用resolve或者reject
            // 3. 如果onFull结果为普通值,则直接调用resolve
            try {
                let result = onFull(this.val);
                if (util.isPromise(result)) {
                    result.then(
                        res => {
                            resolve(res);
                        },
                        e => {
                            reject(e);
                        }
                    );
                } else {
                    resolve(result);
                }
            } catch (e) {
                reject(e);
            }
        };

        let error = () => {
            let reason = util.isFunc(onReject) ? onReject(this.reason) : this.reason;
            reject(reason);
        };

        if (this.status === PROMISE_STATUS.RESOLVED) {
            success();
        } else if (this.status === PROMISE_STATUS.REJECTED) {
            error();
        } else if (this.status === PROMISE_STATUS.PENDING) {
            this.resolveCallback = success;
            this.rejectCallback = error;
        }
    });
};

let myPromise = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        resolve("setTimeout result");
    }, 100);
});

let myP2 = new MyPromise((resolve, reject) => {
    setTimeout(() => {

        resolve("async2 result");
    }, 100);
});

myPromise
    .then(res => {
        console.log("res1: ", res);
        throw 'onfull error'
        return myP2;
    })
    .then(
        res => {
            console.log("res2: ", res);
            return res;
        },
        err => {
            console.log(err);
        }
    )
    .then(res => {
        console.log(res); // res2:
    })
    .then(res => {
        console.log(res); // undefined
    });

敏感词标记

1
2
3
var content = "XX阿斯顿撒旦撒旦和轻微oo饥饿和武as器就可怜见看完你去了哇";
var res = content.replace(/(XX|oo|as)/g, "<span>$1</span");
console.log(res);

实现字符串反转

1
2
3
var reverse = function(str){
    return str.split("").reverse().join("");
}

实现数组随机排序

1
2
3
arr.srot(()=>{
      return Math.random() > 0.5 ? 1 : -1;
})

取出数组中的最大值

1
2
// 不使用遍历
var res = Math.max.apply(null, arr);

实现lazyman

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
LazyMan(Hank)输出:
Hi! This is Hank!

LazyMan(Hank).sleep(10).eat(dinner)输出
Hi! This is Hank!
//等待10秒..
Wake up after 10
Eat dinner~

LazyMan(Hank).eat(dinner).eat(supper)输出
Hi This is Hank!
Eat dinner~
Eat supper~

LazyMan(Hank).sleepFirst(5).eat(supper)输出
//等待5秒
Wake up after 5
Hi This is Hank!
Eat supper

实现flattenDeep

将输入[1,[2,[3,4]],5]转换成一维数组[1,2,3,4,5]

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
function flattenDeep(array){
    let res = []
    array.forEach(item=>{
        if(Array.isArray(item)){
            res = res.concat(flattenDeep(item));
        }else {
            res.push(item)
        }
    })
    return res
}

格式化数字,三个一组,逗号分隔

用零宽正则实现

1
2
3
function toThousands(num) {
    return (num || 0).toString().replace(/(\d)(?=(?:\d{3})+$)/g, '$1,');
}

其实还有更简单的方法

1
Number(1234556).toLocaleString()

手写一个函数实现斐波那契数列

1
2
3
4
5
6
7
8
const feibo= max=>{
    let [a,b,i]= [0,1,1]
    while(i++<=max) {
        [a,b] = [b,a + b ]
       console.log(b)
    }
  return a  
}

防抖与节流

防抖定义:多次触发事件后,事件处理函数只执行一次,并且是在触发操作结束时执行。

防抖原理:对处理函数进行延时操作,若设定的延时到来之前,再次触发事件,则清除上一次的延时操作定时器,重新定时。

节流定义:触发函数事件后,短时间间隔内无法连续调用,只有上一次函数执行后,过了规定的时间间隔,才能进行下一次的函数调用。

节流原理:对处理函数进行延时操作,若设定的延时到来之前,再次触发事件,则清除上一次的延时操作定时器,重新定时。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
 * 防抖函数
 * @param fn 事件触发的操作
 * @param duration 多少毫秒内连续触发事件,不会执行
 */
function debounce(fn,duration){
            var  timer
            window.clearTimeout(timer)
            timer = setTimeout(()=>{
                fn.call(this)
            },duration)
        }
/**
 * 节流函数
 * @param fn 事件触发的操作
 * @param duration 间隔多少毫秒需要触发一次事件
 */
  function throttle(fn,duration){
            let canIRun
            if(!canIRun)return
            fn.call(this)
            canIRun = false
            setTimeout(()=>{
                canIRun = true
            },duration)
        }

js柯里化

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
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

冒泡排序

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
function bubbleSort(arr) {
      var len = arr.length
      for (var i = 0; i < len - 1; i++) {
        for (var j = 0; j < len - 1 - i; j++) {
          if (arr[j] > arr[j + 1]) {
            var temp = arr[j + 1]
            arr[j + 1] = arr[j]
            arr[j] = temp
          }
        }
      }
      return arr
    }
    // 测试
    var arr = [31, 12, 3, 23, 45, 32, 1, 9, 17, 25]
    console.log('冒泡排序', bubbleSort(arr))

快速排序

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
function quickSort(arr, left, right) {
      var len = arr.length,
        partitionIndex,
        left = typeof left != 'number' ? 0 : left,
        right = typeof right != 'number' ? len - 1 : right;

      if (left < right) {
        partitionIndex = partition(arr, left, right);
        quickSort(arr, left, partitionIndex - 1);
        quickSort(arr, partitionIndex + 1, right);
      }
      return arr;
    }

    // 分区操作
    function partition(arr, left, right) { 
      var pivot = left, // 设定基准值(pivot)
        index = pivot + 1;
      for (var i = index; i <= right; i++) {
        if (arr[i] < arr[pivot]) {
          swap(arr, i, index);
          index++;
        }
      }
      swap(arr, pivot, index - 1);
      return index - 1;
    }

    function swap(arr, i, j) {
      var temp = arr[i];
      arr[i] = arr[j];
      arr[j] = temp;
    }

    // 测试
    var arr = [31, 12, 3, 23, 45, 32, 1, 9, 17, 25]
    console.log('快速排序', quickSort(arr))