在这篇文章中,我将用简单的例子解释如何在JavaScript中使用调用、应用和绑定。
我们还将实现一个例子,展示你如何用apply函数创建你自己的地图函数。
不多说了,让我们开始吧:
目录
前提条件
以下是你应该了解的一些情况,以便从本文中获得最大的收获:
定义
让我们再仔细看看我们在这里要学习的函数,了解它们的作用。
调用是一个帮助你改变调用函数的上下文的函数。通俗地说,它可以帮助你用任何你想要的值来替换函数中this 的值。
应用与call 函数非常相似。唯一不同的是,在apply ,你可以传递一个数组作为参数列表。
绑定是一个帮助你创建另一个函数的函数,你可以在以后用提供的this 的新上下文执行这个函数。
现在我们将看看调用、应用和绑定函数的一些基本例子。然后我们将看一个例子,我们将构造我们自己的函数,类似于map函数。
如何在JavaScript中使用调用函数
call 是一个函数,你用它来改变函数内部 的值,并且用提供的参数来执行它。this
下面是call 函数的语法:
func.call(thisObj, args1, args2, ...)
其中:
- func是一个需要用不同的
this对象来调用的函数 - thisObj是一个对象或一个值,需要用函数中的
this关键字来替换。func - args1, args2是参数,用改变后的
this对象传递给调用的函数。
注意,如果你调用一个没有任何thisObj 参数的函数,那么JavaScript认为这个属性是一个全局对象。
现在我们有了一些关于什么是call 函数的上下文,让我们首先通过一些例子来更详细地了解它。
如何在JS中用不同的上下文调用一个函数
考虑一下下面的例子。它由3个类组成 -Car,Brand1, 和Brand2 :
function Car(type, fuelType){
this.type = type;
this.fuelType = fuelType;
}
function setBrand(brand){
Car.call(this, "convertible", "petrol");
this.brand = brand;
console.log(`Car details = `, this);
}
function definePrice(price){
Car.call(this, "convertible", "diesel");
this.price = price;
console.log(`Car details = `, this);
}
const newBrand = new setBrand('Brand1');
const newCarPrice = new definePrice(100000);
如果你仔细观察,你可以看到我们在两个场合使用call 函数来调用Car 函数。首先是在setBrand ,然后是在definePrice 函数中。
在这两个函数中,我们用代表各自函数本身的this 对象来调用Car 函数。例如,在setBrand ,我们用属于其上下文的this 对象调用Car 函数。definePrice 的情况也是如此。
如何在JS中调用一个没有参数的函数
请看下面的例子:
const newEntity = (obj) => console.log(obj);
function mountEntity(){
this.entity = newEntity;
console.log(`Entity ${this.entity} is mounted on ${this}`);
}
mountEntity.call();
在这个例子中,我们调用了没有thisObj 参数的函数mountEntity 。在这种情况下,JavaScript指的是全局对象。
如何在JavaScript中使用Apply函数
Apply 函数与Call 函数非常相似。call 和apply 之间的唯一区别是参数传递方式的不同。
在apply ,参数可以以数组字面形式或新的数组对象形式传递。
下面是apply 函数的语法:
func.apply(thisObj, argumentsArray);
其中:
- func是一个需要用不同的
this对象来调用的函数 - thisObj是一个对象或一个值,需要用函数中的
this关键字来替换。func - argumentsArray可以是一个参数数组,数组对象,或者arguments关键字本身。
正如你在上面看到的,apply 函数有不同类型的语法。
第一种语法是简单的语法。你可以像下面这样传入一个参数数组:
func.apply(thisObj, [args1, args2, ...]);
第二种语法是,我们可以向它传递新的数组对象:
func.apply(thisObj, new Array(args1, args2));
第三种语法是我们可以传入arguments关键字的地方:
func.apply(thisObj, arguments);
arguments是一个在函数中可用的特殊对象。它包含传递给函数的参数值。你可以在apply 函数中使用这个关键字来接受任何数量的任意参数。
关于apply ,最好的部分是我们不需要照顾传递给调用函数的参数数量。由于它的动态和多功能性,你可以在复杂的情况下使用它。
让我们看看与上面相同的例子,但这一次我们将使用apply 函数:
function Car(type, fuelType){
this.type = type;
this.fuelType = fuelType;
}
function setBrand(brand){
Car.apply(this, ["convertible", "petrol"]); //Syntax with array literal
this.brand = brand;
console.log(`Car details = `, this);
}
function definePrice(price){
Car.apply(this, new Array("convertible", "diesel")); //Syntax with array object construction
this.price = price;
console.log(`Car details = `, this);
}
const newBrand = new setBrand('Brand1');
const newCarPrice = new definePrice(100000);
而这里有一个例子,展示了你如何使用arguments 这个关键词:
function addUp(){
//Using arguments to capture the arbitrary number of inputs
const args = Array.from(arguments);
this.x = args.reduce((prev, curr) => prev + curr, 0);
console.log("this.x = ", this.x);
}
function driverFunc(){
const obj = {
inps: [1,2,3,4,5,6]
}
addUp.apply(obj, obj.inps);
}
driverFunc();
如何在JavaScript中使用Bind函数
bind 函数创建了一个函数的副本,并将新的值传递给存在于调用函数内部的this 。
下面是bind 函数的语法:
func.bind(thisObj, arg1, arg2, ..., argN);
其中:
- func是一个需要用不同的
this对象来调用的函数 - thisObj是一个对象或一个值,需要用函数中的
this关键字来替换。func - arg1, arg2...argN- 你可以向调用的函数传递1个参数,也可以传递更多的参数,类似于
call函数。
然后,bind 函数返回一个新的函数,该函数由一个新的上下文组成,该上下文存在于调用函数内的this 变量。
func(arg1, arg2);
现在这个函数func ,以后可以用参数执行。
让我们看看一个经典的例子,如何在基于类的React组件的帮助下使用bind 函数:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
counter: 1
};
}
handleCode() {
console.log("HANDLE CODE THIS = ", this.state);
}
render() {
return <button onClick={this.handleCode}>Click Me</button>;
}
}
考虑一下上面的App组件,它构成了以下内容:
constructor是一个被称为类的函数,并通过 关键字进行实例化。newrender是一个执行/渲染JSX代码的函数。handleCode是一个类方法,记录组件的状态。
如果我们点击Click Me 按钮,那么我们将收到一个错误,说明:Cannot read properties of undefined (reading 'state') 。
你有没有想过为什么会出现这个问题?🤔🤔
你可能期望我们应该能够访问类的状态,因为handleCode 是一个类方法。但这里有一个问题。
thishandleCode里面的状态与类的 里面的状态不一样。this- 在一个类里面,
this是一个普通的对象,它的属性为非静态的类方法。但是在this里面的handleCode将指的是一个不同的上下文。 - 说实话,在这种情况下,
this的价值取决于从哪里调用这些函数。如果你看到,handleCode是在onClick事件中被调用的。 - 但在这个阶段,我们将得到
undefined,因为this的上下文存在于handleCode函数中。 - 我们正试图调用一个未定义值的
state属性。因此,这导致了上述错误。
我们可以通过在handleCode 方法中提供正确的this 上下文来解决这个问题。你可以通过bind 方法来做到这一点:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
counter: 1
};
this.handleCode = this.handleCode.bind(this); //bind this function
}
handleCode() {
console.log("HANDLE CODE THIS = ", this.state);
}
render() {
return <button onClick={this.handleCode}>Click Me</button>;
}
}
bind 将创建一个新的函数,并将其存储在this 对象内,并有一个新的属性作为handleCode 。Bind 将确保类的this 上下文被应用到handleCode 函数内的this 。
如何创建你自己的map 函数
现在我们有了所有必要的东西,让我们开始创建我们的own 地图函数。首先让我们了解一下建立我们的own 地图函数所需要的东西。
下面是map 函数的语法:
arr.map(func)
其中:
- arr是一个数组,在这个数组上调用map。
- func是需要在数组的每个元素上运行的函数。
map 函数的基本功能很简单。
它是一个遍历数组中每个元素的函数,并应用作为参数传递的函数。map的返回类型也是一个数组,func ,并应用于每个元素。
现在我们理解了这些要求,所以我们可以继续创建我们自己的map 函数。下面是我们的新函数map 的代码:
function newMap(func){
let destArr = [];
const srcArrLen = this.length;
for(let i = 0; i < srcArrLen; i++){
destArr.push(func.call(this, this[i]));
}
return destArr;
}
让我们一点一点地理解上述函数:
- 这个函数接受了一个名为
func的参数。它只不过是一个需要在数组的每个元素上调用的函数。 - 代码的其他部分是很好解释的。我们将重点关注下面这一行。
destArr.push(func.call(this, this[i])); - 这一行做了两件事:
1.将变化推送到destArr
2.在call方法的帮助下执行func。在这里,call方法(如前几节所解释的)将执行func方法,给存在于func方法中的this对象一个新值。
现在让我们来看看我们将如何执行我们的newMap 函数。下面这种为现有的原始数据类型添加新方法的方法是不推荐的,但为了本文的目的,我们还是要这样做。
注意:不要在你的生产代码中遵循下面的方法。这可能会对现有的代码造成损害。
Object.defineProperty(Array.prototype, 'newMap', {
value: newMap
});
defineProperty 我们在 里面创建一个新的属性。Array.prototype
一旦这样做了,我们就可以在数组上执行我们新的map函数了:
const arr = [1,2,3];
const newArr = arr.newMap(item => item + 1);
console.log(newArr);
总结
这篇文章通过例子向你展示了调用、应用和绑定函数的作用。
因此,我们来简单地谈谈这些函数:
- 调用、应用和绑定是帮助你改变调用函数中的
this关键字的上下文的函数。 - 我们看到每个函数可以用不同的方式调用--例如,用
apply,你可以执行一个带有参数数组的函数,而用call,你可以执行同样的函数,但参数通过逗号分散。 - 这些函数在React的基于类的组件中真的很有用。
谢谢你的阅读!