JavaScript中的方法是什么

做者:Shadeed
译者:前端小智
来源:dmitripavlutin

有梦想,有干货,微信搜索 【大迁世界】 关注这个在凌晨还在刷碗的刷碗智。javascript

本文 GitHub https://github.com/qq449245884/xiaozhi 已收录,有一线大厂面试完整考点、资料以及个人系列文章。前端

1.什么是方法

定义并调用一个常规函数:java

function greet(who) {
  return `Hello, ${who}!`;
}

greet('World'); // => 'Hello, World!'

function关键字后跟其名称,参数和主体:function greet(who){...}进行常规的函数定义。git

greet('World')是常规的函数调用。函数greet('World')接受参数中的数据。github

若是who是一个对象的属性呢?要方便访问对象的属性,咱们能够将函数附加到该对象,换句话说,就是建立一个方法。面试

咱们将greet()做为对象world的一种方法:windows

const world = {
  who: 'World',

 greet() { return `Hello, ${this.who}!`; }}

world.greet(); // => 'Hello, World!'

greet() { ... }如今是属于world对象的方法, world.greet()是方法调用。浏览器

greet()方法内部,this指向该方法所属的对象—world,这就是为啥能够this.who访问 word属性的缘由。微信

注意,this也称为上下文。app

上下文是可选的

在上一个示例中,咱们使用this来访问该方法所属的对象,可是 JS 没有强制让方法使用 this

所以,能够将对象用做方法的命名空间:

const namespace = {
  greet(who) {
    return `Hello, ${who}!`;
  },

  farewell(who) {
    return `Good bye, ${who}!`;
  }
}

namespace.greet('World');    // => 'Hello, World!'
namespace.farewell('World'); // => 'Good bye, World!'

namespace是一个包含2个方法的对象:namespace.greet()namespace.farewell()

2. 对象字面量方法

如前所述,咱们能够直接在对象字面量中定义方法

const world = {
  who: 'World',

 greet() { return `Hello, ${this.who}!`; }};

world.greet(); // => 'Hello, World!'

greet() { .... }是在对象定义的方法,这种定义类型称为速记方法定义(从ES2015开始可用)。方法定义的语法也更长:

const world = {
  who: 'World',
  greet: function() { 
    return `Hello, ${this.who}!`; 
  }
}

world.greet(); // => 'Hello, World!'

greet: function() { ... }是一个方法定义,注意附加的冒号和function关键字。

动态添加方法

方法只是一个函数,它做为属性存储在对象上。 所以,咱们能够向对象动态添加方法:

const world = {
  who: 'World',

  greet() {
    return `Hello, ${this.who}!`;
  }
};

// A a new property holding a function
world.farewell = function () {
  return `Good bye, ${this.who}!`;
}

world.farewell(); // => 'Good bye, World!'

3.类方法

在 JavaScript 中,类别语法定义了一个类别,该类别将用做其实例的模板。

类也能够有方法:

class Greeter {
  constructor(who) {
    this.who = who;
  }

