【JS笔记】JavaScript语法 《基础+重点》 知识内容,快速上手(六)

本文介绍了面向对象编程(OOP)的核心概念,包括封装、继承和多态,重点讲解了构造函数在对象创建中的作用,以及ES6中类的写法和继承机制。作者通过实例展示了如何使用构造函数和类来创建和扩展对象,强调了面向对象编程的思想模式:通过对象完成功能,而不是过程导向的编程方式。
摘要由CSDN通过智能技术生成

面向对象OOP

  • 首先,我们要明确,面向对象不是语法,是一个思想,是一种 编程模式
  • 面向: 面(脸),向(朝着)
  • 面向过程: 脸朝着过程 =》 关注着过程的编程模式
  • 面向对象: 脸朝着对象 =》 关注着对象的编程模式
  • 实现一个效果
    • 在面向过程的时候,我们要关注每一个元素,每一个元素之间的关系,顺序,。。。
    • 在面向对象的时候,我们要关注的就是找到一个对象来帮我做这个事情,我等待结果
  • 例子 🌰: 我要吃面条
    • 面向过程
      • 用多少面粉
      • 用多少水
      • 怎么和面
      • 怎么切面条
      • 做开水
      • 煮面
      • 吃面
    • 面向对象
      • 找到一个面馆
      • 叫一碗面
      • 等着吃
    • 面向对象就是对面向过程的封装
  • 我们以前的编程思想是,每一个功能,都按照需求一步一步的逐步完成
  • 我们以后的编程思想是,每一个功能,都先创造一个 面馆,这个 面馆 能帮我们作出一个 面(完成这个功能的对象),然后用 面馆 创造出一个 ,我们只要等到结果就好了

面向对象的编程语言 c++ java js python php 大前端 安卓(java) iOS(swift oc) js跨端框架

面向过程编程语言 c

面向对象的三大特点

封装、继承、多态

封装函数 封装对象(属性和方法)

Math.random new Array a.push

创建对象的方式

  • 因为面向对象就是一个找到对象的过程
  • 所以我们先要了解如何创建一个对象

调用系统内置的构造函数创建对象

  • js 给我们内置了一个 Object 构造函数

  • 这个构造函数就是用来创造对象的

  • 当 构造函数 和 new 关键字连用的时候,就可以为我们创造出一个对象

  • 因为 js 是一个动态的语言,那么我们就可以动态的向对象中添加成员了

    // 就能得到一个空对象
    var o1 = new Object() 
    
    // 正常操作对象
    o1.name = 'Jack'
    o1.age = 18
    o1.gender = '男'
    

字面量的方式创建一个对象

  • 直接使用字面量的形式,也就是直接写 {}

  • 可以在写的时候就添加好成员,也可以动态的添加

    // 字面量方式创建对象
    var o1 = {
      name: 'Jack',
      age: 18,
      gender: '男'
    }
    
    // 再来一个
    var o2 = {}
    o2.name = 'Rose'
    o2.age = 20
    o2.gender = '女'
    

使用工厂函数的方式创建对象

  • 先书写一个工厂函数

  • 这个工厂函数里面可以创造出一个对象,并且给对象添加一些属性,还能把对象返回

  • 使用这个工厂函数创造对象

    // 1. 先创建一个工厂函数
    function createObj() {
      // 手动创建一个对象
      var obj = new Object()
    
      // 手动的向对象中添加成员
      obj.name = 'Jack'
      obj.age = 18
      obj.gender = '男'
    
      // 手动返回一个对象
      return obj
    }
    
    // 2. 使用这个工厂函数创建对象
    var o1 = createObj()
    var o2 = createObj()
    

使用自定义构造函数创建对象

  • 工厂函数需要经历三个步骤

    • 手动创建对象
    • 手动添加成员
    • 手动返回对象
  • 构造函数会比工厂函数简单一下

    • 自动创建对象
    • 手动添加成员(属性和方法)
    • 自动返回对象
  • 先书写一个构造函数

  • 在构造函数内向对象添加一些成员

  • 使用这个构造函数创造一个对象(和 new 连用)

  • 构造函数可以创建对象,并且创建一个带有属性和方法的对象

  • 面向对象就是要想办法找到一个有属性和方法的对象

  • 面向对象就是我们自己制造 构造函数 的过程

    // 1. 先创造一个构造函数
    function Person(name, gender) {
      this.age = 18
      this.name = name
      this.gender = gender
    }
    
    // 2. 使用构造函数创建对象
    var p1 = new Person('Jack', 'man')
    var p2 = new Person('Rose', 'woman')
    

