JavaScript的这个难点,毁掉了多少程序员




本文将使用简单的术语和实际示例来解释这个及其在JavaScript中的用法,并告诉您为什么编写好的代码很重要。

这是给你的吗?

我已经阅读了很多关于JavaScript的文章,假设您已经学习了面向对象的编程语言,如Java,C或Python。但是这篇文章适合那些不知道它是什么的读者。我尽量不使用任何术语来解释它是什么以及如何使用它。

也许你一直害怕解开这个秘密,因为它看起来很奇怪和可怕。也许你只在StackOverflow说你需要使用它时才使用它(比如在React中实现一个函数)。

在深入研究这个过程之前,我们首先需要了解函数式编程和面向对象编程之间的区别。

2,函数式编程和面向对象编程

您可能不知道JavaScript具有面向对象和功能结构,因此您可以选择要使用的样式,或两者兼而有之。

很久以前我在使用JavaScript时使用了函数式编程。我避免了面向对象的编程,就像我避免瘟疫一样,因为我不理解面向对象中的关键字,就像这个。我不知道为什么我用这个。没有它,我似乎能够做所有的工作。

我是对的。

从某种意义上说。也许你只专注于一个结构并完全忽略另一个结构,但你只能是一个JavaScript开发人员。为了解释功能和面向对象之间的区别,让我们以Facebook好友列表数组为例。

假设您想成为Web应用程序。当用户使用Facebook登录您的Web应用程序时,他们需要显示他们的Facebook好友信息。您需要访问Facebook并获取用户的朋友数据。数据可以是诸如firstName,lastName,username,numFriends,friendData,birthday和lastTenPosts之类的信息。假设上述数据是通过Facebook API获得的。现在,您需要将其转换为易于用于项目的格式。假设您要显示的好友信息如下:3,功能模式函数方法是将整个数组或数组中的元素传递给函数,然后返回所需的信息:

首先,我们有Facebook API返回的原始数据。要将其转换为所需的格式,首先将数据传递给一个函数,该函数的输出是(或包含)可以在应用程序中呈现给用户的已修改数据。

我们可以以类似的方式获得三篇随机文章,并计算朋友生日的天数。

功能方法是将原始数据传递给函数或函数以获取对项目有用的数据格式。

4,面向对象的方式

对于编程初学者和JavaScript初学者,面向对象的概念可能有点难以理解。我们的想法是,我们希望将每个朋友变成一个可以生成开发人员所需的一切的对象。

您可以创建一个与具有fullName属性的朋友对应的对象,以及两个函数getThreeRandomPosts和getDaysUntilBirthday。

面向对象的方法是为数据创建对象,每个对象都有自己的状态,并包含生成所需数据的必要信息。

5.这与此有什么关系?

您可能永远不会考虑编写上面的initializeSecurity代码,您可能认为这些代码可能很有用。但是你也注意到这不是面向对象的。

