js笔记(四)-面向对象、堆内存、栈内存

69 阅读9分钟

1. 面向对象

1.1 认识对象类型

  1. 在数据类型中我们提到还有一种特别的类型:对象类型。
  • 对象类型涉及到JavaScript的各个方面,所以掌握对象类型非常重要;
  • 对象类型是一种存储键值对 key-value的更复杂的数据类型;
  1. 为什么需要对象类型呢?
  • 基本数据类型可以存储一些简单的值,但是现实世界的事物抽象成程序时,往往比较复杂;
  • 比如一个人,有自己的特性(比如姓名、年龄、身高),有一些行为(比如跑步、学习、工作);
  • 比如一辆车,有自己的特性(比如颜色、重量、速度),有一些行为(比如行驶);
  1. 这个时候,我们需要一种新的类型将这些特性和行为组织在一起,这种类型就是对象类型。
  • 对象类型可以使用{…}来创建的复杂类型,里面包含的是键值对(key: value);
  • 键值对可以是属性和方法(在对象中的函数称之为方法)
  • 其中key是字符串(也叫做属性名property name ,ES6之后也可以是Symbol类型,后续学习);
  • 其中value可以是任意类型,包括基本数据类型、函数类型、对象类型等;
  1. 两个术语: 函数/方法
  • 函数(function): 如果在JavaScript代码中通过function默认定义一个结构, 称之为是函数.
  • 方法(method): 如果将一个函数放到对象中, 作为对象的一个属性, 那么将这个函数称之为方法.

1.2 创建对象和使用对象

  1. 对象的创建方法有很多,包括三种:
  • 对象字面量(Object Literal):通过{}
  • new Object+动态添加属性;
  • new 其他类;

image.png

  1. 目前我们主要掌握对象字面量的方式,后续我们学习其他两种方式。
  • 属性之间是以逗号( comma )分割的;
  1. 对象的使用过程包括如下操作:
  • 访问对象的属性; info.name
  • 修改对象的属性; info.age = 25
  • 添加对象的属性;
  • 删除对象的属性;
<script>
    // 1.定义了一个对象
    var info = {
      name: "why",
      age: 18,
      friend: {
        name: "kobe",
        age: 30
      },
      running: function() {
        console.log("running~")
      }
    }

    // 2.访问对象中的属性
    console.log(info.name)
    console.log(info.friend.name)
    info.running()

    // 3.修改对象中的属性
    info.age = 25
    info.running = function() {
      alert("I am running~")
    }
    console.log(info.age)
    info.running()

    // 4.添加对象中的属性
    info.height = 1.88
    info.studying = function() {
      console.log("I am studying~")
    }
    console.log(info)

    // 5.删除对象中的属性
    // delete关键字(操作符)
    delete info.age
    delete info.height
    console.log(info)

  </script>

1.3 方括号和引用的使用

  1. 为什么需要使用方括号呢?
  • 对于多次属性来说,JavaScript是无法理解的。

image.png

  1. 这是因为点符号要求 key 是有效的变量标识符
  • 不包含空格,不以数字开头,也不包含特殊字符(允许使用 $ 和 _);
  1. 这个时候我们可以使用方括号:
  • 方括号运行我们在定义或者操作属性时更加的灵活;

image.png

1.4 对象的遍历

  1. 对象的遍历(迭代):表示获取对象中所有的属性和方法。
  • Object.keys() 方法会返回一个由一个给定对象的自身可枚举属性组成的数组;

image.png 2. 遍历方式一:普通for循环 3. 遍历方式二:for in 遍历方法

  • for循环对象的key

image.png

2. 栈内存和堆内存

  1. 我们知道程序是需要加载到内存中来执行的,我们可以将内存划分为两个区域:栈内存和堆内存。
  • 原始类型占据的空间是在栈内存中分配的;
  • 对象类型占据的空间是在堆内存中分配的;

image.png

  • 函数的执行是在栈内存中,
  • 函数本身是堆内存中

3. js中的一些现象

1.现象一: 两个对象的比较

// 1.现象一: 两个对象的比较
    var obj1 = {}
    var obj2 = {}
    console.log(obj1 === obj2)//false
  1. 引用的赋值
 // 2.现象二: 引用的赋值
    var info = {
      name: "why",
      friend: {
        name: "kobe"
      }
    }

    var friend = info.friend
    friend.name = "james"
    console.log(info.friend.name) // james

image.png 3. 现象三: 值传递

 // 3.现象三: 值传递
    function foo (a) {
      a = 200
    }
    var num = 100
    foo(num)
    console.log(num) // 100

image.png 4. 现象四: 引用传递, 但是在函数中创建了一个新对象, 没有对传入对象进行修改


    // 4.现象四: 引用传递, 但是在函数中创建了一个新对象, 没有对传入对象进行修改
    function foo (a) {
      a = {
        name: "why"
      }
    }
    var obj = {
      name: "obj"
    }
    foo(obj)
    console.log(obj)//obj

image.png

  1. 现象五: 引用传递, 但是对传入的对象进行修改
// 5.现象五: 引用传递, 但是对传入的对象进行修改
    function foo (a) {
      a.name = "why"
    }

    var obj = {
      name: "obj"
    }
    foo(obj)
    console.log(obj)//why

image.png

4. 值类型和引用类型

  1. 原始类型的保存方式:在变量中保存的是值本身
  • 所以原始类型也被称之为值类型
  1. 对象类型的保存方式:在变量中保存的是对象的“引用”
  • 所以对象类型也被称之为引用类型

image.png

