掌握 call、apply、bind 和 new:提升你的 JavaScript 编程技巧
2023-09-05 02:44:36
对于 JavaScript 开发者来说,了解函数调用方式是必备的基础知识。在日常开发和面试中,经常会遇到关于 call、apply 和 bind 使用区别的问题。此外,new 关键词是如何实例化一个对象的,它的底层又是怎么实现的,这些知识同样重要。在这篇文章中,我们将详细探讨 new、call、apply 和 bind 的用法和底层实现,帮助你提升 JavaScript 编程技巧。
1. call、apply、bind 的用法和区别
在 JavaScript 中,函数调用有四种主要方式:直接调用、使用 call 方法、使用 apply 方法和使用 bind 方法。
1.1 直接调用
直接调用是最简单的一种函数调用方式,直接在函数名后跟上参数即可。例如:
function sum(a, b) {
return a + b;
}
console.log(sum(1, 2)); // 输出:3
1.2 使用 call 方法
call 方法可以显式地指定函数的调用者,即函数执行时 this 指向的对象。call 方法的语法如下:
functionName.call(thisArg, arg1, arg2, ..., argN)
其中,thisArg 是指定函数调用者的对象,arg1、arg2、...、argN 是要传递给函数的参数。例如:
function sum(a, b) {
return a + b;
}
const obj = {
a: 1,
b: 2
};
console.log(sum.call(obj)); // 输出:3
在上面的例子中,我们使用 call 方法指定函数 sum 的调用者为 obj 对象,因此 this 指向 obj 对象,函数 sum 中的 a 和 b 分别取值为 obj 对象的 a 和 b 属性的值。
1.3 使用 apply 方法
apply 方法与 call 方法类似,也可以显式地指定函数的调用者。但是,apply 方法的参数列表与 call 方法不同。apply 方法的语法如下:
functionName.apply(thisArg, [args])
其中,thisArg 是指定函数调用者的对象,args 是一个包含要传递给函数的参数的数组。例如:
function sum(a, b) {
return a + b;
}
const obj = {
a: 1,
b: 2
};
const args = [1, 2];
console.log(sum.apply(obj, args)); // 输出:3
在上面的例子中,我们使用 apply 方法指定函数 sum 的调用者为 obj 对象,args 数组包含了要传递给函数 sum 的参数。
1.4 使用 bind 方法
bind 方法可以创建函数的新副本,并显式地指定函数的调用者。bind 方法的语法如下:
functionName.bind(thisArg, arg1, arg2, ..., argN)
其中,thisArg 是指定函数调用者的对象,arg1、arg2、...、argN 是要传递给函数的新副本的参数。bind 方法返回一个新的函数,这个新函数的调用者已经固定为 thisArg,并且初始参数也已经绑定。例如:
function sum(a, b) {
return a + b;
}
const obj = {
a: 1,
b: 2
};
const boundSum = sum.bind(obj);
console.log(boundSum()); // 输出:3
在上面的例子中,我们使用 bind 方法创建了函数 sum 的新副本 boundSum,并指定函数 boundSum 的调用者为 obj 对象。当我们调用 boundSum 函数时,this 指向 obj 对象,函数 boundSum 中的 a 和 b 分别取值为 obj 对象的 a 和 b 属性的值。
1.5 区别
call、apply 和 bind 的主要区别在于参数的传递方式。call 和 apply 方法需要显式地指定参数列表,而 bind 方法只需要指定 thisArg 对象,参数列表可以稍后传递。此外,bind 方法返回一个新的函数,而 call 和 apply 方法不返回任何值。
2. new 关键词的用法和底层实现
new 关键词用于实例化一个对象,即创建一个新对象并将其链接到原型对象。new 关键词的语法如下:
new Constructor([args])
其中,Constructor 是要实例化的对象的构造函数,args 是要传递给构造函数的参数列表。例如:
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
const person = new Person('John Doe', 30);
console.log(person.name); // 输出:John Doe
console.log(person.age); // 输出:30
在上面的例子中,我们使用 new 关键词实例化了 Person 类,并传递了两个参数:name 和 age。构造函数 Person 的代码在创建对象时会被执行,this 指向新创建的对象,因此 this.name 和 this.age 分别被赋值为 'John Doe' 和 30。
2.1 new 关键词的底层实现
在 JavaScript 中,new 关键词的底层实现是一个内置函数,它的具体实现如下:
function _new(constructor, ...args) {
// 1. 创建一个新的空对象
const obj = {};
// 2. 将对象的原型设置为构造函数的原型对象
obj.__proto__ = constructor.prototype;
// 3. 调用构造函数,并将新对象作为 this 对象传入
const result = constructor.call(obj, ...args);
// 4. 如果构造函数返回一个对象,则返回该对象,否则返回新对象
return result instanceof Object ? result : obj;
}
从上面的实现可以看出,new 关键词主要做了以下四件事:
- 创建一个新的空对象。
- 将对象的原型设置为构造函数的原型对象。
- 调用构造函数,并将新对象作为 this 对象传入。
- 如果构造函数返回一个对象,则返回该对象,否则返回新对象。
3. 总结
在本文中,我们详细探讨了 JavaScript 中的 call、apply、bind 和 new 的用法和底层实现。通过学习这些知识,可以帮助我们更好地理解 JavaScript 的函数调用机制和对象创建机制,从而提升我们的编程技巧。