前端面经每日一题day02

bind,call,apply

bind,call,apply作用都可以改变this的指向。对函数调用bind,call,apply,第一个参数是希望this指向的对象,后面就是传递的参数。

用法如下:

let dog={
    name:'dog',
    sayname(num1,num2){
        console.log('name is',this.name)
        console.log('sum is',num1+num2)
    }
}
let cat={
    name:'cat'
}
dog.sayname(1,2)
dog.sayname.call(cat,1,2)
dog.sayname.apply(cat,[1,2])
//bind一种方式
let fn=dog.sayname.bind(cat,1,3)
fn()
//bind另一种方式,柯里化
let fn=dog.sayname.bind(cat,1)
fn(3)

区别:

  • call和apply区别就是传递参数的方式,call参数是一个一个传递的,apply传递的参数以数组的形式。
  • bind和call,apply的区别,bind调用后返回一个值,不是立即执行去函数,所以需要一个函数接收返回值,然后调用函数。call和apply调用后会立即执行函数。 注意bind的一个特点:可以实现柯里化,bind参数可以部分传递,调用新函数在传递剩余参数。

原生js代码

我们自己写一个call,apply,bind
原始js实现call
Function.prototype.mycall = function (ctx, ...args) {
    ctx = ctx || globalThis //如果没有指定对象,就需要指定全局对象
    //this指向调用mycall的对象,this()就是执行Mycall方法,我们需要去让this指向ctx
    // 创建一个唯一的属性名,用于存储我们的函数
    const fn = Symbol();
    ctx[fn] = this  //this放到指向对象的属性里面,这样this就指向了ctx
    // 调用ctx对象上的函数,并传递参数
    const result = ctx[fn](...args)
    delete ctx[fn]
    // 函数需要返回值
    return result
}
function add(x, y) {
    console.log(this, x, y)
    return x + y
}
// 但是我们的ctx里面有一个参数fn
console.log(add.mycall({}, 1, 2, 3))
原始js实现apply
Function.prototype.myapply=function(ctx,args){
	ctx=ctx || globalThis
	const fn=Symbol()
	ctx[fn]=this
	apply必须入数组,所以需要判断,
	if(!Array.isArray(args){
		throw new TypeError('参数错误')
	}
	const result=ctx[fn](...(args || [] )
	delete ctx[fn]
	return result
}
console.log(myapply({},[1,2,3])
原始js实现bind
function fn(...args) {
    console.log('args', ...args)
    console.log('this', this)
    return 11
}
Function.prototype.mybind = function (ctx, ...args) {
    // 对类数组(函数的参数argument)切割获取得到数组
    const arg1 = Array.prototype.slice.call(args, 1)
    const fn = this
    // bind返回函数调用,调用的函数就是newfn,实际上就是fn,也就是this
    // 参数是两次都有,需要我们合并起来
    return function A(...args) {
        // 类数组转化为数组
        let arg2 = Array.prototype.slice.call(args)
        let arg = [...arg1, ...arg2]
        // 需要设置return的返回值,但是我们需要判断,知道他第二次使用函数是
        // 通过判断原型得知他是通过new还是什么方法调用的
        if (Object.getPrototypeOf(this)===A.prototype) {
            // 这里就不用修改this的指向了
            return fn(...arg)
        }
        else {
            return fn.apply('ctx', arg)
        }
    }
}
let newfn = fn.mybind({ 'name': 'jack' }, 1, 2)
// 1.直接使用,这里this指向就是我们bind指定的
let news = newfn(3, 4)
// 2.通过new一个实例,this指向的就是实际调用bind的函数fn
console.log(news)

补充:柯里化。

简单来说就是将函数转化为接收部分参数的函数。也就是函数里面返回函数。

具体可以看这个视频:JavaScript函数柯里化 - Web前端工程师面试题讲解_哔哩哔哩_bilibili

作用一:针对函数传递参数的优化

//输入网址的时候,https/http都是重复的,所以我们使用函数柯里化
let url=function(protocol){
    return function(hostname,pathname){
        return protocol+hostname+pathname
    }
}
let urlall=url('https://')
let url1=urlall('www.baidu.com','/index')
let url2=urlall('www.xinlang.com','/login')
console.log(url1,url2)

作用二:兼容性的检测。比如提前判断浏览器是否支持相应的方法。

//attachEventh(IE)和addEventListner(主流浏览器方法)
// 提供跨浏览器的方法来添加事件监听器,确保无论在哪种浏览器环境下,开发者都能以一致的方式工作。
// 使用立即执行函数
const whichevent=(function(){
    if(window.addEventListener){
        return function(element,type,listener,usecapture){
            element.addEventListener(type,function(e){
                listener.call(element,e)
            },usecapture)
        }
    }else if(window.attachEvent){
        return function(element,type,handler){
            element.attachEvent('on'+type,function(e){
                handler.call(element,e)
            })
        }
    }
})()
//如何使用
whichevent(document.getElementById('myButton'), 'click', function(event) {
    alert('Button was clicked!');
}, false);

作用三:延迟执行,需要时才执行某个操作或计算,而不是在程序一开始就立即执行。

题目

add(1)(2)(3)=6

add(1,2,3)(4)=10

add(1)(2)(3)(4)(5)=15

//add() 没有去执行inner这个函数,返回return inner,
//add()() 去执行inner这个函数,返回return sum
function add(){
    // arguments是一个对象,所以我需要把它转变为数组
    // let args=Array.prototype.slice.call(arguments)
    let args=[...arguments]
    let inner=function(){
        args.push(...arguments)
        let sum=args.reduce(function(pre,cur){
            return pre+cur
        },0)
        return sum
    }
    return inner
}
console.log(add(1,2,3,4)(2))
// 但是现在面临的问题就是不能是不能任意延迟输入参数,
// 每次增加一个(),就需要新增一个内部执行函数,太麻烦
// 我们的解决办法就是通过递归,内部函数返回内部函数,但是函数返回的内容怎么办?
// 可以通过 inner.toStrng控制台输出
function add(){
    // arguments是一个对象,所以我需要把它转变为数组
    let args=[...arguments]
    let inner=function(){
        args.push(...arguments)
        return inner
    }
    inner.toString=function(){
        return args.reduce(function(pre,cur){
            return pre+cur
        },0)
    }
    return inner
}
console.log(add(1,2,3,4)(22)(2).toString())

作用四:含有柯里化的箭头函数

const namelist1=[
    {mid:'one',profession:'yi'},
    {mid:'two',profession:'er'},
    {mid:'three',profession:'san'},
    {mid:'four',profession:'si'}
]
const namelist2=[
    {adc:'jam',profession:'abc'},
    {adc:'mack',profession:'abc'},
    {adc:'tom',profession:'abc'},
]
// 输出数组对象中的abc和mid
console.log(namelist1.map(item=>item.mid))
console.log(namelist2.map(item=>item.adc))
// 如果对象的键值对不一样,我们柯里化箭头函数
const curfn=name=>element=>element[name]
const namemid=curfn('mid')
console.log(namelist1.map(namemid))

补充:立即执行函数IIFE

定义变量fn接收立即执行函数的返回值,调用fn函数,实际上是在调用return返回的函数,因为内部函数被赋值给fn

function fn(){}的意思就是声明语句,语句没有返回值,不可以使用括号调用。只有表达式才有返回值,如fn()

特点:1.不必为函数命名,避免污染全局变量。2.立即执行函数内部形成了单独的作用域,可以封装一些外部无法读取的私有变量。

//首先格式有两种 
(function(){})()
(funcion(){}())

//方式一
(function () {
    console.log(2321)
})();
//方式二
(function () {
    console.log(1111)
}())
//fn是立即执行函数表达式,调用fn传递参数,实际上是在调用立即执行函数返回的函数。
//所以下面传参,可以实现两数相加,调用了立即执行函数返回的函数。
let fn=(function(){
    return function(x,y){
        return x+y
    }
})()
console.log(fn(1,2))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值