未加星标

React怎样从函数中辨别类

字体大小 | |
[前端(javascript) 所属分类 前端(javascript) | 发布者 店小二03 | 时间 2018 | 作者 红领巾 ] 0人收藏点击收藏

考虑用函数定义的组件 Greeting :

function Greeting() { return <p>Hello</p> } 复制代码

React也支持使用类定义它:

class Greeting extends React.Component { render() { return <p>Hello</p> } } 复制代码

(直到最近,那是唯一的方式使用state特性)

当你render一个 <Greeting /> 的时候,你不需要关心它是如何定义的。

// 类 或 函数 <Greeting /> 复制代码

但是React自己关心它们的不同!

如果 Greeting 是一个函数,React需要调用它:

// 你的代码 function Greeting() { return <p>Hello</p> } // React 内部 const result = Greeting(props); // <p>Hello</p> 复制代码

但是如果 Greeting 是一个类,React需要通过 new 操作符将它实例化,然后调用实例的 render 方法:

// 你的代码 class Greeting extends React.Component { render() { return <p>Hello</p>; } } // React 内部 const instance = new Greeting(props); // Greeting {} const result = instance.render(); // <p>Hello</p> 复制代码

在两种情况下React的目标都是获取被渲染的节点(在这个例子中就是, <p>Hello</p> )。但是具体的步骤取决于 Greeting 是怎样定义的。

因此React是怎样分辨类和函数的呢?

正如我之前文章所说的,不知道这些你也能使用React生产。多年来我都不知道这件事。请不要把这个问题变成面试问题。事实上,这篇文章更多的关于是javascript而不是React。

这篇文章是为那些好奇于React为何会在一种特定方式下工作的读者写的。你是那样的读者吗?让我们一起深入探讨吧。

这是一段较长的旅程。这篇博客不会有很多关于React的信息,但是我们会审查某些方面 new , this , class , arrow functions , prototype , __proto__ , instanceof ,和这些东西在 JavaScript 中是如何一起工作的。幸运的是,当你使用React时你不需要考虑太多这方面的问题。如果你正在实现那么...

(如果你只是想要知道结果,请导航到最底部。)

首先,我们需要了解为什么将函数和类视为不同的是重要的。注意我们怎样使用new操作符当调用一个类时:

// 如果Greeting是一个函数 const result = Greeting(props); // <p>Hello</p> // 如果Greeting是一个类 const instance = new Greeting(props); // Greeting {} const result = instance.render(); // <p>Hello</p> 复制代码

让我们粗略了解一下JavaScript中的new操作符是做什么的。

在过去,JavaScript中不存在类。可是,你可以通过简单的函数表示一个相似的类。具体说来,你可以使用任何函数类似于类的角色通过添加 new 在它被调用时:

// 仅仅是个函数 function Person(name) { this.name = name; } var fred = new Person('Fred'); // Person {name: 'Fred'} var george = Person('George'); // Won't work 复制代码

你现在依然能够这样写!尝试写在DevTools上。

如果你调用 Person('Fred') 没有 new ,它内部的 this 将指向全局或者不可用(例如, window 或者 undefined ).因此我们的代码可能崩溃或者做一些愚蠢的事情例如设置 window.name 。

调用之前通过添加 new ,我们说:“嗨 JavaScript ,我知道 Person 是一个函数但是假装它是一个类构造器。 创建一个 {} 对象并将 Person 函数内部的 this 指向该对象,因此我可以定义一些东西比如 this.name 。然后它会返回一个对象给我。

这就是 new 操作符所做的事情。

var fred = new Person('Fred'); // 相同的对象this在Person内部 复制代码

new 操作符也能使我们定义在 Person.prototype 的任何东西可用:

function Person(name) { this.name = name; } Person.prototype.sayHi = function() { alert('Hi, I am ' + this.name); } var fred = new Person('Fred'); fred.sayHi(); 复制代码

这就是人们在JavaScript直接添加类之前模拟类的方式。

JavaScript中的 new 已经存在一段时间了。然后, classe 没出现多久。这让我们重写上面的代码,以便更加地匹配我们的意图:

class Person { constructor(name) { this.name = name; } sayHi() { alert('Hi, I am ' + this.name); } } let fred = new Person('Fred'); fred.sayHi(); 复制代码

捕捉开发者的意图 对于一种语言或者 API 的设计是重要的。

如果你写了一个函数,JavaScript猜不到是像 alert() 这样调用它还是像 new Person() 一样作为一个构造函数。忘记使用 new 特殊的处理像 Person 这样的函数会导致混乱的行为。

Class 语法让我们说:“这不只是函数――它是一个类并且拥有构造器” 。如果你忘记使用 new 当调用它的时候,JavaScript会抛出一个错误:

