JS基础回顾——bind方法/new实现/继承实现方式

bind

以下是 JavaScript 中 bind 方法的实现原理及关键步骤解析:


//bind函数
  Function.prototype.myBind = function (context = window, ...initialArgs) {
    
    
    // 检查调用 myBind 的是否为函数
    if (typeof this!== 'function') {
    
    
        throw new TypeError('调用 myBind 的对象必须是函数');
    }
    // 保存调用 myBind 的原始函数
    const originalFunction = this;
    // 返回一个新函数
    const boundFunction = function (...newArgs) {
    
    
        // 判断是否使用 new 调用
        const isUsingNew = this instanceof boundFunction;
        // 合并预设参数和新传入的参数
        const allArgs = [...initialArgs, ...newArgs];
        // 如果是使用 new 调用,this 指向新创建的实例
        const thisValue = isUsingNew? this : context;
        // 调用原始函数
        return originalFunction.apply(thisValue, allArgs);
    };
    // 复制原函数的原型
    if (originalFunction.prototype) {
    
    
        boundFunction.prototype = Object.create(originalFunction.prototype);
    }
    return boundFunction;
};

核心目标

bind 方法用于创建一个新函数,该函数的 this 值被永久绑定到指定对象,并支持预设部分参数。其实现需解决以下问题:

  1. 绑定 this:确保新函数调用时的上下文固定。
  2. 参数合并:支持预设参数与新参数的拼接(柯里化)。
  3. 原型链继承:新函数需继承原函数的原型链,且允许 new 操作符调用。

实现步骤详解

1. 校验调用者类型

必要性bind 必须由函数调用,否则抛出错误。
实现:检查 this 的类型是否为函数。

if (typeof this !== 'function') {
    
    
  throw new Error('Function.prototype.bind - not callable');
}
2. 保存原函数与预设参数

闭包与柯里化:通过闭包保存原函数(self)和预设参数(args),以便后续拼接参数。

const self = this; // 原函数
const args = Array.prototype.slice.call(arguments, 1); // 预设参数
3. 创建新函数并处理调用逻辑

动态 this 判断
• 若通过 new 调用新函数,this 指向新实例(忽略绑定的上下文)。
• 否则,使用绑定的上下文(context)。

const fBound = function() {
    
    
  const bindArgs = Array.prototype.slice.call(arguments);
  return self.apply(
    this instanceof fNOP ? this : context, // new 调用时忽略绑定
    args.concat(bindArgs) // 合并预设参数与调用参数
  );
};
4. 维护原型链继承

