跳到主要内容

2 篇博文 含有标签「es6」

查看所有标签

面向对象编程及其在 javascript 中的实现

· 阅读需 5 分钟

历史

谈到 OOP(面向对象编程),我的印象总是停留在我刚学一些静态语言时(如 java),相对于 python、JavaScript,OOP 的概念总是被很早的引入。视频里的讲师总是举一些诸如 Animal 类和 Cat\Dog 对象的例子。Ok 能听懂,so what the fuck is next? 这东西到底有啥用?实际编写网站项目时,也用不着什么 Animal 类啊。

在一番探索后,我目前的想法是:OOP 本身是为了降低代码的耦合度而提出的,那么一切代码耦合度较高的地方都可以尝试用 OOP。另外,现代的框架很多时候是这样工作的,框架规范了写法是在类内书写属性和方法,然后创建对象的工作交给了框架在后台进行,故看不到。这样的框架在前后端都有,比如 react、eggjs。也就是说,实际上 OOP 无处不在,学习 OOP 有助于理解框架并更好的使用。

说到 Javascript 的面向对象,ES6 之前是借助一套复杂难懂的原型系统实现,ES6 之后向主流的面向对象语言写法靠近,采用了 class 关键字来创建类。以前的那套只做了解,重点掌握的应该是现代的写法。

规范

OOP 作为一种编程范式,有它自己的一套规定和要素。

属性和方法

class Animal {
constructor(type = "cat", legs = 4) {
this.type = type;
this.legs = legs;
}
}

let dog = new Animal((type = "dog"), (legs = 4));
dog.legs = 2; // 对象属性可以修改

constructor 构造函数执行类的初始化操作,如果不需要初始化,可以不定义构造函数,这种情况相当于构造函数内部为空。

静态方法

无需实例化对象就可以调用类中的方法。

class Animal {
constructor(type = "cat", legs = 4) {
this.type = type;
this.legs = legs;
}

static return10() {
return "10";
}
}

Animal.return10();

getter/setter

明明是方法,但是调用时形式像是属性。

class Animal {
constructor(type = "cat", legs = 4) {
this.type = type;
this.legs = legs;
}

get metaData() {
return `${this.type}, ${this.legs}`;
}

set setType(value) {
this.type = value;
}
}

let dog = new Animal((type = "dog"), (legs = "2"));

console.log(dog.metaData); // getter调用
dog.setType = "alaska dog"; // setter调用

继承

新类拓展父类,而不需要将父类复制一遍,这就是继承。问题是,如何继承?如何拓展?借助 super 关键字。

class Animal {
constructor(type = "cat", legs = 4) {
this.type = type;
this.legs = legs;
}

makeNoise(noise = "loud noise") {
console.log(noise);
}
}

class Cat extends Animal {
constructor(type, legs, tail) {
super(type, legs); // 继承
this.tail = tail;
}

makeNoise(noise, db) {
// 重写
super.makeNoise(noise); // 继承
console.log(`DB is ${db}`); // 拓展的新功能
}
}

let cat = new Cat("cat", 4, true);

cat.makeNoise("meow", 40);

这里的 super 就起到继承的作用,构造函数的继承就是 super(),而方法的继承是 super.methodName()。super 只能在类的构造函数和方法中使用。必须在构造函数中首先调用 super:在子类的构造函数中,必须在使用 this 之前先调用 super。否则会抛出错误。

以下内容只存在于 typescript 中。

权限控制符:public、private、protect

对一个变量、方法的使用范围做限制的修饰符。public 可以在类和对象使用;private 只能在类内使用;private 可以在类及其子类中使用。

interface 接口

接口的含义是,使用这个接口(关键字是 implements)的类,必须将接口中的属性和方法全部实现一遍。

abstract class 抽象类

抽象类是类的模板,可以被类继承,但是不能直接实例化出对象。

拧巴的地方

JavaScript 中并没有类这个数据类型,类的实质是函数:

class Person {}
console.log(Person); // class Person {}
console.log(typeof Person); // function

ES6 中的 class 很大程度上是基于既有原型机制的语法糖。

es6重要特性

· 阅读需 3 分钟

前言

语言学习看文档是非常无聊的,所以找来视频来学,并做了笔记。

模板语法

当返回一串比较复杂的字符串时,优先使用模板语法:

`${var1}, ${var2}`;

优点:模板所见即所得,即不需要敲换行、空格等占位符。

解构和扩展运算符

一切可迭代的数据类型(数组、对象等)都可以解构。解构出的是子元素。扩展运算府(三个点)也是将子元素拿出来放到一个新数据里。他们两个容易弄混:

解构:
let income = [10, 20, 30];
let [a,b,c]=income //a=10,b=20,c=30

扩展运算符:
let x=[...income] //x=[10,20,30]

看出差别了吧,解构是为了把数据取出来赋值给分散的新变量,而扩展运算符是把数据取出来包裹起来作为整体赋给一个变量。

对象字面量

如果对象的属性是从之前声明的变量获得的,那么在创建变量时,可以直接用变量来进行声明,而不需要写完整的键值对:

let name, age;
(name = "jason"), (age = 18);
let user = {
name,
age,
};

剩余操作符

"Rest operator" 可以翻译为 "剩余操作符" 或者 "剩余参数"。在 JavaScript 中,这个概念指的是使用三个点符号(...)来收集函数中的剩余参数,将它们组合成一个数组。

举个例子,假设有一个函数用来计算总和:

function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}

console.log(sum(1, 2, 3, 4)); // 输出 10

let const

let/const 一直是 ES6 经久不衰的一个重要知识点。首先必须要知道,他俩为什么要发明出来取代 var。因为 var 有一个让人非常不爽的特点:变量提升。这是一个十分臭名昭著的特性,给 JavaScript 代码带来了很多 bug。所以 let、const 被提出,他俩就非常老实,只在自己的块级作用域内生效。也许 javascript 一开始就本该如此,谁知道创始人发了什么疯呢。

export import

见之前博文:javascript 模块系统

Class

见之前博文:面向对象编程及其在 javascript 中的实现

async/await promise

见之前博文:同步异步任务在 javascript 中的实现