前端手写题(二)

目录

一、new

二、call

三、apply

四、bind

五、create


一、new

new 的核心作用:基于构造函数创建实例,绑定原型链,初始化对象属性。

底层四步:创建空对象 → 绑定原型 → 执行构造函数 → 处理返回值

function objectFactory(constructor, ...args) {
  // 严格校验构造函数
  if (typeof constructor !== 'function') {
    throw new TypeError('Constructor must be a function');
  }

  // 创建对象并绑定原型
  const instance = Object.create(constructor.prototype);
  
  // 执行构造函数并获取返回值
  const result = constructor.apply(instance, args);
  
  // 处理返回值(特别处理 null)
  return result !== null && 
    (typeof result === 'object' || typeof result === 'function') 
      ? result 
      : instance;
}
// 构造函数返回对象的情况
function Car(model) {
    this.model = model;
    return { custom: 'return value' };
}

const car1 = new Car('Tesla');
const car2 = objectFactory(Car, 'BMW');

console.log('\n--- 构造函数返回对象测试 ---');
console.log('原生 new:', car1 instanceof Car, car1); // false { custom: 'return value' }
console.log('手写实现:', car2 instanceof Car, car2); // false { custom: 'return value' }

// 构造函数返回原始值的情况
function Bike(brand) {
    this.brand = brand;
    return 'This should be ignored';
}

const bike1 = new Bike('Yamaha');
const bike2 = objectFactory(Bike, 'Honda');

console.log('\n--- 构造函数返回原始值测试 ---');
console.log('原生 new:', bike1 instanceof Bike, bike1); // true Bike { brand: 'Yamaha' }
console.log('手写实现:', bike2 instanceof Bike, bike2); // true Bike { brand: 'Honda' }

 局限性:

  1. 对内置包装对象(如 NumberString)的处理不完善
  2. 内置包装构造函数(NumberStringBoolean)在被函数式调用(如 apply)时,会尝试返回原始值而不是设置新对象的内部插槽
  3. SymbolBigInt 等类型不能作为构造函数调用,会直接抛出错误
  4. DateRegExp 等内置类型在被函数式调用时有不同的初始化逻辑

改进思路​​:

  1. 特殊处理内置构造函数,直接使用 new 创建实例
  2. 针对特定类型(如 Symbol)创建适配逻辑
  3. 完善返回值处理逻辑,考虑原始值返回的特殊情况

二、call

call:立即调用函数,指定 this 并逐个传递参数。

实现步骤:

  1. 处理上下文(context为null或undefined时指向全局对象)
  2. 创建唯一属性键,避免覆盖原有属性
  3. 将当前函数设置为上下文的方法
  4. 执行函数并保存结果
  5. 删除临时添加的方法
  6. 返回执行结果
Function.prototype.myCall = function (context, ...args) {
  // 1. 处理上下文(context为null或undefined时指向全局对象)
  context = context || globalThis;

  // 2. 创建唯一属性键,避免覆盖原有属性
  const fnKey = Symbol('fnKey');

  // 3. 将当前函数设置为上下文的方法
  context[fnKey] = this;

  // 4. 执行函数并保存结果
  const result = context[fnKey](...args);

  // 5. 删除临时添加的方法
  delete context[fnKey];

  // 6. 返回执行结果
  return result;
};
// 测试用例
function greet(greeting, punctuation) {
  return `${greeting}, ${this.name}${punctuation}`;
}

const person = { name: 'Alice' };
console.log(greet.myCall(person, 'Hello', '!')); // "Hello, Alice!"

三、apply

apply:立即调用函数,指定 this 并以数组形式传递参数。

实现步骤:

  1. 处理上下文(context为null或undefined时指向全局对象)
  2. 创建唯一属性键,避免覆盖原有属性
  3. 将当前函数设置为上下文的方法
  4. 执行函数,处理参数数组
  5. 删除临时添加的方法
  6. 返回执行结果
Function.prototype.myApply = function(context, argsArray) {
  // 1. 处理上下文
  context = context || globalThis;
  
  // 2. 创建唯一属性键
  const fnKey = Symbol('fnKey');
  
  // 3. 将当前函数设置为上下文的方法
  context[fnKey] = this;
  
  // 4. 执行函数(处理参数数组)
  const result = argsArray 
    ? context[fnKey](...argsArray) 
    : context[fnKey]();
  
  // 5. 删除临时添加的方法
  delete context[fnKey];
  
  // 6. 返回执行结果
  return result;
};
// 测试用例
function sum(a, b, c) {
  return a + b + c + this.d;
}

const obj = { d: 10 };
console.log(sum.myApply(obj, [1, 2, 3])); // 16 (1+2+3+10)

四、bind

bind:返回一个绑定了 this 和预设参数的新函数,需手动调用。

Function.prototype.myBind = function (context, ...args) {
  // 保存原始函数引用
  const self = this;

  // 返回绑定函数
  return function boundFn(...newArgs) {
    // 判断是否通过 new 调用
    if (this instanceof boundFn) {
      return new self(...args, ...newArgs);
    }

    // 普通调用使用绑定的上下文
    return self.apply(context, [...args, ...newArgs]);
  };
};
// 测试用例
function multiply(a, b, c) {
  return a * b * c * this.d;
}

const bound = multiply.myBind({ d: 2 }, 3);
console.log(bound(4, 5)); // 3*4*5*2 = 120

// 测试 new 调用
function Person(name, age) {
  this.name = name;
  this.age = age;
}
const BoundPerson = Person.myBind(null, 'Alice');
const p = new BoundPerson(30);
console.log(p.name, p.age); // Alice 30

五、create

Object.create:用于创建一个新对象,并将现有对象作为新创建对象的原型。

function myCreate(proto, properties) {
  if (typeof proto !== 'object' && proto !== null) {
    throw new Error();
  }

  function F() {} // 空构造函数
  F.prototype = proto; // 设置原型
  const obj = new F(); // 创建新对象

  // 可选:添加属性
  if (properties) {
    Object.defineProperties(obj, properties);
  }

  return obj;
}
// 测试
const person = { name: 'Alice' };
const me = myCreate(person);
console.log(me.name); // "Alice"(继承自 person)
console.log(Object.getPrototypeOf(me) === person); // true

ps:Object.create() 创建的对象只是原型指向源对象,并不会继承它的任何属性;而 new 出来的对象会继承原型对象的属性和方法。 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值