5. 为什么需要this?

  1. 在常见的编程语言中,几乎都有this这个关键字(Objective-C中使用的是self),但是JavaScript中的this和常见的面向对象 语言中的this不太一样:
  • 常见面向对象的编程语言中,比如Java、C++、Swift、Dart等等一系列语言中,this通常只会出现在类的方法中。
  • 也就是你需要有一个类,类中的方法(特别是实例方法)中,this代表的是当前调用对象
  1. 但是JavaScript中的this更加灵活,无论是它出现的位置还是它代表的含义;
  2. 看一下编写一个obj的对象,有this和没有this的区别:

image.png

5.1 this 的执向

  1. 情况一: 如果普通的函数被默认调用, 那么this指向的就是 window

image.png 2. 情况二: 如果函数它是被某一个对象来引用并且调用它, 那么this会指向这个对象(调用的那个调用)

image.png

  1. 题目
  • window调用 image.png
  • obj调用

image.png

6. 创建读写

6.1 类和对象的思维方式

  1. 我们来思考一个问题:如果需要在开发中创建一系列的相似的对象,我们应该如何操作呢?
  2. 比如下面的例子:
  • 游戏中创建一系列的英雄(英雄具备的特性是相似的,比如都有名字、技能、价格,但是具体的值又不相同)
  • 学生系统中创建一系列的学生(学生都有学号、姓名、年龄等,但是具体的值又不相同)
  1. 当然,一种办法是我们创建一系列的对象 image.png
  2. 这种方式有一个很大的弊端:创建同样的对象时,需要编写重复的代码;
  • 我们是否有可以批量创建对象,但是又让它们的属性不一样呢?

6.2 创建对象的方案 – 工厂函数

  1. 我们可以想到的一种创建对象的方式:工厂函数
  • 我们可以封装一个函数,这个函数用于帮助我们创建一个对象,我们只需要重复调用这个函数即可;
  • 工厂模式其实是一种常见的设计模式

image.png

6.2 认识构造函数

  1. 工厂方法创建对象有一个比较大的问题:我们在打印对象时,对象的类型都是Object类型
  • 但是从某些角度来说,这些对象应该有一个他们共同的类型;
  • 下面我们来看一下另外一种模式:构造函数的方式;
  1. 我们先理解什么是构造函数?
  • 构造函数也称之为构造器(constructor),通常是我们在创建对象时会调用的函数;
  • 在其他面向的编程语言里面,构造函数是存在于类中的一个方法,称之为构造方法
  • 但是JavaScript中的构造函数有点不太一样,构造函数扮演了其他语言中类的角色
  1. 也就是在JavaScript中,构造函数其实就是类的扮演者
  • 比如系统默认给我们提供的Date就是一个构造函数,也可以看成是一个类;
  • 在ES5之前,我们都是通过function来声明一个构造函数(类)的,之后通过new关键字来对其进行调用;
  • 在ES6之后,JavaScript可以像别的语言一样,通过class来声明一个类;
  1. 那么类和对象到底是什么关系呢?
  • 那么什么是类(构造函数)呢?
  • 现实生活中往往是根据一份描述/一个模板来创建一个实体对象的.
  • 编程语言也是一样, 也必须先有一份描述, 在这份描述中说明将来创建出来的对象有哪些属性(成员变量)和行为(成员方法)

6.2 在函数调用的前面加 new 关键字(操作符)

  1. JavaScript已经默认提供给了我们可以更加符合JavaScript思维方式(面向对象的思维方式)的一种创建对象的规则
  2. 在函数中的this一般指向某一个对象 如果一个函数被new操作符调用
  • 1.创建出来一个新的空对象
  • 2.这个对象内部的[[prototype]]属性会被赋值为该构造函数的prototype属性;
  • 3.让this指向这个空对象
  • 4.执行函数体的代码块
  • 5.如果没有明确的返回一个非空对象, 那么this指向的对象会自动返回
// JavaScript已经默认提供给了我们可以更加符合JavaScript思维方式(面向对象的思维方式)的一种创建对象的规则
    // 在函数中的this一般指向某一个对象
    /*
      如果一个函数被new操作符调用
        1.创建出来一个新的空对象
        2. 这个对象内部的[[prototype]]属性会被赋值为该构造函数的prototype属性;
        3.让this指向这个空对象
        4.执行函数体的代码块
        5.如果没有明确的返回一个非空对象, 那么this指向的对象会自动返回
    */
    function coder(name, age, height) {
      this.name = name
      this.age = age
      this.height = height
      this.running = function() {
        console.log("running~")
      }
    }

    // 在函数调用的前面加 new 关键字(操作符)
    var stu1 = new coder("why", 18, 1.88)
    var stu2 = new coder("kobe", 30, 1.98)
    console.log(stu1, stu2)

7. 全局对象

  1. js中存在一个全局对象bject -> window
  • 作用一: 查找变量时, 最终会找到window头上
  • 作用二: 将一些浏览器全局提供给我们的变量/函数/对象, 放在window对象上面
  • 作用三(了解): 使用var定义的变量会被默认添加到window上面
  1. 函数也是一个对象
<script>

    // 定义原始类型的变量
    var name = "why"
    var age = 18

    // 定义对象类型的变量
    // 地址 - 指针 - 引用
    var obj = {} // 堆内存
    var foo = function() {} // 堆内存
    function bar() {} // 堆内存

    console.log(typeof obj) // object
    console.log(typeof foo) // function -> object

    // var stu = new Student() // stu是一个Student -> Person

    // 引申一些别的知识(了解)
    var info = {}
    info.name = "abc"

    function sayHello() {
    }
    sayHello.age = 18
    console.log(sayHello.age)

    function Dog() {
      
    }
    // 构造函数上(类上面)添加的函数, 称之为类方法
    Dog.running = function() {}
    Dog.running()
    
  </script>