每日前端手写题--day12

以下题目来自掘金等其它博客,但是问题的答案都是根据笔者自己的理解做出的。如果你最近想要换工作或者巩固一下自己的前端知识基础,不妨和我一起参与到每日刷题的过程中来,如何?

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

  1. 反转字符串并输出
  2. 实现一个eventEmitter
  3. 叙述jsonp原理并实现此功能

下面是我自己写的答案:

1. 反转字符串并输出

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

js 复制代码
function sReverse (source) {
    if (!source.length) return '';
    return source.split('').reverse().join('');
}

2. 实现一个eventEmitter

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

js 复制代码
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脚本中,如下所示:

前端代码

js 复制代码
const scriptUrl = 'http://localhost:6666/my-script.js';

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

  // 在脚本加载完成时触发回调函数
  script.onload = callback;

  // 将 <script> 标签插入到页面
  document.body.appendChild(script);
}

// 加载脚本并定义回调函数
loadScript(scriptUrl, function() {
  console.log(window._info); // 输出 "今天中午吃米线"
});

后端代码

js 复制代码
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功能。

js 复制代码
function myJsonp ( {url, params} ) {
    return new Promise(
        res => {
            // 创建动态script标签:创建不会发起get请求,需要设置src的值,type的值然后插入到文档中才会生效
            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('&')}`;
            // 往文档中添加此动态script标签,发起get请求
            document.body.appendChild(_script);

        }
    );
}

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

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

对应的后端代码示例:

js 复制代码
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') {
    // const callbackName = queryData.callback;
    // const data = queryData.data;

    // 设置响应头
    res.setHeader('Content-Type', 'application/javascript');

    // 构造 JSONP 响应内容
    const _f = /callback=(.*?)[&]*$/g.exec(req.url); //   callback=tmp_0df0608773855
    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}`);
});
相关推荐
蜗牛快跑21311 分钟前
面向对象编程 vs 函数式编程
前端·函数式编程·面向对象编程
Dread_lxy12 分钟前
vue 依赖注入(Provide、Inject )和混入(mixins)
前端·javascript·vue.js
涔溪1 小时前
Ecmascript(ES)标准
前端·elasticsearch·ecmascript
用户3157476081351 小时前
成为程序员的必经之路” Git “,你学会了吗?
面试·github·全栈
榴莲千丞1 小时前
第8章利用CSS制作导航菜单
前端·css
奔跑草-1 小时前
【前端】深入浅出 - TypeScript 的详细讲解
前端·javascript·react.js·typescript
羡与1 小时前
echarts-gl 3D柱状图配置
前端·javascript·echarts
guokanglun1 小时前
CSS样式实现3D效果
前端·css·3d
咔咔库奇2 小时前
ES6进阶知识一
前端·ecmascript·es6
前端郭德纲2 小时前
浏览器是加载ES6模块的?
javascript·算法