js笔试中的手写题

call 方法

call方法的作用:改变某个函数运行时的 context 即函数的执行上下文。换句话说,就是改变函数体内部 this 的指向。

	// 这里不能用箭头函数,否则this的指向会有问题(这里的this指调用的那个函数)
    Function.prototype.my_call = function(context, ...argument) {
        let ctx = context || window
        ctx.fn = this				// this赋值给ctx.fn,即ctx.fn指向原函数
        return ctx.fn(...argument)	// 执行ctx.fn,即ctx调用了原函数,此时原函数的this指向ctx
    }

	// 例子
	const obj = {
		name: '孙悟空',
		showName(age) {
			console.log(`${this.name}今年${age}`)
		}
	}

	const newObj = {
		name: '诸葛亮',
	}

	obj.showName.my_call(newObj, 18)    // 诸葛亮今年18岁
拓展补充(基础好的朋友请绕过~)

有人说,网上有很多种写法,和你这个传的参数不一样,甚至不用传参数。其实我想说,都一样的,归根到底都是用来应付面试的,在实际开发过程中,根本用不到手写原生就有的方法~~当然对你有意义有帮助的是手写的过程的一个思路。

	// 当前已知传了一个参数context,思路是一样的,我们要想的是如何获取剩余参数
	Function.prototype.my_call = function(context) {
		const ctx = context || window
		// arguments是一个类数组对象,可以使用Array.from方法转为真正的数组,
		// 这样才得以使用数组原型上的方法shift。目前是假定不能使用call/apply,假如可以使用的话,
		// 可以用Array.prototype.slice.call(arguments, 1)来获取剩余参数
		const args = Array.from(arguments)	// 简单理解为args就是arguments
		args.shift()	// shift方法调用后会影响原数组,所以args就是剩余参数
		ctx.fn = this
		return ctx.fn(...args)
	}
	// 无参数情况,思路是一样的,我们要想如何获取第一个参数和剩余参数
	Function.prototype.my_call = function() {
		const args = Array.from(arguments)
		const ctx = args.shift()		// 获取第一个参数ctx,此时调用了shift方法,args就是剩余参数
		ctx.fn = this
		return ctx.fn(...args)
	}

	// 只要思路清晰,变形是多种多样的
	Function.prototype.my_call = function() {
		const ctx = Array.prototype.shift.call(arguments)   // 获取第一个参数ctx,此时调用了shift方法,arguments就是剩余参数
		ctx.fn = this
		return ctx.fn(...arguments)
	}

	Function.prototype.my_call = function() {
		const [ctx, ...args] = arguments   // 使用拓展运算符解构赋值获取第一个参数ctx和剩余参数args
		ctx.fn = this
		return ctx.fn(...args)
	}

arguments使用技巧总结:

  • 获取第一个参数:Array.prototype.shift.call(arguments)[].shift.call(arguments)注意 shift 方法会影响原数组
  • 获取剩余参数:Array.prototype.slice.call(arguments, 1) 或 [].slice.call(arguments, 1)
  • 使用Array.from(arguments)转为数组后使用数组原型上的方法。
  • 使用解构赋值 const [firstArg, secondArg, ...restArg] = arguments

apply 方法

applycall的作用一样,只是传的参数不一样。

  • func.call(this, arg1, arg2, ...)
  • func.apply(this, [arg1, arg2, ...])
    Function.prototype.my_apply = function(context, argument) {
        let ctx = context || window
        if (!Array.isArray(argument)) {
        	throw new Error('传入的第二个参数必须数组')
		}
        ctx.fn = this
        return ctx.fn(...argument)
    }
	
	// 例子
	const list = [1, 2, 3, 4, 5]
	const min = Math.min.my_apply(Math, list)
	console.log(min)	// 1

new 操作符

	function myNew(Constructor, ...arguments){
		// 创建一个新的空对象
	    let obj = {}
	    // 将新对象的内部属性__proto__指向构造函数的原型,这样新对象就可以访问原型中的属性和方法
	    obj.__proto__ = Constructor.prototype;
	    // 取得构造函数的返回值
	    const ret = Constructor.call(obj, ...arguments)
	    // 如果返回值是一个对象就返回该对象,否则返回构造函数的一个实例对象
	    return typeof ret === 'object' ? ret : obj
	}

