JavaScript开发者应懂的33个概念25-工厂函数和类
目录
- 调用堆栈
- 原始类型
- 值类型和引用类型
- 隐式, 显式, 名义和鸭子类型
- == 与 ===, typeof 与 instanceof
- this, call, apply 和 bind
- 函数作用域, 块级作用域和词法作用域
- 闭包
- map, reduce, filter 等高阶函数
- 表达式和语句
- 变量提升
- Promise async 与 wait
- 立即执行函数, 模块化, 命名空间
- 递归
- 算法
- 数据结构
- 消息队列和事件循环
- setTimeout, setInterval 和 requestAnimationFrame
- 继承, 多态和代码复用
- 按位操作符, 类数组对象和类型化数组
- DOM 树和渲染过程
- new 与构造函数, instanceof 与实例
- 原型继承与原型链
- Object.create 和 Object.assign
- 工厂函数和类
- 设计模式
- Memoization
- 纯函数, 函数副作用和状态变化
- 耗性能操作和时间复杂度
- JavaScript 引擎
- 二进制, 十进制, 十六进制, 科学记数法
- 偏函数, 柯里化, Compose 和 Pipe
- 代码整洁之道
简介
记录一个重新学习javascript的过程 ,文章并不是按顺序写的,写完就会更新目录链接 本篇文章目录是参照 @leonardomso 创立,英文版项目地址在这里
前言
本篇文章分为四个部分
- 工厂函数和构造函数的区别
- ES6的类
1.工厂函数和构造函数的区别
看一个例子
function person(firstName, lastName, age) {
const person = {};
person.firstName = firstName;
person.lastName = lastName;
person.age = age;
return person;
}
上述代码,创建一个新对象,并将传递参数作为属性附加到该对象上并返回新对象。 这是一个简单的 JavaScript 工厂函数
实际上工厂函数也很好理解了:
- 它是一个函数。
- 它用来创建对象。
- 它像工厂一样,“生产”出来的函数都是“标准件”(拥有同样的属性)
我们来使用一下工厂函数,
const person1 = person('张', '三', 18);
const person2 = person('李', '四', 20);
console.log(person1);
console.log(person2);
只需要传入一个正确的参数,就可以获取你所需要的对象,而无需知道其创建细节
console.log(person1.constructor=Object);//true console.log(person1.constructor=person);//false
使用构造函数改造一下
function Person(firstName, lastName, age) {
this.firstName = firstName
this.lastName = lastName
this.age = age
}
const person3 = new Person('王', '二', 30);
console.log(person3);
二者写法的不同之处
1.没有显示地创建对象 2.直接将属性和方法赋值给了this 3.没有return语句,还应当注意到 Person的名字使用的是大写的P
我的理解,构造函数就相当于是创建了一个class类,创建实例的话,就是用new 创建,这种创建实例的方式,更像是es6的class。 总之,构造函数本身也是函数,只不过可以用来创建对象而已。 要创建Person的新实例,必须使用new操作符。以这种方式调用构造函数,实际上会有以下4个步骤。
1.创建一个新对象 2.将构造函数的作用域赋值给新对象(因此this就指向了这个新对象) 3.执行构造函数中的代码(为这个新对象添加属性) 4.返回新对象
构造函数的可以通过原型链来实现对象的继承和共享方法。所以说,工厂函数通常用于创建简单对象,而构造函数通常用于创建具有行为和状态的对象
console.log(person1.constructor=Object);//false console.log(person1.constructor=Person);//true
2.ES6的类
实际上,类是“特殊的函数”,就像你能够定义的函数表达式和函数声明一样,类语法有两个组成部分:类表达式和类声明。
2.1类声明
定义类的一种方法是使用类声明。要声明一个类,你可以使用带有class关键字的类名(这里是“Penson”)。定义类的开头字母要大写
class Person {
constructor(firstName, lastName,age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age
}
}
2.2提升
函数声明和类声明之间的一个重要区别在于,函数声明会提升,类声明不会。你首先需要声明你的类,然后再访问它,否则类似以下的代码将抛出ReferenceError:
函数声明这样写不会报错
const person2 = new Person('王', '二', 30);
console.log(person2);
function Person(firstName, lastName, age) {
this.firstName = firstName
this.lastName = lastName
this.age = age
}
但是类声明这样写会报错
const person2 = new Person('王', '二', 30)//报错
class Person {
constructor(firstName, lastName,age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age
}
}
2.3 类表达式
类表达式是定义类的另一种方法。类表达式可以命名或不命名。命名类表达式的名称是该类体的局部名称
//匿名类
const Person = class {
constructor(firstName, lastName,age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age
}
}
console.log(Person.name);//输出Person
// 命名类
const Person = class Person2{
constructor(firstName, lastName,age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age
}
}
console.log(Person.name);//输出Person2
2.3 constructor()
constructor方法是类的默认方法,通过**new命令生成对象实例时,自动调用该方法**,默认返回实例对象(即this)。一个类必须有constructor方法,如果没有手动定义,JS引擎会自动为它添加一个空的constructor方法
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
call() {
console.log(`我叫${this.name}, 今年${this.age}岁`);
}
play(ball) {
console.log(`我爱玩${ball}`);
}
}
var p5 = new Person("Tom", 18);
p5.call(); // 我叫Tom, 今年18岁
p5.play("足球"); // 我爱玩足球
Person.prop = 1;
console.log(Person.prop) // 1
实例对象身上有自身的属性,那class本身也会有自己的属性,也就是静态属性:
2.4 static静态方法
敲黑板了,凡是被static修饰的属性和方法都是静态方法和属性,只能被类名调用,不能被实例化对象调用.同时也不能被子类继承,换句话说它属于当前这个类的.
class Person {
constructor() {}
call() {
console.log(`我是实例方法`);
}
static staticFn() {
console.log(`我是静态方法`,this);
}
}
var p6 = new Person("Tom", 18);
p6.call();//我是实例方法
Person.staticFn();//我是静态方法
p6.staticFn();//报错
2.5 extends 类的继承
class之间可以通过extends关键字实现继承(继承父类的所有属性和方法)
// 父类
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
call() {
console.log(`我叫${this.name}, 今年${this.age}岁`);
}
play(ball) {
console.log(`我喜欢玩${ball}`);
}
}
// 子类
class Student extends Person {
constructor(name, age, score) {
super(name, age);
this.score = score;
}
// 重写父类的方法
call() {
console.log(
`我叫${this.name},今年${this.age}岁, 考了${this.score}分`
);
}
//如果子类中定义了构造函数,那么它必须先调用 super() 才能使用 this
}
var s1 = new Student("Tom", 18, 99);
s1.play("篮球"); // 我喜欢玩篮球
s1.call(); // 我叫Tom,今年18岁, 考了99分
从上面的案例中我们可以看到,当子类中出现与父类同名的方法时,会将父类的方法进行重写,从而使得子类实例能获取到最新的结果。
super
从上一个继承案例中我们可以发现有一个super关键字,子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象。
总结
- class类是基于原型的继承的语法糖;
- class的出现使得代码更加优雅,代码量减少;
- class类能清晰的指定构造函数和抽象方法;
- class类在声明时就可以设定属性和方法,不需要再往函数的原型中添加方法;
- 使用class类时必须实例化;
- 使用extends关键字实现继承。