中间空函数(fNOP:避免直接修改原函数的原型链,通过一个空函数继承原函数原型。

const fNOP = function() {
    
    };
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP(); // 继承原函数原型
5. 返回绑定后的函数

• 最终返回 fBound,其原型链与原函数一致,且 this 和参数已处理完成。


关键问题与解决方案

1. new 操作符的特殊处理

现象:若对 bind 返回的函数执行 new,原构造函数会被调用,且 this 指向新实例。
原理:通过 this instanceof fNOP 判断是否通过 new 调用。

2. 多次 bind 的不可覆盖性

表现:多次调用 bind 时,首次绑定的 this 无法被后续 bindcall 覆盖。
原因bind 生成的函数内部已固化目标函数和 this,仅参数可叠加。

3. 原型链污染规避

中间函数隔离:通过 fNOP 隔离新函数与原函数的原型,避免直接修改原型的副作用。


Polyfill 实现示例

Function.prototype.myBind = function(context) {
    
    
  if (typeof this !== 'function') {
    
    
    throw new TypeError('Error');
  }
  const self = this;
  const args = Array.prototype.slice.call(arguments, 1);
  const fNOP = function() {
    
    };
  const fBound = function() {
    
    
    return self.apply(
      this instanceof fNOP ? this : context,
      args.concat(Array.prototype.slice.call(arguments))
    );
  };
  fNOP.prototype = this.prototype;
  fBound.prototype = new fNOP();
  return fBound;
};

应用场景

  1. 固定 this:解决回调函数中的 this 丢失问题(如事件处理器)。
  2. 参数预设:实现函数柯里化,例如预设 API 请求的公共参数。
  3. 构造函数兼容:允许 bind 生成函数通过 new 调用,保持原型链完整性。

总结

bind 的实现核心在于 闭包保存上下文参数柯里化拼接原型链继承处理。通过中间函数隔离原型、动态判断 new 调用场景,确保其行为与原生方法一致。实际开发中可直接使用原生 bind,但理解其原理有助于解决高阶函数设计中的疑难问题。

new

function myNew(constructor,...args){
    
    
    let obj =obj.create(constructor.Prototype);
    let result=constructor.apply(obj,args);
    return typeof result ===`object`?result:obj;
}

继承实现方式

// 继承的多种方式
/* // 1. 原型链继承
//共享父类实例的属性和方法
function Parent() {
    this.name = 'parent'
    this.play = [1, 2, 3]
}
Parent.prototype.getName = function() {
    console.log(this.name)
}

function Child() {
    this.type = 'child'
}
核心
Child.prototype = new Parent();
const child1 = new Child();
child1.play.push(4);
console.log(child1.play); // [1, 2, 3, 4]
const child2 = new Child();
console.log(child2.play); // [1, 2, 3, 4] */

/* // 2. 构造函数继承
//只能继承父类实例的属性和方法,不能继承父类原型的属性和方法
function Parent(name) {
    this.name =name
    this.play = [1, 2, 3]
}
Parent.prototype.getName = function() {
    console.log(this.name)
}

function Child(name) {
    Parent.call(this,name)
}
const child = new Child("child1");
child.play.push(4);
console.log(child.play); // [1, 2, 3, 4]
console.log(child.name); // child1
const child2 = new Child("child2");
console.log(child2.name); // child2
console.log(child2.play); // [1, 2, 3]
console.log(child.getName()); // 报错,无法获取原型上方法
 */

/*  // 3. 组合继承
//结合了两种模式的优点,但是会调用两次父类构造函数
function Parent(name) {
    this.name =name
    this.play = [1, 2, 3]
}
Parent.prototype.getName = function() {
    console.log(this.name)
}

function Child(name,age) {
    Parent.call(this,name)
    this.age = age
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;

const child = new Child("child1",18);
child.play.push(4);
console.log(child.play); // [1, 2, 3, 4]
console.log(child.name); // child1
console.log(child.age); // 18
const child2 = new Child("child2",19);
console.log(child2.name); // child2
console.log(child2.age); // 19
console.log(child2.play); // [1, 2, 3]  */

/*// 4.  寄生组合继承
//组合继承的优化版,避免了组合继承的缺点 ,只调用一次父类构造函数
function Parent(name) {
    this.name =name
    this.play = [1, 2, 3]
}
Parent.prototype.getName = function() {
    console.log(this.name)
}

function Child(name,age) {
    Parent.call(this,name)
    this.age = age
}
//new Parent()->Object.create(Parent.prototype)
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;

const child = new Child("child1",18);
child.play.push(4);
console.log(child.play); // [1, 2, 3, 4]
console.log(child.name); // child1
console.log(child.age); // 18
const child2 = new Child("child2",19);

console.log(child2.name); // child2
console.log(child2.age); // 19
console.log(child2.play); // [1, 2, 3]  */

/*// 5.  class继承
class Parent {
    constructor(name) {
        this.name = name
        this.play = [1, 2, 3]
    }
    getName() {
        console.log(this.name)
    }
}

class Child extends Parent {
    constructor(name,age) {
        super(name)
        this.age = age
    }
}
const child = new Child("child1",18);
child.play.push(4);
console.log(child.play); // [1, 2, 3, 4]
console.log(child.name); // child1
console.log(child.age); // 18
const child2 = new Child("child2",19); 
console.log(child2.name); // child2
console.log(child2.age); // 19
console.log(child2.play); // [1, 2, 3] */

猜你喜欢

转载自blog.csdn.net/m0_55049655/article/details/147063841
今日推荐