构造函数详解

  • 我们了解了对象的创建方式
  • 我们的面向对象就是要么能直接得到一个对象
  • 要么就弄出一个能创造对象的东西,我们自己创造对象
  • 我们的构造函数就能创造对象,所以接下来我们就详细聊聊 构造函数

构造函数的基本使用

  • 和普通函数一样,只不过 调用的时候要和 new 连用,不然就是一个普通函数调用

    function Person() {}
    var o1 = new Person()  // 能得到一个空对象
    var o2 = Person()      // 什么也得不到,这个就是普通函数调用
    
    • 注意: 不写 new 的时候就是普通函数调用,没有创造对象的能力
  • 首字母大写

    function person() {}
    var o1 = new person() // 能得到一个对象
    
    function Person() {}
    var o2 = new Person() // 能得到一个对象
    
    • 注意: 首字母不大写,只要和 new 连用,就有创造对象的能力
  • 当调用的时候如果不需要传递参数可以不写 (),建议都写上

    function Person() {}
    var o1 = new Person()  // 能得到一个空对象
    var o2 = new Person    // 能得到一个空对象 
    
    • 注意: 如果不需要传递参数,那么可以不写 (),如果传递参数就必须写
  • 构造函数内部的 this,由于和 new 连用的关系,是指向当前实例对象的

    function Person() {
      console.log(this)
    }
    var o1 = new Person()  // 本次调用的时候,this => o1
    var o2 = new Person()  // 本次调用的时候,this => o2
    
    • 注意: 每次 new 的时候,函数内部的 this 都是指向当前这次的实例化对象
  • 因为构造函数会自动返回一个对象,所以构造函数内部不要写 return

    • 你如果 return 一个基本数据类型,那么写了没有意义
    • 如果你 return 一个引用数据类型,那么构造函数本身的意义就没有了

使用构造函数创建一个对象

  • 我们在使用构造函数的时候,可以通过一些代码和内容来向当前的对象中添加一些内容

    function Person() {
      this.name = 'Jack'
      this.age = 18
    }
    
    var o1 = new Person()
    var o2 = new Person()
    
    • 我们得到的两个对象里面都有自己的成员 name 和 age
  • 我们在写构造函数的时候,是不是也可以添加一些方法进去呢?

    function Person() {
      this.name = 'Jack'
      this.age = 18
      this.sayHi = function () {
        console.log('hello constructor')
      }
    }
    
    var o1 = new Person()
    var o2 = new Person()
    
    • 显然是可以的,我们的到的两个对象中都有 sayHi 这个函数
    • 也都可以正常调用
  • 但是这样好不好呢?缺点在哪里?

    function Person() {
      this.name = 'Jack'
      this.age = 18
      this.sayHi = function () {
        console.log('hello constructor')
      }
    }
    
    // 第一次 new 的时候, Person 这个函数要执行一遍
    // 执行一遍就会创造一个新的函数,并且把函数地址赋值给 this.sayHi
    var o1 = new Person()
    
    // 第二次 new 的时候, Person 这个函数要执行一遍
    // 执行一遍就会创造一个新的函数,并且把函数地址赋值给 this.sayHi
    var o2 = new Person()
    
    • 这样的话,那么我们两个对象内的 sayHi 函数就是一个代码一摸一样,功能一摸一样
    • 但是是两个空间函数,占用两个内存空间
    • 也就是说 o1.sayHi 是一个地址,o2.sayHi 是一个地址
    • 所以我们执行 console.log(o1 === o2.sayHi) 的到的结果是 false
    • 缺点: 一摸一样的函数出现了两次,占用了两个空间地址
  • 怎么解决这个问题呢?

    • 就需要用到一个东西,叫做 原型,后面讲

es6新增的类和继承

class的写法及继承JavaScript 语言中,生成实例对象的传统方法是通过构造函数。下面是一个例子

function Point(x, y) {
  this.x = x;
  this.y = y;}

Point.prototype.toString = function () {
  return '(' + this.x + ', ' + this.y + ')';};

var p = new Point(1, 2);

上面这种写法跟传统的面向对象语言(比如 C++ 和 Java)差异很大,很容易让新学习这门语言的程序员感到困惑。

定义类

ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。 基本上,ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。上面的代码用 ES6 的class改写,就是下面这样。

//定义类
class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }}

上面代码定义了一个“类”,可以看到里面有一个constructor方法,这就是构造方法,而this关键字则代表实例对象。也就是说,ES5 的构造函数Point,对应 ES6 的Point类的构造方法 point类除了构造方法,还定义了一个toString方法。注意,定义“类”的方法的时候,前面不需要加上function这个关键字,直接把函数定义放进去了就可以了。另外,方法之间不需要逗号分隔,加了会报错。