原因是上面例子中getThreeRandomPosts或getdaysUntilBirtyday的原因实际上是一个闭包。因为使用了闭包,所以在initializeFriend返回后它们仍然可以访问数据。有关关闭包的更多信息,请查看本文:范围和闭包(我还应该做什么?我们假设此方法称为问候语。请注意,方法(与JavaScript对象相关的方法)实际上只是一个属性。属性值是一个函数。我们想在问候语中实现以下功能:

这可以正常工作吗?

没有!

我们新创建的对象可以访问initializeFriend中的所有变量,但无法访问对象本身的属性或方法。当然你会问,我不能直接在问候语中使用data.firstName和data.lastName吗?当然可以。但是,如果您想在朋友的问候中为朋友的生日添加几天呢?我们最好有办法在问候语中调用getDaysUntilBirthday。

这是转向的方式!

6,最后——是什么?

这可以指不同环境中的不同事物。在默认的全局环境中,这指的是一个全局对象(这是浏览器中的一个窗口对象),这几乎是无用的。这在以下规则中很有用:

如果在对象的方法中使用它并在对象的上下文中调用它,这将引用对象本身。

你会说“在对象的上下文中调用”......你的意思是什么?

别担心,我们稍后再说。

所以,如果我们想从问候中调用getDaysUntilBirtyday,我们只需要编写this.getDaysUntilBirthday,因为这是对象本身。

注意:请勿在全局范围内的普通函数或其他函数范围内使用它!这是一个面向对象的东西,只在对象(或类的上下文)的上下文中有意义。

我们用它来覆盖initializeFriend:function initializeFriend(data){

返回{

lastTenPosts: data.lastTenPosts,

生日: data.birthday,

fullName:` $ {data.firstName} $ {data.lastName}`,

getThreeRandomPosts: function {

//从this.lastTenPosts中获取三篇随机帖子

},

getDaysUntilBirthday: function {//使用this.birthday获取生日前的num天数

},

问候:函数{Const numDays=this.getDaysUntilBirthday

返回`你好,这是$ {this.fullName}的数据!直到$ {this.fullName}的生日是$ {numDays}!

}

};

}

现在,在执行initializeFriend之后,对象所需的所有内容都在对象本身的范围内。

内。我们的方法不再需要依赖闭包,它们只使用对象本身包含的信息。

好吧,这是用法之一,但你说这在不同的背景下有不同的含义。那是什么意思?为什么不一定指向对象本身?

有时你需要指出具体的东西。一个案例是事件处理程序。例如,当用户点击朋友时,我们想要打开朋友的Facebook页面。我们将向对象添加以下内容onClick方法:

函数initializeFriend(data){

返回{

lastTenPosts: data.lastTenPosts,

生日: data.birthday,

用户名: data.username,

fullName:` $ {data.firstName} $ {data.lastName}`,

getThreeRandomPosts: function {

//从this.lastTenPosts中获取三篇随机帖子

},

getDaysUntilBirthday: function {

//使用this.birthday获取生日前的num天数

},

问候:函数{

Const numDays=this.getDaysUntilBirthday返回`Hello,这是$ {this.fullName}的数据!直到$ {this.fullName}的生日是$ {numDays}!},

onFriendClick: function {

Window.open(

}

};

}

请注意,我们将username属性添加到对象中,以便onFriendClick可以访问它并在新窗口中打开朋友的Facebook页面。现在你只需要编写HTML:和JavaScript:

在上面的代码中,我们为Bob Ross创建了一个对象。然后我们为Bob Ross获得了DOM元素。然后执行onFriendClick方法打开Bob的Facebook页面。似乎没有问题,对吧?

有问题!

哪里出错了?

请注意,我们称之为onclick处理程序的代码是bobRossObj.onFriendClick。你见过这个问题吗?如果你这样写,你能看到吗?

你现在看到问题了吗?如果事件处理程序被写为bobRossObj.onFriendClick,则保存在bobRossObj.onFriendClick上的函数实际上作为参数传递。它不再“附加”到bobRossObj,也就是说,它不再指向bobRossObj。它实际上指向全局对象,这意味着this.username不存在。似乎我们无事可做。

这是绑定的扭曲!

7,显然限制了这一点

我们需要将它显式绑定到bobRossObj。我们可以用bind来做到这一点:

以前,这是根据默认规则设置的。但是在使用bind之后,我们在bobRossObj.onFriendClick中将this的值显式设置为bobRossObj对象本身。

到目前为止,我们已经看到了为什么使用它以及为什么它被明确约束。最后,让我们来介绍一下这实际上是一个箭头函数。 8,箭头功能

您可能已经注意到箭头功能最近很受欢迎。人们喜欢箭头功能,因为它非常简单和优雅。而且你也知道箭头功能与正常功能略有不同,虽然不清楚具体的区别是什么。简而言之,两者之间的区别在于,在定义箭头函数时,无论你指向谁,箭头函数总是指向同一个东西。

嗯......这似乎没用......似乎表现得像一个正常的功能?

我们使用initializeFriend来说明这一点。假设我们要添加一个名为greeting的函数:

这可以运行吗?如果没有,我该如何修改它才能运行?

答案是不。由于未在对象的上下文中调用getLastPost,因此getLastPost中的getLastPost根据默认规则指向全局对象。

你说没有“在对象的上下文中调用”......是从initializeFriend调用的吗?如果这不称为“在对象的上下文中调用”,那么我不知道要计算什么。

我知道“在一个对象的上下文中调用”这个词是模棱两可的。也许确定函数是否“在对象的上下文中调用”的好方法是检查函数的调用过程以查看对象是否“附加”到函数。

让我们看看执行bobRossObj.onFriendClick时会发生什么。 “给我对象bobRossObj,找到onFriendClick并调用与属性对应的函数。”

我们还在执行getLastPost时检查情况。 “给我一个名为getLastPost的函数并执行它。”看到?我们根本没有提到这个对象。

好的,这是测试你的理解的问题。假设有一个名为functionCaller的函数,其函数是调用函数:

如果你调用functionCaller(bobRossObj.onFriendClick)怎么办?你认为onFriendClick是“在对象的上下文中调用的”吗?是否定义了this.username?让我们来看看:“给我bobRosObj对象并查看其属性onFriendClick。获取值(此值恰好是一个函数),然后将其传递给functionCaller,将其命名为fn。然后,执行名为fn的函数。”请注意,此函数在调用之前已与bobRossObj对象“分离”,因此不会在“对象的上下文”中调用它,因此未定义this.username。此时,您可以使用箭头功能来解决此问题:上述代码的规则是:

箭头功能在问候语中定义。我们知道问候语中的内容指向对象本身。因此,箭头函数也指向对象本身,这正是我们所需要的。

9.结论

这有时很难理解,但对于开发JavaScript应用程序非常有用。本文当然不包括这方面的所有方面。一些未被发现的主题包括:

我建议您首先在这些情况下问自己,然后在浏览器中执行代码以验证结果。

编辑:钱锋HTML5

本文档被授权从公共号码CSDN转移(ID:CSDNnews)

作者:软件工程师Austin Tackaberry,Human API,翻译:meniscus,编辑:Tu Min









时间:2019-02-09 13:20:05 来源:诺亚彩票 作者:匿名