【JavaScript】源码实现(待更新)


1. Array.isArray()

Array.myIsArray = function(arr){
    return Object.prototype.toString.call(arr) === '[object Array]';
}

let arr = new Array(1,3,45,6,7);
var mycars=new Array("Saab","Volvo","BMW")

console.log(Array.myIsArray(arr)); //true
console.log(Array.myIsArray(mycars)); //true

console.log(Array.isArray(arr));//true
console.log(Array.isArray(mycars));//true

2. New

new方法效果:

function People(name,age) {
    this.name = name
    this.age = age
}
let peo = new People('Bob',22)
console.log(peo.name)  // Bob
console.log(peo.age)  // 22

实现思路:

  1. 直接通过原型创建新对象 ;
  2. 将this(也就是上一句中的新对象)和调用参数传给构造器,执行;
  3. 如果构造器没有手动返回对象,则返回第一步创建的新对象,如果有,则舍弃掉第一步创建的新对象,返回手动return的对象
function createNew(Parent, ...arr) {
    // 1.以构造器的prototype属性为原型,创建新对象;
    let obj = Object.create(Parent.prototype);
    // 2.将this和调用参数传给构造器执行
    let result = Parent.apply(obj, arr) //
    // 3.如果构造器没有手动返回对象,则返回第一步的对象
    return typeof result === 'object' ? result : obj 
}

实现效果:

function People(name,age) {
    this.name = name
    this.age = age
}
let peo = createNew(People,'Bob',22)
console.log(peo.name)  // Bob
console.log(peo.age)  // 22

const

consst constangize = (obj) => {
	Object.freeze(obj);
	Object.keys(obj).forEach((key, i) => {
		if (typeof(obj[key]) === 'object') {
			constantize(obj[key]);
		}
	});
};

3. call方法

call方法效果

// call实现
function add(c, d) {
    return this.a + this.b + c + d;
}
const obj = {
    a: 1,
    b: 2
};
let num = add.call(obj, 3, 4)
console.log(num); // 10

方法实现:

/* 思路:将要改变this指向的方法挂到目标this上执行并返回*/
Function.prototype.myCall = function (context) {
    if (typeof this !== 'function') {
        throw new TypeError('not funciton');
    }
    let content = context || window;
    content.fn = this; // 1. 将函数设为对象的属性
    let args = [...arguments].slice(1); // 2. 执行属性函数
    var result = content.fn(...args);
    delete content.fn; // 3. 删除属性
    return result;
}
let numCall = add.myCall(obj, 3, 4);
console.log(numCall); // 10

4. apply

apply方法效果

function f(x) {
    console.log(x, this.y)
}
//your code here
f.bind({y: 'foo'})(); //undefined "foo"
f.bind()(); //undefined undefined
f.bind({y: 'bar'}, 'foo')(); //"foo" "bar"

方法实现:

// apply实现
// 思路:将要改变this指向的方法挂到目标this上执行并返回
Function.prototype.myApply = function (context) {
    if (typeof this !== 'function') {
        throw new TypeError('not funciton');
    }
    context = context || window;
    context.fn = this; // 1. 将函数设为对象的属性
    let result
    // 2. 执行属性
    if (arguments[1]) {
        result = context.fn(...arguments[1]);
    } else {
        result = context.fn();
    }
    delete context.fn; // 3. 删除属性
    return result;
}
let numApply = add.myApply(obj, [3, 4]);
console.log(numApply); // 10

5. bind

bind方法效果

function Person(){
    this.name = "zx";
    this.age = "19";
    this.gender = "male";
}
let obj={
    hobby: "看书"
}
// 构造函数的this绑定为obj
let changeObj = Person.bind(obj,1,3)
// 直接调用构造函数操作,会给obj增加三个数属性
changeObj(); 
console.log(obj); // {hobby: "看书"; name = "zx"; age = "19";  gender = "male";}
// 对changeObj进行new操作
let newChangeObj = new changeObj() 
// new后的构造后的newChangeObj的原型链指向Person {},之前的bind操作无效了
console.log(newChangeObj); // Person { name = "zx"; age = "19";  gender = "male";}
console.log(newChangeObj.__proto__); // Person {}

从上述代码,可以得出结论,bind获得改变this指向后的函数,在new之后,bind失效。

实现

Function.prototype.mybind = function (context) {
    if (typeof this !== 'function') {
        throw new TypeError('Error');
    }
    // 保存函数
    let _this = this;
    // 目标对象在外的参数,赋值给数组args
    let args = [...arguments].slice(1); 
	// 返回一个待执行的函数
    return function fBound() {
        /* 处理函数使用new的情况
           instanceof用来检测某个实例对象的原型链上是否存在这个构造函数的prototype属性,
           this instanceof fBound === true时,说明返回的fBound被当做new的构造函数调用,
           此时this=fBound(){},否则this=window, 如果是的话使用新创建的this代替硬绑定的this
        */
        if (this instanceof fBound) {
            // 用new调用原函数,并用扩展运算符传递参数
            return new _this(...args, ...arguments);
        } else {
            // 用apply调保存函数_this并传说第合并的参数数组
            return _this.apply(context, args.concat(...arguments));
        }
    }
}
f.mybind({y: 'foo'})(); //undefined "foo"
f.mybind()(); //undefined undefined
f.mybind({y: 'bar'}, 'foo','bbbb')(); //"foo" "bar"

