1.js面向对象
Everything is object (万物皆对象)
对象到底是什么,我们可以从两个层次来理解。
(1) 对象是单个事物的抽象。
一本书、一辆汽车、一个人都可以是对象,一个数据库、一张网页、一个与远程服务器的连接也可以是对象。当实物被抽象成对象,实物之间的关系就变成了对象之间的关系,从而就可以模拟现实情况,针对对象进行编程。
(2) 对象是一个容器,封装了属性(property)和方法(method)。
属性是对象的状态,方法是对象的行为(完成某种任务)。比如,我们可以把动物抽象为animal对象,使用“属性”记录具体是哪一种动物,使用“方法”表示动物的某种行为(奔跑、捕猎、休息等等)。
在实际开发中,对象是一个抽象的概念,可以将其简单理解为:数据集或功能集。
ECMAScript-262 把对象定义为:无序属性的集合,其属性可以包含基本值、对象或者函数。 严格来讲,这就相当于说对象是一组没有特定顺序的值。对象的每个属性或方法都有一个名字,而每个名字都映射到一个值。
提示:每个对象都是基于一个引用类型创建的,这些类型可以是系统内置的原生类型,也可以是开发人员自定义的类型。
面向对象与面向过程:
- 面向过程就是亲力亲为,事无巨细,面面俱到,步步紧跟,有条不紊
- 面向对象就是找一个对象,指挥得结果
- 面向对象将执行者转变成指挥者
- 面向对象不是面向过程的替代,而是面向过程的封装
面向对象的特性:
- 封装性
- 继承性
- [多态性]抽象
1.1在 es6 中,官方给出了 class 关键字来实现面向对象风格的写法,但本质上是寄生组合式继承的语法糖。
class Father{
constructor(nickname,money,house){
this.name= nickname
this.money = money
this.house = house
this.eat = function(){
console.log('我是Father上的方法eat');
}
console.log(nickname);
console.log(this); //在class中 this执行实例化出来的对象
}
jump(){
console.log('我是Father方法 跳一跳');
}
}
class Son extends Father{
constructor(name,money,house){;
// console.log(this);
super() //他的作用 更改this指向,复制父类的属性和方法,访问this之前一定要先调用super
this.name = name
this.money = money
this.house = house
console.log(this);
this.type = 'hhhhh'
}
fn(){
console.log('hhhh');
}
}
let fa = new Father('爸爸','有钱','有别墅')
console.log(fa);
let s = new Son('儿子','有钱','有别墅')
console.log(s);
2.TS修饰符
/ 类的修饰符 // // 设置这个修饰符 就相当于是限制属性的使用范围
- public 公共的 如果一个属性定义为public 那么在子类 父类 不管是类的内部还是外部都能使用
- protected 受保护的只在类里面以及子类里面能用 在外面不能用
- private 私有的 只在自己的类中能访问 子类 还有外面都不能访问
- readonly 只读 就是一个属性在赋值了之后 就不能再修改了
3.使用递归解决层级比较深的数据
简单地理解递归就是:自己调用自己
主要有两个关键步骤:
- 写出递归公式
- 找到终止条件
先来看个简单的例子:如何求1+2+3+4+...+n的和?相信用for循环的方法大家都知道如何编写:
function sum(n) {
var total = 0
for (int i = 1; i <= n; i++) {
total = total + i
}
return total
}
那如何改为递归的写法呢?
第一步: 写出递归公式 那如何改为递归的写法呢?
细心观察就会发现,其实就是n与n-1和n-2的关系
sum(n) = sum(n-1) + n
···
···
···
sum(100) = sum(99) + 100
sum(99) = sum(98) + 99
···
···
···
sum(5) = sum(4) + 5
sum(4) = sum(3) + 4
sum(3) = sum(2) + 3
sum(2) = sum(1) + 2
sum(1) = 1
细心观察就会发现,其实就是n与n-1和n-2的关系
sum(n) = sum(n-1) + n
···
···
···
sum(100) = sum(99) + 100
sum(99) = sum(98) + 99
···
···
···
sum(5) = sum(4) + 5
sum(4) = sum(3) + 4
sum(3) = sum(2) + 3
sum(2) = sum(1) + 2
sum(1) = 1
将如上转化成递归公式就是
function sum(n) {
return sum(n-1) + n
}
如何用递归思想实现数组的扁平化❓ // 数组的扁平化 就是多层数组的嵌套 转换为一层数组
// let arr = [1,2,[3,4,[5,6]]] ----> arr = [1,2,3,4,5,6]
// let arr = [1,2,[3,4,[5,6,[7]]]]
// // 递归
// function flat(arr){
// let newArr = []
// arr.forEach(item=>{
// // 条件 调用自己的条件 判断item是不是数组 如何还是数组 就去调用自己,如何不是数组
// if(Array.isArray(item)){
// console.log(newArr);
// newArr = newArr.concat(flat(item))
// }else{
// newArr.push(item)
// }
// })
// return newArr
// }
// console.log(flat(arr));
4.枚举
枚举是组织收集有关联变量的一种方式 ,每个枚举成员都有一个 name 和一个 value。数字枚举成员值的默认类型是 number 类型
enum Color {
Red, // 默认情况下,第一个枚举值是 0,然后每个后续值依次递增 1
Green,
Blue
}
let col = Color.Red;
col = 0; // 有效的,这也是 Color.Red
枚举是一个被命名的整型常数的集合,用于声明一组命名的常数,当一个变量有几种可能的取值时,可以将它定义为枚举类型
通俗来说,枚举就是一个对象的所有可能取值的集合
枚举的使用是通过enum关键字进行定义,形式如下:
enum xxx { ... }
类型可以分成:
- 数字枚举
- 字符串枚举
- 异构枚举
#数字枚举
当我们声明一个枚举类型时,虽然没有给它们赋值,但是它们的值其实是默认的数字类型,而且默认从 0 开始依次累加:
enum Direction {
Up, // 值默认为 0
Down, // 值默认为 1
Left, // 值默认为 2
Right, // 值默认为 3
}
console.log(Direction.Up === 0) // true
console.log(Direction.Down === 1) // true
console.log(Direction.Left === 2) // true
console.log(Direction.Right === 3) // true
如果我们将第一个值进行赋值后,后面的值也会根据前一个值进行累加 1:
enum Direction {
Up = 10,
Down,
Left,
Right,
}
console.log(Direction.Up, Direction.Down, Direction.Left, Direction.Right) // 10 11 12 13
字符串枚举
枚举类型的值其实也可以是字符串类型:
enum Direction {
Up = 'Up',
Down = 'Down',
Left = 'Left',
Right = 'Right'
}
console.log(Direction['Right'], Direction.Up); // Right Up### 枚举
定义类
在 TypeScript 中,我们也是通过 Class 关键字来定义一个类, 使用 constructor 定义构造函数。
class Animal {
public name: string;
constructor(name: string) {
this.name = name;
}
sayHi(): string {
return `我的名字 ${this.name}`;
}
}
继承
TS中的继承ES6中的类的继承极其相似,子类可以通过extends关键字继承一个类 例如
在构造器里访问 this 的属性之前,一定要调用 super() ,这个是 TypeScript 强制执行的一条重要规则。
class Person {
constructor (name) {
// 等价于我们的构造函数体
this.name = name
}
// 原型上的方法
sayHi () {
console.log('hello world')
}
}
// 2. 准备一个子类
// 一个继承自 Person 的 Student 子类
class Student extends Person {
constructor (age, name) {
// super(name) // 就相当于我们 es5 的借用构造函数继承
// 只不过不需要我们写 call 去改变 this 指向了
// 直接帮我们改变好 this 指向了, 我们只需要传递参数就可以了
super(name)
this.age = age
}
son () {
console.log('我是 子类的方法')
}
}
var s1 = new Student(18, 'Jack')
console.log(s1)
跟ES6一样,子类构造函数必须加上super()执行父类的构造constructor函数
所以,大家常常说,一个子类实例,同时也是父类的实例
类的修饰符
TypeScript 中有三类访问修饰符,分别是: public、private、protected。不写默认为 public
public:自己、自己的子类 和其他类都可以访问 (默认值)protected:受保护的 自己和自己的子类能访问, 其他类不能访问private:私有的 只能自己访问,自己的子类不能访问,其他类更不能访问
// 定义一个类
class Person {
// name:string
// private name:string
protected name:string
constructor(name:string){
this.name = name
}
public eat(){
console.log(`${this.name},好吃`)
}
}
// 定义一个子类
class Student extends Person {
constructor(name:string){
super(name)
}
play(){
// 当name为protected时,子类中也可访问;
// 但如果其为private,子类中则无法访问
console.log('like play',this.name)
}
}
const p1 = new Person('张三')
// console.log(per.name) // name 为私有,外部无法访问
per.eat()
定义接口
接口的作用类似于抽象类,不同