 greet() { console.log(this === myGreeter); // logs true return `Hello, ${this.who}!`; }}

const myGreeter = new Greeter('World');
myGreeter.greet(); // => 'Hello, World!'

greet() { ... }是在类内部定义的方法。

每次咱们使用new操做符(例如myGreeter = new Greeter('World'))建立一个类的实例时,均可以在建立的实例上调用方法。

myGreeter.greet()是如何在实例上调用方法greet()的方法。 重要的是方法内部的this等于实例自己:this等于greet() { ... }方法内部的 myGreeter

4.如何调用方法

4.1方法调用

JavaScript 特别有趣的是,在对象或类上定义方法只能算完成工做的一半。为了维护方法的上下文,咱们必须确保将方法做为方法调用。

咱们来看看为何它很重要。

回忆一下有greet()方法的world对象。咱们测试一下greet()做为一个方法和一个常规函数调用时,this值是什么:

const world = {
  who: 'World',

  greet() {
 console.log(this === world);    return `Hello, ${this.who}!`;
  }
};

// 方法调用
world.greet(); // logs true
const greetFunc = world.greet;
// 常规函数调用
greetFunc(); // => logs false

world.greet()是一个方法调用。对象world,后面是一个点.,最后是使方法调用的方法自己。

greetFuncworld.greet是同一个函数。但看成为常规函数greetFunc()调用时,这个在greet()中的并不等于world对象,而是全局对象(在浏览器中是window)

咱们将诸如greetFunc = world.greet之类的表达式命名为将方法与其对象分离的方法。 调用分离的方法greetFunc()时,this等于全局对象。

将方法与其对象分离能够采用不一样的形式:

// 方法分离, this 丢失了!
const myMethodFunc = myObject.myMethod;

// 方法分离, this 丢失了!
setTimeout(myObject.myMethod, 1000);

// 方法分离, this 丢失了!
myButton.addEventListener('click', myObject.myMethod)

// 方法分离, this 丢失了!
<button onClick={myObject.myMethod}>My React Button</button>

为了不丢失方法的上下文,请确保使用方法调用world.greet()或手动将方法绑定到对象greetFunc = world.greet.bind(this)

4.2间接函数调用

如上一节所述,常规函数调用已将this解析为全局对象。 常规函数是否能够经过方法自定义 this值?

欢迎使用如下间接函数调用:

myFunc.call(thisArg, arg1, arg2, ..., argN);
myFunc.apply(thisArg, [arg1, arg2, ..., argN]);

函数对象上可用的方法。

myFunc.call(thisArg)myFunc.apply(thisArg) 的第一个参数是间接调用的上下文(this值)。 换句话说,咱们能够手动指定函数内部 this 的值。

例如,让咱们将greet()定义为一个常规函数,以及一个具有who属性的对象alien:

function greet() {
  return `Hello, ${this.who}!`;
}

const aliens = {
  who: 'Aliens'
};

greet.call(aliens); // => 'Hello, Aliens!'
greet.apply(aliens); // => 'Hello, Aliens!'

greet.call(aliens)greet.apply(aliens)都是间接的方法调用。这个在greet()函数中的值等于aliens对象。

4.3 绑定函数调用

最后,还有一种在对象上使函数做为方法调用的第三种方法。 咱们能够将函数绑定为具备特定上下文。

可使用特殊方法建立绑定函数

const myBoundFunc = myFunc.bind(thisArg, arg1, arg2, ..., argN);

myFunc.bind(thisArg)的第一个参数是函数要绑定到的上下文。

例如,让咱们重用greet()并将其绑定到aliens上下文

function greet() {
  return `Hello, ${this.who}!`;
}

const aliens = {
  who: 'Aliens'
};

const greetAliens = greet.bind(aliens);

greetAliens(); // => 'Hello, Aliens!'

调用 greet.bind(aliens) 会建立一个新函数,该函数将 this 绑定到aliens对象。

一样,使用绑定函数能够模拟方法调用。当调用绑定函数greetAliens()时,this等于该函数中的 aliens

5. 箭头函数做为方法

不推荐使用箭头函数做为方法,缘由以下。

咱们将greet()方法定义为一个箭头函数:

const world = {
  who: 'World',

  greet: () => {
    return `Hello, ${this.who}!`;
  }
};

world.greet(); // => 'Hello, undefined!'

不幸的是,world.greet()返回'Hello, undefined!而不是咱们期待的'Hello, World!'

问题是箭头函数内部的this等于外部做用域的this。 可是,此时,咱们想要的thisworld对象。

上述箭头功能内部 this 等于全局对象:window'Hello, ${this.who}!' 结果是 Hello, ${windows.who}!,最后是 'Hello, undefined!'

我喜欢箭头功能, 可是它们不能用做方法。

6. 总结

该方法是一个属于对象的函数。方法的上下文(this)等于该方法所属的对象。

还能够在类上定义方法。这个类的方法内部等于实例。 JS 特有的一点是,仅仅定义一个方法是不够的。咱们还须要确保使用方法调用。一般,方法调用具备如下语法

// Method invocation
myObject.myMethod('Arg 1', 'Arg 2');

有趣的是,在 JS 中,咱们能够定义一个常规函数,但不属于一个对象,而后做为一个任意对象的方法调用该函数。可使用间接函数调用或将函数绑定到特定上下文来实现这一点

// Indirect function invocation
myRegularFunc.call(myObject, 'Arg 1', 'Arg 2');
myRegularFunc.apply(myObject, 'Arg 1', 'Arg 2');

// Bound function
const myBoundFunc = myRegularFunc.bind(myObject);
myBoundFunc('Arg 1', 'Arg 2');

我是小智,我要去刷碗了,咱们下期见~


代码部署后可能存在的BUG无法实时知道,过后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给你们推荐一个好用的BUG监控工具 Fundebug

原文:https://dmitripavlutin.com/ja...

交流

有梦想,有干货,微信搜索 【大迁世界】 关注这个在凌晨还在刷碗的刷碗智。

本文 GitHub https://github.com/qq44924588... 已收录,有一线大厂面试完整考点、资料以及个人系列文章。

相关文章
相关标签/搜索