var ab = A.mybind("",1)
var ba = new ab(2) // {a: 1, b: 2}
console.log(ab); 
console.log(ba); //A { a:1, b:2}

https://segmentfault.com/a/1190000021758529
https://www.cnblogs.com/chenwenhao/p/11294541.html#_label4
https://www.cnblogs.com/lxy-starry/p/11302746.html

6. 浅拷贝、深拷贝的实现

浅拷贝:

复制// 1. ...实现
let copy1 = {...{x:1}}

// 2. Object.assign实现
let copy2 = Object.assign({}, {x:1})

深拷贝:

复制// 1. JOSN.stringify()/JSON.parse()  
// 缺点:拷贝对象包含 正则表达式,函数,或者undefined等值会失败
let obj = {a: 1, b: {x: 3}}
JSON.parse(JSON.stringify(obj))

// 2. 递归拷贝


// 深拷贝 类型判断+递归拷贝

function deepClone(obj, hash = new WeakMap()) {
    // null或undefined
    if (obj == null) return obj;
    //  number string boolean symbol
    if (typeof obj !== 'object') return obj;
    // object
    // 日期 
    if (obj instanceof Date) return new Date(obj);
    // 正则
    if (obj instanceof RegExp) return new RegExp(obj);
    if (hash.get(obj)) {
        return hash.get(obj);
    }
    // 数组 / {}
    let newObj = new obj.constructor;
    hash.set(obj, newObj);
    for (let key in obj) {
        // obj无论是数组还是对象 都可以进行for in迭代
        newObj[key] = deepClone(obj[key], hash);
    }
    return newObj;
}

7. 一个节流函数&防抖函数

节流函数:

