144. 面试官:Vue模板是如何编译的?

144期

1. Vue模板是如何编译的?
2. TS中never类型是用来干嘛的?
3. 请手写冒泡排序?

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

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

143期问题及答案

1. 手写一个apply函数?

apply 函数是 JavaScript 中的 Function 原型上的一个方法,它的功能是调用一个函数,同时可以将一个上下文(即this)和单个数组(或类数组对象)作为参数传递给这个函数。以下是一个用来模拟原生 apply 方法功能的自定义实现:

function customApply(context = window, args) {
  if (typeof this !== 'function') {
    throw new TypeError('Error');
  }

  // 给context创建一个唯一属性以避免覆盖原有属性
  const fnSymbol = Symbol('fn');
  // this指向调用customApply的函数
  context[fnSymbol] = this;

  // 处理边界情况,即没有第二个参数args的情况
  let result;
  if (args == null) {
    result = context[fnSymbol]();
  } else if (!Array.isArray(args)) {
    throw new TypeError('CreateListFromArrayLike called on non-object');
  } else {
    result = context[fnSymbol](...args); // 展开参数数组
  }

  delete context[fnSymbol]; // 移除临时属性
  return result; // 返回函数执行结果
}

// 通过Function.prototype添加该自定义函数
Function.prototype.customApply = customApply;

// 下面是测试代码
function greet(greeting, punctuation) {
  return `${greeting}, ${this.name}${punctuation}`;
}

const context = {
  name: 'John'
};

// 正常使用apply
console.log(greet.apply(context, ['Hello', '!'])); // "Hello, John!"

// 使用自定义的customApply
console.log(greet.customApply(context, ['Hello', '!'])); // "Hello, John!"

在这个 customApply 函数实现中,我们首先检查了调用 customApply 的对象是否为一个函数类型,如果不是则抛出 TypeError。接着,我们为 context 对象创建了一个唯一标识的属性,并且将这个属性指向了调用 customApply 的函数。然后,我们根据传递的参数 args 调用这个函数,并在调用后删除我们之前添加到 context 上的属性,并返回函数调用的结果。如果 args 不传递或者为 null,则直接调用函数;如果传递的 args 不是数组类型,则抛出 TypeError

这就是一个 apply 函数的简单模拟实现,它允许你在任何函数上调用 customApply 方法并且指定 this 上下文和参数。

2. 删除链表中的某个节点?

在数据结构中的单向链表中删除一个节点需要以下步骤:

  1. 定位到待删除节点的前一个节点。

  2. 将前一个节点的 next 指针指向待删除节点的 next 节点。

这里是删除链表节点的一个基本示例,使用JavaScript来实现:

function ListNode(val) {
  this.val = val;
  this.next = null;
}

function removeNode(head, node) {
  if (head === null) return null;
  
  if (head === node) { // 如果要删除的是头节点
    return head.next; // 直接返回头节点的下一节点
  }

  let current = head;

  // 遍历链表,找到待删除节点的前一个节点
  while (current.next !== null && current.next !== node) {
    current = current.next;
  }

  if (current.next === node) {
    // 找到节点,进行删除
    current.next = current.next.next;
  } else {
    // 没找到节点,可能是因为传入了链表中不存在的节点
    console.log("Node not found in the list");
  }

  return head; // 返回链表头节点
}

在这段代码中,我们首先检查 head 是否为空或者就是要删除的节点,接着遍历链表直到找到待删除节点的前一个节点,并对其进行删除操作。若待删除的节点在链表中不存在,我们将打印一条错误信息。

这是用 JavaScript 实现的链表节点删除操作的伪代码,其他的编程语言实现会有所不同,但是理念是一致的。需要注意的是,删除链表节点时,如果外部环境还持有对该节点的引用,则需要额外注意内存管理。在使用如C++这样手动管理内存的编程语言时,还需要确保删除后释放节点占用的内存空间。

3. new过程具体执行了什么?能否自己实现一个?

在 JavaScript 中,new 操作符用于创建一个用户定义的对象类型的实例或具有构造函数的内置对象类型的实例。使用 new 进行对象创建的过程大致可以分为以下几个步骤:

  1. 创建一个新对象。

  2. 将这个新对象的内部属性 [[Prototype]] (proto) 链接到构造函数的原型对象(prototype)上。

  3. 将新对象作为 this 上下文传递给构造函数执行。

  4. 如果构造函数返回的是一个对象,则返回这个对象。如果构造函数没有显式返回一个对象,则默认返回创建的新对象。

下面我们通过伪代码来模拟 new 操作符的工作流程:

function customNew(constructor, ...args) {
  // 1. 创建一个新对象
  const obj = {};

  // 2. 将新创建的对象的__proto__链接到构造函数的prototype对象上
  Object.setPrototypeOf(obj, constructor.prototype);

  // 3. 将新对象作为this上下文调用构造函数
  const result = constructor.apply(obj, args);

  // 4. 根据构造函数返回值确定返回的对象
  return (result !== null && (typeof result === 'object' typeof result === 'function')) ? result : obj;
}

// 测试用例
function Person(name, age) {
  this.name = name;
  this.age = age;
  this.sayHello = function() {
    console.log(`Hello, my name is ${this.name}`);
  };
}

// 使用customNew模拟new操作
const john = customNew(Person, 'John', 30);
john.sayHello(); // Hello, my name is John
console.log(john); // Person { name: 'John', age: 30, sayHello: [Function] }

customNew 函数中,我们首先创建了一个空对象 obj。然后我们将这个空对象的 __proto__ 属性设置为构造函数 constructorprototype 属性,从而实现继承原型。接着,我们使用 apply 方法以新对象作为 this 上下文调用构造函数,传递剩余参数 ...args。最后,我们检查构造函数是否返回了一个对象。如果构造函数返回了一个对象,我们就使用这个对象;如果没有返回,或者返回的不是对象类型,我们就返回最开始创建的新对象 obj

customNew 函数能够模拟 new 操作符的大部分功能,但由于它是一个函数而不是操作符,所以在细节上可能会与 new 操作符的行为存在一些差异(比如对构造函数的错误处理)。

需要注意的是,在现代JavaScript引擎中,new 的实现会更加复杂并且进行了优化,上述代码压思才是用来帮助理解 new 的基本工作原理。在实际项目开发中,应当直接使用 new 而不是自己实现。

我要提问

如果你遇到有趣的面试题,或者有想知道的前端面试题,可以在下面的小程序提问,收到问题后会在第一时间为你解答。

我要出题

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值