/

point.hasOwnProperty('x') // true

point.hasOwnProperty('y') // true

ES6 的类,完全可以看作构造函数的另一种写法 class Point {// ...} typeof Point // "function"上面代码表明,类的数据类型就是函数,类本身就指向构造函数

let methodName = 'getArea';

class Square {
   //构造函数
  constructor(length) {
  // ...
  }
  //实例方法 对象方法
  [methodName]() {
  // ...
  }}
  //实例方法 对象方法
  wang(){
  
  }

上面代码中,Square类的方法名getArea,是从表达式得到的。

类内部,默认就是严格模式,所以不需要使用use strict指定运行模式。只要你的代码写在类或模块之中,就只有严格模式可用。 考虑到未来所有的代码,其实都是运行在模块之中,所以 ES6 类实际上把整个语言升级到了严格模式。

constructor 方法

constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。 class Point {}// 等同于class Point { constructor() {}}上面代码中,定义了一个空的类Point,JavaScript 引擎会自动为它添加一个空的constructor方法。 constructor方法默认返回实例对象(即this

类必须使用new调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用new也可以执行。

class Foo { constructor() { }} Foo()// TypeError: Class constructor Foo cannot be invoked without 'new'

生成类的实例对象的写法,与 ES5 完全一样,也是使用new命令。前面说过,如果忘记加上new,像函数那样调用Class,将会报错。 class Point {// ...}// 报错var point = Point(2, 3);// 正确var point = new Point(2, 3);

类不存在变量提升(hoist),这一点与 ES5 完全不同。 new Foo(); // ReferenceErrorclass Foo {}

类方法

加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。

class Foo { static classMethod() { return 'hello'; }} Foo.classMethod() // 'hello'var foo = new Foo();foo.classMethod()// TypeError: foo.classMethod is not a function

上面代码中,Foo类的classMethod方法前有static关键字,表明该方法是一个静态方法,可以直接在Foo类上调用(Foo.classMethod()),而不是在Foo类的实例上调用。如果在实例上调用静态方法,会抛出一个错误,表示不存在该方法。

继承

类的继承Class 可以通过extends关键字实现继承 这比 ES5 的通过修改原型链(在后面章节会讲解)实现继承,要清晰和方便很多。

class Point {}

class ColorPoint extends Point {}

上面代码定义了一个ColorPoint类,该类通过extends关键字,继承了Point类的所有属性和方法。但是由于没有部署任何代码,所以这两个类完全一样,等于复制了一个Point类。下面,我们在ColorPoint内部加上代码。

class ColorPoint extends Point {
  constructor(x, y, color) {
    super(x, y); // 调用父类的constructor(x, y)
    this.color = color;
  }

  toString() {
    return this.color + ' ' + super.toString(); // 调用父类的toString()
  }}

上面代码中,constructor方法和toString方法之中,都出现了super关键字,它在这里表示父类的构造函数,用来新建父类的this对象。 ES6 要求,子类的构造函数必须执行一次super函数

子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象。

class Point { /* ... */ }

class ColorPoint extends Point {
  constructor() {
  }}

let cp = new ColorPoint(); // ReferenceEr

上面代码中,ColorPoint继承了父类Point,但是它的构造函数没有调用super方法,导致新建实例时报错。

需要注意的地方是,在子类的构造函数中,只有调用super之后,才可以使用this关键字,否则会报错。这是因为子类实例的构建,是基于对父类实例加工,只有super方法才能返回父类实例

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }}

class ColorPoint extends Point {
  constructor(x, y, color) {
    this.color = color; // ReferenceError
    super(x, y);
    this.color = color; // 正确
  }}

上面代码中,子类的constructor方法没有调用super之前,就使用this关键字,结果报错,而放在super方法之后就是正确的。

总结

  • 到了这里,我们就发现了面向对象的思想模式了

    • 当我想完成一个功能的时候
    • 先看看内置构造函数有没有能给我提供一个完成功能对象的能力
    • 如果没有,我们就自己写一个构造函数,能创造出一个完成功能的对象
    • 然后在用我们写的构造函数 new 一个对象出来,帮助我们完成功能就行了
  • 比如: tab选项卡

    • 我们要一个对象
    • 对象包含一个属性:是每一个点击的按钮
    • 对象包含一个属性:是每一个切换的盒子
    • 对象包含一个方法:是点击按钮能切换盒子的能力
    • 那么我们就需要自己写一个构造函数,要求 new 出来的对象有这些内容就好了
    • 然后在用构造函数 new 一个对象就行了
  • 17
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

旺旺大力包

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值