今天刚好在看构造器相关的东西,下午写到react函数组件的默认值和类型检查时,引发了一点点小的思考。
React的类组件类型检查,我们一般会声明propTypes和defaultProps为静态属性。
import React, {component} from 'react';
import PropTypes from 'prop-types';
class Demo extends React.Component {
constructor() {
super();
}
static defaultProps = {
name: 'Erina'
}
static propTypes = {
name: PropTypes.string
}
render() {
return (
<h1>Hello, {this.props.name}</h1>
);
}
}
函数组件这样写,其实类组件也可以这样写,下面的写法并不是函数组件的专有写法。
import PropTypes from 'prop-types';
class Demo extends React.Component {
render() {
return (
<h1>Hello, {this.props.name}</h1>
);
}
}
Demo.defaultProps = {
name: 'Erina'
}
Demo.propTypes = {
name: PropTypes.string
};
static只是语法糖,背后机制其实还是挂载到构造函数Demo上去,作为Demo的属性。
背后会经过Babel的转换工具transform-class-properties 转换。
function Demo() {};
Demo.name = 'Erina';
console.log(Demo.name); // 'Erina'
console.log(new Demo().name); // undefined
console.log(new Demo()); // Demo() {}
===================================
class Demo {
static name = 'Erina';
}
console.log(Demo.name); // 'Erina'
console.log(new Demo().name); // undefined
console.log(new Demo()); // Demo() {}
构造函数
构造函数在技术上是常规函数。不过有两个约定:
- 它们的命名以大写字母开头。
它们只能由
"new"
操作符来执行。function User(name) {
this.name = name;
this.isAdmin = false;
}
let user = new User("Jack");
alert(user.name); // Jack
alert(user.isAdmin); // false
当一个函数被使用
new
操作符执行时,它按照以下步骤:一个新的空对象被创建并分配给
this
。- 函数体执行。通常它会修改
this
,为其添加新的属性。 - 返回
this
的值。
换句话说,new User(...)
做的就是类似的事情:
function User(name) {
// this = {};(隐式创建)
// 添加属性到 this
this.name = name;
this.isAdmin = false;
// return this;(隐式返回)
}
所以 new User("Jack")
的结果是相同的对象:
let user = {
name: "Jack",
isAdmin: false
};
现在,如果我们想创建其他用户,我们可以调用 new User("Ann")
,new User("Alice")
等。比每次都使用字面量创建要短得多,而且更易于阅读。
这是构造器的主要目的 —— 实现可重用的对象创建代码
构造器模式测试:new.target
在一个函数内部,我们可以使用 new.target
属性来检查它是否被使用 new
进行调用了。
对于常规调用,它为空,对于使用 new
的调用,则等于该函数:
function User() {
alert(new.target);
}
// 不带 "new":
User(); // undefined
// 带 "new":
new User(); // function User { ... }
它可以被用在函数内部,来判断该函数是被通过 new
调用的“构造器模式”,还是没被通过 new
调用的“常规模式”。
我们也可以让 new
调用和常规调用做相同的工作,像这样:
function User(name) {
if (!new.target) { // 如果你没有通过 new 运行我
return new User(name); // ……我会给你添加 new
}
this.name = name;
}
let john = User("John"); // 将调用重定向到新用户
alert(john.name); // John
这种方法有时被用在库中以使语法更加灵活。这样人们在调用函数时,无论是否使用了 new
,程序都能工作。
构造器的 return
通常,构造器没有 return
语句。它们的任务是将所有必要的东西写入 this
,并自动转换为结果。
但是,如果这有一个 return
语句,那么规则就简单了:
- 如果
return
返回的是一个对象,则返回这个对象,而不是this
。 - 如果
return
返回的是一个原始类型,则忽略。
换句话说,带有对象的 return
返回该对象,在所有其他情况下返回 this
。
例如,这里 return
通过返回一个对象覆盖 this
:
function BigUser() {
this.name = "John";
return { name: "Godzilla" }; // <-- 返回这个对象
}
alert( new BigUser().name ); // Godzilla,得到了那个对象
这里有一个 return
为空的例子(或者我们可以在它之后放置一个原始类型,没有什么影响):
function SmallUser() {
this.name = "John";
return; // <-- 返回 this
}
alert( new SmallUser().name ); // John
通常构造器没有 return
语句。这里我们主要为了完整性而提及返回对象的特殊行为。
省略括号
顺便说一下,如果没有参数,我们可以省略 new
后的括号:
let user = new User; // <-- 没有参数
// 等同于
let user = new User();
这里省略括号不被认为是一种“好风格”,但是规范允许使用该语法。
构造器中的方法
使用构造函数来创建对象会带来很大的灵活性。构造函数可能有一些参数,这些参数定义了如何构造对象以及要放入什么。
当然,我们不仅可以将属性添加到 this
中,还可以添加方法。
例如,下面的 new User(name)
用给定的 name
和方法 sayHi
创建了一个对象:
function User(name) {
this.name = name;
this.sayHi = function() {
alert( "My name is: " + this.name );
};
}
let john = new User("John");
john.sayHi(); // My name is: John
/*
john = {
name: "John",
sayHi: function() { ... }
}
*/
总结
- 构造函数,或简称构造器,就是常规函数,但大家对于构造器有个共同的约定,就是其命名首字母要大写。
- 构造函数只能使用
new
来调用。这样的调用意味着在开始时创建了空的this
,并在最后返回填充了值的this
。
后半段内容来源于:https://zh.javascript.info/constructor-new#gou-zao-qi-de-return