instanceof 操作符

    function my_instanceof(L, R) {
    	// L 表示左表达式,R 表示右表达式
    	// 取 R 的显示原型
        const r = R.prototype
        // 取 L 的隐式原型
        let l = L.__proto__
        while(true) {
            if (l === null) return false
            if (l === r) return true
            l = l.__proto__
        }
    }

map 方法

	Array.prototype.my_map = function (fn, context) {
		// 判断第一个参数是否是函数
	    if (typeof fn !== 'function') {
	        throw new Error(`${fn} is not a function`)
	    }
	    let result= []
		// 定义上下文
	    const ctx = context || this
	    // 将回调结果放入数组中
	    this.forEach((item, index) => {
	        result.push(fn.call(ctx, item, index, this)) 
	    })
	    return result
	}

filter 方法

	Array.prototype.my_filter = function (fn, context) {
		// 判断第一个参数是否是函数
	    if (typeof fn !== 'function') {
	        throw new Error(`${fn} is not a function`)
	    }
	    // 定义上下文
	    const ctx = context || this
	    let result = []
	    // 将回调结果放入数组中
	    this.forEach((item, index) => {
	    	if (fn.call(ctx, item, index, this)) {
				result.push(item) 
			}
	    })
	    return result
	}

forEach 方法

	Array.prototype.my_forEach = function (fn, context) {
		// 判断第一个参数是否是函数
	    if (typeof fn !== 'function') {
	        throw new Error(`${fn} is not a function`)
	    }
		// 定义上下文
	    const ctx = context || this
	    for (let i = 0, len = this.length; i < len; i++) {
			fn.call(ctx, this[i], i, this)
	    }
	}

some 方法

	Array.prototype.my_some = function(fn, context) {
  		// 判断第一个参数是否是函数
	    if (typeof fn !== 'function') {
	        throw new Error(`${fn} is not a function`)
	    }
	    // 定义上下文
        const ctx = context || this
        let result = false
        this.forEach((item, index) => {
            if(fn.call(ctx , item, index, this)){
                result = true
            }
        })
        return result
    }

every 方法

	Array.prototype.my_every = function(fn, context) {
  		// 判断第一个参数是否是函数
	    if (typeof fn !== 'function') {
	        throw new Error(`${fn} is not a function`)
	    }
	    // 定义上下文
        const ctx = context || this
        let result = false
        this.forEach((item, index) => {
            if(fn.call(ctx, item, index, this)){
                result = true
            } else {
            	result = false
            }
        })
        return result
    }

deepClone 深克隆

	// 乞丐版,当被克隆对象的属性值类型为Symbol、Function时克隆后新对象会丢失这些类型
	const deepClone = target => JSON.parse(JSON.stringify(target))
	const isObject = obj => typeof obj === 'object' && obj !== null   // 判断是否为对象,注意typeof null 为 object 需要排除
	const deepClone = (target, map = new WeakMap()) => {   // target 为被深克隆的对象,map用于处理互相引用的情况,用WeakMap而不是Map是因为WeakMap是弱引用,有利于垃圾回收
		if (!isObject(target)) return target		 // 判断不是对象则返回自己
		if (map.get(target)) return map.get(target)  // 处理相互引用
		const cloneTarget = Array.isArray(target) ? [] : {}	// 初始化新对象时,注意数组和对象类型
		map.set(target, cloneTarget)   
		// 用Reflect.ownKeys方法可以处理Symbol类型的属性,用Object.keys则不行
		Reflect.ownKeys(target).forEach(key => {
			// 为新对象进行赋值,先判断是否是对象,若是对象则递归调用
			cloneTarget[key] = isObject(target[key]) ? deepClone(target[key]) : target[key]
		})
		return cloneTarget
	}

ajax 请求

	const ajax = (method, url, data) => {
		const xhr = new XMLHttpRequest()
		xhr.open(method, url, true)
		xhr.onreadystatechange = () => {
			if (xhr.readystate === 4 && xhr.status === 200) {
				console.log(xhr.responseText)
			}
		}
		xhr.send(data)
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值