let fred = new Person('Fred'); // 如果Person是一个函数:正常工作 // 如果Person是一个类:也能正常工作 let george = Person('George'); // 没有加new // 如果Person是一个构造函数的样子,迷惑的行为 // 如果Person是一个类:将立即失败 复制代码

这会帮助我们很早地捕获错误而不是等到一些迷惑的 bug 出现就像 this.name 被认为是 window.name 而不是 george.name 。

然而,这就意味着React需要将 new 加上在调用任何类之前。不能仅仅将它作为普通的函数调用,否则JavaScript会将它看做是一个错误!

class Counter extends React.Component { render() { return <p>Hello</p> } } // React 不能这样做 const instance = Counter(props); 复制代码

这样会导致问题。

在我们看React是如何解决这个问题时,记住大多数人在使用React时,为了兼容老的浏览器,都会使用编译器比如 Babel 将现有的一些特性例如类进行编译是重要的。因此我们需要考虑编译因素在我们的设计中。

在最近的 Babel 版本中,类可以不使用 new 而被调用。但是,这很快被修复了――通过生成一些额外的代码:

function Person(name) { // 大大的精简了从Babel的输出中 if (!(this instanceof Person)) { throw new TypeError("Cannot call a class as a function"); } // 我们的代码 this.name = name; } new Person('Fred'); // Okay Person('George'); // 不能调用类像函数一样 复制代码

你可能有一些代码像这样在你的包中。那些都是 _classCallCheck 函数所做的事情。(你可以通过选择“松散模式”来减少包的大小,而无需进行检查,但是这可能会使你最终转换到真正的本地类的过程变得复杂。)

到目前为止,你应该大致了解了使用new或不使用new调用某些东西的区别:

New Person() Person()

class this是一个Person实例 TypeError

function this是一个Person实例 this是window或者undefined

这就是为什么正确调用组件的React非常重要。 如果你的组件被定义为一个类,则React在调用它时需要使用 new 。

那么React仅仅检查某个东西是否是类吗?

不会这么简单的!尽管我们 在JavaScript中能够从函数中分辨出类 ,这仍然不适用于Babel等工具处理的类。对于浏览器来说,它们只是普通函数。对React是不好的。

好吧,也许React在每次调用时都要使用 new ?不幸的是,这也不总是有效的。

对于常规函数,用 new 调用它们会给它们一个 this 的对象实例。对于作为构造函数编写的函数(如我们上面提到的Person)来说,它是可取的,但是对于函数组件来说,它可能会令人混淆:

function Greeting() { // 我们不期望this是任何类型的实例 return <p>Hello</p> } 复制代码

不过,这是可以容忍的。有两个其他的原因扼杀了这个想法。

总是使用`new`不起作用的第一个原因是本地箭头函数(不是`Babel`编译的那些函数)),调用使用`new`会抛出一个错误:

const Greeting = () => <p>Hello</p>; new Greeting(); // Greeting不是一个构造器 复制代码

这种行为是有意的,并遵循箭头函数的设计。箭头函数的一个主要好处是它们没有自己的 this 值――相反, this 的值指向最近的常规函数:

class Friends extends React.Component { render() { const friends = this.props.friends; return friends.map(friend => <Friend // `this`是render中的值 size={this.props.size} name={friend.name} key={friend.id} /> ); } } 复制代码

箭头函数没有自己的 this 。这意味着它们作为构造函数将完全无用!

const Person = (name) => { // :red_circle: 这样是没有意义的 this.name = name; } 复制代码

因此, JavaScript不允许使用 new 调用箭头函数 。如果你这样做了,无论如何你都可能犯了一个错误,最好早点告诉你。这类似于JavaScript在没有 new 的情况下不允许调用类。

这很好,但也破坏了我们的计划。React不能对所有东西都调用 new ,因为它会破坏箭头函数!我们可以通过箭头函数缺少原型来检测它们,而不仅仅 new 一个:

(() => {}).prototype // undefined (function() {}).prototype // {constructor: f} 复制代码

但这 不适用 于Babel编译的函数。这可能不是什么大问题,但还有一个原因使这种方法成为死胡同。

我们不能总是使用new的另一个原因是,它将阻止对返回字符串或其他基本类型的组件的支持。

本文前端(javascript)相关术语:javascript是什么意思 javascript下载 javascript权威指南 javascript基础教程 javascript 正则表达式 javascript设计模式 javascript高级程序设计 精通javascript javascript教程

分页:12
转载请注明
本文标题:React怎样从函数中辨别类
本站链接:https://www.codesec.net/view/620878.html


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 前端(javascript) | 评论(0) | 阅读(12)