161. 面试官:commonjs中的module.export和module怎么实现的?

161期

1. commonjs中的module.export和module怎么实现的?
2. export和default export有什么区别?
3. commonJs的模块和esmodule的模块有什么区别?

上面问题的答案会在第二天的公众号(程序员每日三问)推文中公布

也可以小程序刷题,已收录500+面试题及答案2b36074c2a35d0cc6a42b4f3f1b15a98.jpeg

160期问题及答案

1. JS中改变this指向的方法有哪些?

在JavaScript中,this指向的是函数调用的上下文,它可能是全局对象、函数内部的局部对象、某个对象的方法或者通过构造函数创建的实例。但有时,我们需要改变this的指向,以适应不同的编程场景。JavaScript提供了几种方法来实现这一点:

  1. call()方法:call()方法可以让我们在调用函数时,将函数内部的this指向第一个参数指定的对象。如果第一个参数为nullundefinedthis会指向全局对象。

    function showName(label) {
      console.log(label + ':', this.name);
    }
    
    const person = { name: 'Alice' };
    showName.call(person, 'Person Name'); // 输出:Person Name: Alice
  2. apply()方法:apply()方法的作用与call()方法类似,唯一的区别是apply()方法接受参数为一个数组(或者类数组对象)。

    function sum(a, b) {
      return a + b + this.c;
    }
    
    const obj = { c: 3 };
    console.log(sum.apply(obj, [1, 2])); // 输出:6
  3. bind()方法:bind()方法可以创建一个新的函数,这个新函数的this被永久绑定为bind()方法的第一个参数指定的对象,不管这个新函数是如何被调用的。

    function greet(greeting) {
      console.log(greeting + ' ' + this.name);
    }
    
    const user = { name: 'John' };
    const greetUser = greet.bind(user);
    greetUser('Hello'); // 输出:Hello John
  4. 箭头函数:箭头函数不绑定自己的this,它会捕获其所处上下文的this值作为自己的this值。因此,在箭头函数内部使用this时,其指向在它被创建时的上下文。

    const person = {
      name: 'Emma',
      sayHi: function() {
        setTimeout(() => {
          console.log('Hi, ' + this.name);
        }, 1000);
      }
    };
    
    person.sayHi(); // 1秒后输出:Hi, Emma

请注意,在箭头函数中,尝试使用call()apply()bind()方法改变this指向是不会有效果的,因为箭头函数的this已经按词法作用域绑定了。

  1. 使用上下文对象:有时候,你可能不想用上述方法中的任意一个来改变this的指向,你也可以通过将需要的this值保存到一个变量中,然后在需要的地方使用这个变量。

    function Timer() {
      this.seconds = 0;
      var self = this; // 保存this值
      setInterval(function() {
        self.seconds++;
      }, 1000);
    }
    
    var timer = new Timer();
    setTimeout(() => console.log(timer.seconds), 3100); // 大约3秒后输出:3

上述方法都是在不同场景下用来改变函数内部this指向的常见技术。一定要根据具体的编程需求和场景来选择合适的方法。

2. 说说React事件和原生事件的执行顺序?

在React中,事件处理机制由React自己的合成事件(SyntheticEvent)系统处理,并不是原生事件处理机制。React的合成事件系统是基于原生事件系统之上实现的,它在内部为了处理跨浏览器的兼容性问题,抹平了浏览器之间的差异,同时也提供了一种方式来在React的组件树中更加一致地处理事件。

React事件和原生事件的执行顺序取决于如何注册这些事件。如果你使用React来绑定事件,那么合成事件会首先处理,然后才是任何原生事件处理器。React在文档的根节点上添加了单一的事件监听器来处理所有事件,这种事件处理机制称为事件委托。

具体执行顺序如下:

  1. 当事件发生时,首先触发React的合成事件处理机制。

  2. 若在React中注册的事件中调用了event.stopPropagation(),事件将不会冒泡到DOM树更上层的原生事件监听器。

  3. 如果没有在React的合成事件中阻止事件冒泡,合成事件处理完成后,事件会继续冒泡,按照事件监听器在DOM树中的注册位置,触发对应的原生事件监听器。

  4. 如果原生事件处理器调用了event.stopPropagation(),继续冒泡的过程则停止。

