一文搞懂手写 new 函数以及绑定作用域的 bind、call、apply 方法
1.手写 new 函数
一般情况的 new:
function One(name, age) {
this.name = name;
this.age = age;
}
let a = new One("XiaoMing", "18");
console.log(a);
手写 new,分四步骤直接贴代码:
理解:手写 new 函数就是把构造函数和属性作为参数进行处理,生成一个对象的过程!
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.bind
、apply
和 call
三者的对比
bind
、apply
和 call
都是 JavaScript 中用于管理函数上下文(作用域)和参数的方法,但它们在使用和功能上有一些不同。以下是它们的对比:
bind:
作用:
bind
用于创建一个新的函数,将指定的上下文绑定到该函数,并可以预先指定一些参数。返回值:返回一个新的函数,不会立即执行函数。
示例:
jsfunction 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.
call:
作用:
call
用于在指定的上下文中立即执行函数,并传递参数列表。返回值:立即执行函数,并返回函数的返回值。
示例:
jsfunction greet(name) { console.log(`Hello, ${name}! I'm ${this.title}.`); } const person = { title: "Mr." }; greet.call(person, "John"); // 输出:Hello, John! I'm Mr.
apply:
作用:
apply
用于在指定的上下文中立即执行函数,并传递参数数组。返回值:立即执行函数,并返回函数的返回值。
示例:
jsfunction greet(name) { console.log(`Hello, ${name}! I'm ${this.title}.`); } const person = { title: "Mr." }; greet.apply(person, ["Jane"]); // 输出:Hello, Jane! I'm Mr.
主要区别:
bind
创建一个新函数,不执行函数,而call
和apply
立即执行函数。bind
可以预先绑定上下文和参数后面再调用,而call
和apply
必须在调用时指定。call
传递参数列表(,分隔),但是apply
传递参数数组,这也是二者唯一的区别
选择使用哪种方法取决于你的需求。如果你需要在未来某个时刻使用带有特定上下文和参数的函数,那么使用 bind
。如果你想要立即执行一个函数并传递参数列表,那么使用 call
。如果你想要立即执行一个函数并传递参数数组,那么使用 apply
。
从 ES6 开始,引入了展开操作符 ...
,它使得使用 apply
的场景相对减少,因为你可以使用展开操作符将数组展开为参数列表。
例如:
greet.call(person, ...["Alice"]);
// 或者
greet.apply(person, ["Bob"]);
个人理解:
bind 方法是一类的,而 call 和 apply 方法是另一类的
bind 方法是绑定函数执行上下文对象以及参数(传递参数列表),并返回一个新的函数
call 和 apply 方法是绑定函数执行上下文对象以及参数(call 是传递参数列表,apply 是传递参数数组),并立即执行函数,并返回函数的返回值
注意:在 JavaScript 中,this
不是函数的作用域,而是一个关键字,用于引用当前执行上下文中的对象
而作用域(Scope)是指在代码中定义变量时,这些变量的可访问性和生命周期的规则。
一定要改正自己的说法,不要对函数说作用域,而是说执行上下文对象!