Skip to content

一文搞懂手写 new 函数以及绑定作用域的 bind、call、apply 方法

1.手写 new 函数

一般情况的 new:

js
function One(name, age) {
  this.name = name;
  this.age = age;
}
let a = new One("XiaoMing", "18");
console.log(a);

手写 new,分四步骤直接贴代码:

理解:手写 new 函数就是把构造函数和属性作为参数进行处理,生成一个对象的过程!

js
function One(name, age) {
  //构造函数
  this.name = name;
  this.age = age;
}
//Fun为构造函数, args表示传参
function myNew(Fun, ...args) {
  //...args获取剩余的参数
  // 1.在内存中创建一个新对象
  let obj = {}; //作为new出来的对象

  // 2.把新对象的原型指针__proto__指向构造函数的原型属性prototype(函数既有__proto__又有prototype)
  obj.__proto__ = Fun.prototype;
  //还可以写为:const obj = Object.create(Fun.prototype); //这种写法更加高级,但是注意是重新创建一个新的空对象了!
  //还可以写为 Object.setPrototypeOf(obj, Fun.prototype); //这是最好的写法,不会创建一个新的对象!而是基于原来的对象!

  // 3.改变this指向,并且执行构造函数内部的代码(传参),注意是Fun.apply()相当于new了!
  let res = Fun.apply(obj, args); //函数.apply()就相当于new(作为它的核心),传入一个空对象obj指定作用域,再传入参数args,最终通过构造函数里面的逻辑得到一个结果对象!
  //注意:apply和call是类似的,都是指定作用域并立即执行函数!
  //apply会自动把数组展开变成一个个元素传进去的

  // 4.判断函数执行结果的类型
  if (res instanceof Object) {
    //如果是Object类型的
    return res;
  } else {
    return obj;
  }
}
//本来是:let obj = new One("XiaoMing", "18");
let obj = myNew(One, "XiaoMing", "18"); //传入一个构造函数,和属性值
console.log("newObj:", obj);

参数为一个构造函数和需要的属性,主要做了两件事情:

1.用构造函数去给空对象赋值(必须用 apply 绑定作用域为{})

2.指定对象的隐式原型指向构造函数的原型对象

2.bindapplycall三者的对比

bindapplycall 都是 JavaScript 中用于管理函数上下文(作用域)和参数的方法,但它们在使用和功能上有一些不同。以下是它们的对比:

  1. bind

    • 作用:bind 用于创建一个新的函数,将指定的上下文绑定到该函数,并可以预先指定一些参数。

    • 返回值:返回一个新的函数,不会立即执行函数。

    • 示例:

      js
      function greet(name) {
        console.log(`Hello, ${name}! I'm ${this.title}.`);
      }
      
      const person = { title: "Mr." };
      const boundGreet = greet.bind(person, "Alice");
      boundGreet(); // 输出:Hello, Alice! I'm Mr.
  2. call

    • 作用:call 用于在指定的上下文中立即执行函数,并传递参数列表。

    • 返回值:立即执行函数,并返回函数的返回值

    • 示例:

      js
      function greet(name) {
        console.log(`Hello, ${name}! I'm ${this.title}.`);
      }
      
      const person = { title: "Mr." };
      greet.call(person, "John"); // 输出:Hello, John! I'm Mr.
  3. apply

    • 作用:apply 用于在指定的上下文中立即执行函数,并传递参数数组。

    • 返回值:立即执行函数,并返回函数的返回值

    • 示例:

      js
      function greet(name) {
        console.log(`Hello, ${name}! I'm ${this.title}.`);
      }
      
      const person = { title: "Mr." };
      greet.apply(person, ["Jane"]); // 输出:Hello, Jane! I'm Mr.

主要区别:

  • bind 创建一个新函数,不执行函数,而 callapply 立即执行函数。
  • bind 可以预先绑定上下文和参数后面再调用,而 callapply 必须在调用时指定。
  • call 传递参数列表(,分隔),但是 apply 传递参数数组,这也是二者唯一的区别

选择使用哪种方法取决于你的需求。如果你需要在未来某个时刻使用带有特定上下文和参数的函数,那么使用 bind。如果你想要立即执行一个函数并传递参数列表,那么使用 call。如果你想要立即执行一个函数并传递参数数组,那么使用 apply

从 ES6 开始,引入了展开操作符 ...,它使得使用 apply 的场景相对减少,因为你可以使用展开操作符将数组展开为参数列表

例如:

js
greet.call(person, ...["Alice"]);
// 或者
greet.apply(person, ["Bob"]);

个人理解:

bind 方法是一类的,而 call 和 apply 方法是另一类的

bind 方法是绑定函数执行上下文对象以及参数(传递参数列表),并返回一个新的函数

call 和 apply 方法是绑定函数执行上下文对象以及参数(call 是传递参数列表,apply 是传递参数数组),并立即执行函数,并返回函数的返回值

注意:在 JavaScript 中,this 不是函数的作用域,而是一个关键字,用于引用当前执行上下文中的对象

而作用域(Scope)是指在代码中定义变量时,这些变量的可访问性和生命周期的规则。

一定要改正自己的说法,不要对函数说作用域,而是说执行上下文对象!