举例来说,如果你在一个React元素上通过onClick属性注册了事件处理函数,并且也在其父元素的原生DOM上通过addEventListener注册了一个click事件处理器,点击该React元素时会发生如下情况:

  • React内部的合成click事件首先被触发。

  • 如果合成事件处理函数中没有调用event.stopPropagation(),事件会继续传播。

  • DOM树中的原生click事件监听器随后被触发。

需要注意的是,从React 17开始,React事件处理不再使用文档根节点上的事件委托,而是将事件委托到了React挂载的根节点(即ReactDOM.render(<App />, root)中的root)。相比之前的版本,这改变了一些事件处理的细节,但总体的执行顺序仍然如上所述。

示例代码:

class MyComponent extends React.Component {
  componentDidMount() {
    // 在原生DOM上注册一个click事件
    this.ref.addEventListener('click', this.handleNativeClick);
  }

  componentWillUnmount() {
    // 组件卸载前移除在原生DOM上注册的click事件
    this.ref.removeEventListener('click', this.handleNativeClick);
  }

  handleNativeClick = (e) => {
    console.log('native event');
    // 可以调用e.stopPropagation()阻止事件冒泡到React合成事件
  };

  handleClick = (e) => {
    console.log('synthetic event');
    // 使用e.stopPropagation()可以阻止事件冒泡到原生事件
  };

  render() {
    // 使用React的合成事件注册click事件
    return <div onClick={this.handleClick} ref={(el) => { this.ref = el; }}>Click Me</div>;
  }
}

在上述示例中,如果点击<div>元素,控制台会首先打印synthetic event,然后打印native event。如果我们在handleClick(合成事件处理器)中调用了e.stopPropagation()handleNativeClick将不会执行,因此控制台只会打印synthetic event

3. 去除字符串中出现次数最少的字符,不改变原字符串的顺序

要去除一个字符串中出现次数最少的字符,不改变原字符串的顺序,可以按照以下步骤编写代码:

  1. 首先统计字符串中每个字符出现的次数。

  2. 找出出现次数最少的字符,可能有多个。

  3. 遍历原字符串,构建一个新字符串,只包含出现次数不是最少的字符。

以下是用JavaScript实现的示例代码:

function removeLeastFrequentChars(str) {
  const charMap = {};
  let minFrequency = Infinity;
  let result = '';

  // 统计每个字符的出现次数
  for (const char of str) {
    if (!charMap[char]) {
      charMap[char] = 1;
    } else {
      charMap[char] += 1;
    }
    // 与最小出现次数进行比较
    minFrequency = Math.min(minFrequency, charMap[char]);
  }

  // 找出出现次数最少的字符集合
  const leastFrequentChars = new Set();
  for (const char in charMap) {
    if (charMap[char] === minFrequency) {
      leastFrequentChars.add(char);
    }
  }

  // 构建不包含出现次数最少字符的字符串
  for (const char of str) {
    if (!leastFrequentChars.has(char)) {
      result += char;
    }
  }

  return result;
}

// 示例
const input = 'ababac';
const output = removeLeastFrequentChars(input);
console.log(output); // 'ababa'

在这个例子中,我们通过charMap对象来计数每个字符的出现次数,然后通过leastFrequentChars集合确定哪些字符是出现次数最少的。最后,我们遍历原字符串str,并构建出新的字符串result,其中不包括出现次数最少的字符。这样,新字符串保留了原有字符的顺序,只是去除了那些出现次数最少的字符。

因为微信公众号修改规则,如果不标星或点在看,你可能会收不到我公众号文章的推送,请大家将本公众号星标,看完文章后记得点下赞或者在看,谢谢各位!

学习不打烊,充电加油只为遇到更好的自己,每天早上9点纯手工发布面试题,每天坚持花20分钟来学习与思考,在千变万化,类库层出不穷的今天,不要等到找工作时才狂刷题,提倡每日学习。

  • 21
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值