// 思路:在规定时间内只触发一次
function throttle (fn, delay) {
    // 利用闭包保存时间
    let prev = Date.now()
    return function () {
      let context = this
      let arg = arguments
      let now = Date.now()
      if (now - prev >= delay) {
        fn.apply(context, arg)
        prev = Date.now()
      }
    }
  }
  function fn () {
    console.log('节流')
  }
  
  addEventListener('scroll', fn); // 事件持续发生时持续打印'节流'
  addEventListener('scroll', throttle(fn, 1000); // 事件持续发生时,间隔1s打印一次'节流'

防抖函数:

// 思路:在规定时间内未触发第二次,则执行
function debounce (fn, delay) {
  // 利用闭包保存定时器
  let timer = null
  return function () {
    let context = this
    let arg = arguments
    // 在规定时间内再次触发会先清除定时器后再重设定时器
    clearTimeout(timer)
    timer = setTimeout(function () {
      fn.apply(context, arg)
    }, delay)
  }
}

function fn () {
  console.log('防抖')
}
addEventListener('scroll', debounce(fn, 1000))

8. instanceof 的原理

instanceof 用于判断一个引用类型是否属于某构造函数;还可以在继承关系中用来判断一个实例是否属于它的父类型。
instanceof 的原理是判断实例对象的 __proto__ 是否与构造函数的prototype指向同一个引用。


// 思路:右边变量的原型存在于左边变量的原型链上
function myinstanceOf(obj, type) {
    let objValue = obj.__proto__
    let typeValue = type.prototype
    while (true) {
      if (objValue === null) {
        return false
      }
      if (objValue === typeValue) {
        return true
      }
      objValue = objValue.__proto__
    }
}

let n = 1;
console.log(myinstanceOf(n, Number));

9.柯里化函数的实现

todo

柯里化函数的主要作用还是延迟执行。

const curry = ( fn, arr = []) => (...args) => ( (a,b) => b.length === 0? fn(...a) : curry(fn, a))([...arr, ...args],[...args])
let curryPlus = curry((...x)=>x.reduce((a,b)=>a+b))

curryPlus(1) //返回一个函数
curryPlus(1)(2) //返回一个函数

//遇到参数个数为0的情况才执行
curryPlus(1)(2)(4)() //返回7
curryPlus(1,2)(4)() //返回7

10. Object.create 的基本实现原理

Object.create()

Object.myCreate = function (proto, propertyObject = undefined) {
    if (propertyObject === null) {
        // 这里没有判断propertyObject是否是原始包装对象
        throw 'TypeError'
    } else {
        function Fn() {};
        Fn.prototype = proto;
        const obj = new Fn();
        if (propertyObject !== undefined) {
            Object.defineProperties(obj, propertyObject)
        }
        if (proto === null) {
            // 创建一个没有原型对象的对象, Object.create(null)
            obj.__proto__ = null
        }
        return obj
    }
}
// 示例
//  第二个参数null时, 抛出TypeError
// const throwErr = Object.mycreate({a:'aa'}, null) //Uncaught Typetrror
// 构建
const obj1 = Object.myCreate({n
    a: 'aa'
});
console.log(obj1) // {},obj1的构造函数的原型对象是{a: 'aa'} 
const obj2 = Object.myCreate({a: 'aa'}, {
    b: {
        value: 'bb',
        enumerable: true
    }
});
console.log(obj2) //{b:'bb'}, obj2的构造函数的原型使{a: 'aa'}

11 待补充

实现一个基本的 Event Bus
实现一个双向数据绑定
实现一个简单路由
实现懒加载
rem 基本设置
手写实现 AJAX
Array.prototype.reduce()
实现async/awit
Array.prototype.flat()
事件代理
Vue的双向绑定
Array.prototype.map()

12. promise

const resolvePromise = (promise2, x, resolve, reject) => {
    // x和promise2不能是同一个人,如果是同一个人就报错
    // 加一个开关,防止多次调用失败和成功,跟pending状态值一样的逻辑一样,走了失败就不能走成功了,走了成功一定不能在走失败
    if (promise2 === x) {
        return reject(
            new TypeError('Chaining cycle detected for promise #<promise>')
        )
    }
    // 判断如果x是否是一个对象,判断函数是否是对象的方法有:typeof instanceof constructor toString
    if ((typeof x === 'object' && x != null) || typeof x === 'function') {
        let called
        try { // 预防取.then的时候错误
            let then = x.then // Object.definePropertype
            if (typeof then === 'function') {
                // 用then.call()为了避免在使用一次x.then报错
                then.call(x, y => {
                    // resolve(y)// 采用promise的成功结果,并且向下传递
                    if (called) {
                        return
                    }
                    called = true
                    // y有可能是一个promise,那么我们就要继续使用回调函数,直到解析出来的值是一个普通值
                    resolvePromise(promise2, y, resolve, reject)
                }, r => {
                    if (called) {
                        return
                    }
                    called = true
                    reject(r) // 采用promise的失败结果,并且向下传递
                })
            } else {
                if (called) {
                    return
                }
                called = true
                resolve(x) // x不是一个函数,是一个对象
            }
        } catch (err) {
            if (called) {
                return
            }
            called = true
            reject(err)
        }
    } else {
        // x是一个普通值
        resolve(x)
    }
}

class Mypromise {
    constructor(executor) {
        this.state = 'pending';
        this.value = undefined;
        this.reason = undefined;

        this.onResolvedCallbacks = [];
        this.onRejectedCallbacks = [];

        let resolve = (value) => {
            if (this.state == 'pending') {
                this.state = 'fullFilled';
                this.value = value;

                this.onResolvedCallbacks.forEach(fn => fn())
            }
        }
        let reject = (reason) => {
            if (this.state == 'pending') {
                this.state = 'rejected';
                this.reason = reason;

                this.onRejectedCallbacks.forEach(fn => fn())
            }
        }

        try {
            executor(resolve, reject);
        } catch (err) {
            reject(err)
        }
    }
    then(onFullFilled, onRejected) {

        let promise2 = new Mypromise((resolve, reject) => {

            let x;
            console.log('this', this)

            // 同步
            if (this.state == 'fullFilled') {
                setTimeout(() => {
                    try {
                        x = onFullFilled(this.value)
                        // 添加一个resolvePromise()的方法来判断x跟promise2的状态,决定promise2是走成功还是失败
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (err) { // 中间任何一个环节报错都要走reject()
                        reject(err)
                    }
                }, 0)
            }
            if (this.state == 'rejected') {
                setTimeout(() => {
                    try {
                        x = onRejected(this.value)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (err) { // 中间任何一个环节报错都要走reject()
                        reject(err)
                    }
                }, 0)
            }

            // 异步
            if (this.state == 'pending') {
                this.onResolvedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            x = onFullFilled(this.value)
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (err) { // 中间任何一个环节报错都要走reject()
                            reject(err)
                        }
                    }, 0)
                })
                this.onRejectedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            x = onRejected(this.value)
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (err) { // 中间任何一个环节报错都要走reject()
                            reject(err)
                        }
                    }, 0)
                })
            }
        });

        return promise2
    }
}

// const p = new Mypromise((resolve, reject) => {
//     // resolve('success')   // 走了成功就不会走失败了
//     throw new Error('失败') // 失败了就走resolve
//     reject('failed')       // 走了失败就不会走成功
// })
// p.then((res) => {
//     console.log(res)
// }, (err) => {
//     console.log(err)
// })

const p = new Mypromise((resolve, reject) => {
    resolve(100)
})
p.then((data) => {
    console.log('sucess')
    return 520 * data
}, (err) => {
    console.log(err)
}).then((data) => {
    return new Promise((resolve, reject) => {
        console.log(data)
        resolve(data)
    })
}).then((data) => {
    console.log('result', data) // 52000
})


// function promiseTest() {
//     let promise = new Mypromise((resolve, reject) => {
//         let r = parseInt(Math.random() * 10)
//         if (r % 2 == 0) {
//             resolve('成功')
//         } else {
//             reject('失败')
//         }
//     })
//     return promise
// }
// const promise = promiseTest()
// promise.then((data) => {
//     console.log(data)
// }).catch((err) => {
//     console.log(err)
// })
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值