每日前端手写题--day12

如果你最近想要换工作或者巩固一下自己的前端知识基础,不妨和我一起参与到每日刷题的过程中来,如何?

第12天要刷的手写题如下:

  1. 反转字符串并输出

  2. 实现一个eventEmitter

  3. 叙述jsonp原理并实现此功能

下面是我自己写的答案:

1. 反转字符串并输出

思路:将字符出先转成数组,然后进行反转,最后再拼接成新的字符串。这道题看似在考查字符串,实际上是在考察数组上的方法:

function sReverse (source) {
    if (!source.length) return '';
    return source.split('').reverse().join('');
}

2. 实现一个eventEmitter

分析:此功能的本质是考察函数作为数组元素以及元素上的方法。 方法: - 订阅事件:向话题对应的数组中添加函数元素 - 发布事件:依次执行某个话题对应的数组中的所有函数 - 取消订阅:对话题对应的数组中中的某个函数元素进行删除 - 单次订阅:可以看成由订阅和取消订阅功能组成

class EventEmitter {
    constructor () {
        this.events = {};
    }
    on (topic, registerCb) {
        this.events[topic] = this.events[topic] ?? [];
        this.events[topic].push(registerCb);
    }
    off (topic, registerCb) {
        this.events[topic] = this.events[topic].filter(f => f!==registerCb);
    }
    once (topic, registerCb) {
        const _registerCb = () => {
            this.off(topic, _registerCb);
            registerCb();
        }

        this.on(topic, _registerCb);
    }
    emit (topic) {
        const _topicCallbacks = this.events[topic];
        _topicCallbacks?.forEach(
            cb => void cb()
        )
    }
}


3. 叙述jsonp原理并实现此功能

  • jsonp原理:前端构造script标签,和一个数据接受函数f; 然后设置src的值并将其插入到文档中去,以此发送一个绕过跨域检测的get请求;服务器收到这种get请求之后,根据get请求传递的信息(函数f名),将准备好的数据作为调用此约定函数的形参,并将函数调用作为字符串原码写在将要返回的脚本文件中;当script标签加载完毕之后,返回的脚本文件会自动执行,此时数据就通过预设函数f被转发到正确的位置(或被预定操作处理)。

  • 用一句话来概括一下:将本来通过接口访问的信息通过js脚本来传递,然后通过函数调用的方法将js脚本中的信息取出来。

  • 实际上,数据接受函数f不是重点,重点是将数据写在js脚本中,如下所示:

前端代码

const scriptUrl = 'http://localhost:6666/my-script.js';

function loadScript(url, callback) {
  const script = document.createElement('script');
  script.src = url;

  
  script.onload = callback;

  
  document.body.appendChild(script);
}


loadScript(scriptUrl, function() {
  console.log(window._info); 
});


后端代码

const http = require('http');

const server = http.createServer((req, res) => {
  if (req.url === '/my-script.js') {
    
    res.setHeader('Content-Type', 'application/javascript');

    
    const scriptContent = 'window._info = "今天中午吃米线";';
    res.end(scriptContent);
  } else {
    res.statusCode = 404;
    res.end('Not Found');
  }
});

const port = 6666;
server.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});


上面的代码已经很好的展示了jsonp的原理了,但是将传递的数据挂载在window上面毕竟是不正规的做法,并且在动态的script标签生效之后并没有及时移除。 下面实现一个基本的通用型jsonp功能。

function myJsonp ( {url, params} ) {
    return new Promise(
        res => {
            
            let _script = document.createElement('script');
            
            let _f = Math.random().toString(16).replace('0.','tmp_');
            
            globalThis[_f] = function (data) {
                
                res(data);
                
                document.body.removeChild(_script);
                
                delete globalThis[_f];
            }
            
            let _arr = [];
            for (let key in params) {
                if (params.hasOwnProperty(key)) {
                    arr.push(`${key}=${params[key]}`)
                }
            }
            if (_arr.length) {
                _arr.unshift('');
            }
            
            _script.type = 'text/javascript';
            
            _script.src = `${url}?callback=${_f}${_arr.join('&')}`;
            
            document.body.appendChild(_script);

        }
    );
}

const url = `http://localhost:6666/my-jsonp`;
const _r = myJsonp(
    {
        url,
    }
);

_r.then(d=>void console.log(d));

对应的后端代码示例:

const http = require('http');
const url = require('url');
const querystring = require('querystring');

const server = http.createServer((req, res) => {
  const parsedUrl = url.parse(req.url, true);

  if (parsedUrl.pathname === '/my-jsonp') {
    
    

    
    res.setHeader('Content-Type', 'application/javascript');

    
    const _f = /callback=(.*?)[&]*$/g.exec(req.url); 
    const data = `今天中午吃红烧肉`;
    const responseContent = `globalThis.${_f[1]}("${data}")`;
    res.end(responseContent);
  } else {
    res.statusCode = 404;
    res.end('Not Found');
  }
});

const port = 6666;
server.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});


  • 8
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Web面试那些事儿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值