07_JavaScript对象的基本使用-引用类型的值传递-构造函数

导读:本篇文章讲解 07_JavaScript对象的基本使用-引用类型的值传递-构造函数,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

认识对象类型

  • 在数据类型中我们提到还有一种特别的类型:对象类型
    • 对象类型涉及到JavaScript的各个方面,所以掌握对象类型非常重要
    • 对象类型是一种存储键值对(key-value)的更复杂的数据类型
  • 为什么需要对象类型呢?
    • 基本数据类型可以存储一些简单的值,但是现实世界的事物抽象成程序时,往往比较复杂;
    • 比如一个人,有自己的特性(比如姓名、年龄、身高),有一些行为(比如跑步、学习、工作);
  • 这个时候,我们需要一种新的类型将这些特性和行为组织在一起,这种类型就是对象类型。
    • 对象类型可以使用{…}来创建的复杂类型,里面包含的是键值对(“key: value)
    • 键值对可以是属性和方法(在对象中的函数称之为方法)
    • 其中key是字符串 一般情况下key的引号省略
    • 其中value可以是任意类型,包括基本数据类型、函数类型、对象类型等

创建对象和使用对象

  • 对象的创建方法有很多,包括三种

    • 对象字面量: 通过{}
    • new Object + 动态添加属性
    • new 其他类
  • 目前我们主要掌握对象字面量的方式,后续我们学习其他两种方式

    • 属性之间是以逗号分割的

    • 对象的使用过程包括如下操作

      • 访问对象的属性;

        • //访问obj对象的name
          console.log(obj.name)
          
      • 修改对象的属性;

        • //修改对象的属性
          obj.name = "kaisa"
          obj.age = 30
          
      • 添加对象的属性;

        • //添加对象属性
          obj.height = 1.88
          obj.address = "成都"
          
      • 删除对象的属性

        • //删除对象属性
          delete obj.name
          delete obj.age
          

方括号和引用的使用

  • 为什么要使用方括号

    • 如果属性名是存放在变量中 就需要使用方括号

    • 对于一些特殊属性来说, js是无法理解的, 例如:

    • var obj = {
        name = "kaisa",
        //例如两个单词的属性
        "my friend" = "why"
      }
      console.log(obj["my friend"])
      
  • 这是因为 点符号要求key是有效的变量标识符

    • 不包含空格、不以数字开头、也不能包含($ 和_ 除外的符号)
  • 当我们属性名中确实有以上符号时,我们可以使用 方括号

    • 方括号运行我们在定义或操作时更加灵活

    • var obj = {
        name = "kaisa",
        //例如两个单词的属性
        "my friend" = "why"
      }
      console.log(obj["my friend"])
      

对象的遍历

  • 对象的遍历(迭代): 表示获取对象中所有属性和方法

    • Object.keys() 方法会返回一个由一个 给定对象的自身可枚举属性 组成的数组

    • var info = {
      	name: "why",
      	age: 18,
      	height: 1.88,
      }
      //拿到info对象中所有的属性名(key) 并返回一个数组
      console.log(Object.keys(info))
      
  • 遍历方式一: 普通for循环

    • //定义对象
      var info = {
      	name: "why",
      	age: 18,
      	height: 1.88,
      }
      //拿到info对象中所有的属性名(key) 并返回一个数组
      var infoKeys = Object.keys(info)
      //for循环遍历对象
      for (let i = 0; i < infoKeys.length; i++) {
        var key = infoKeys[i]
        var value = info[key]
        console.log(`key: ${key},value: ${value}`)
      }
      
  • 遍历方式二 : for…in…遍历方法

    • //定义对象
      var info = {
      	name: "why",
      	age: 18,
      	height: 1.88,
      }
      //for...in...遍历对象
      for (key in info) {
        var value = info[key]
        console.log(`key: ${key},value: ${value}`)
      }
      

栈内存和堆内存

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

值类型和引用类型

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

引用类型的一些现象

  • 现象一: 两个对象比较

    • var num1 = 123
      var num2 = 123
      console.log(num1 === num2) //返回的是true
      
      var obj1 = {}
      var obj2 = {}
      console.log(obj1 === obj2) //返回的是false
      
    • 上述代码中 两个对象返回的是false

    • 原因是: 每创建一个对象,都会在堆内存中开辟一个空间, 而变量中保存的是堆内存的内存地址

    • 两个对象比较也就是在比较变量中保存的地址 obj1 和 obj2 中保存的内存地址是不一样的 因此是false

  • 现象二: 引用的赋值

    • //创建一个对象
      var obj = {
        name: "why"
      }
      //将obj对象赋值给一个变量
      var foo = obj
      foo.name = "kebe"
      console.log(obj.name) //kobe
      
    • 上述代码 返回的是kobe

    • 原因是: 函数中的foo对象和obj对象 指向的是堆内存中的同一个内存地址

    • 当通过foo将name属性修改, 由于obj也指向的同一个地址 因此obj的name属性也会是修改后的值

  • 现象三: 值传递

    • //声明一个函数
      function foo(a) {
        a = 200
      }
      var num = 100
      foo(num)
      console.log(num) //打印结果为100
      
    • 上述代码中 num的值没有改变 还是100

    • 原因是: 将num当参数传入函数foo中时, 相当于做了一步操作 a = 100

    • a的值会在栈内存中单独开辟一个空间保存, 当a = 200发生改变时, 改变的是a的值,num的值没有改变 还是100

现象四: 引用传递

  • 情况一: 引用传递 但是在函数中创建了一个新对象, 没有对传入对象进行修改

    • //声明一个函数
      function foo(a) {
      	a = {
      		name: "kobe"
      	}
      }
      //定义一个对象
      var obj = {
      	name: "why" 
      }
      foo(obj)
      console.log(obj.name) //打印结果why 
      
    • 打印结果没有改变 任然是obj

    • 原因是: 当obj作为参数传入函数时 相当于把obj的地址赋值给了变量a

    • 而函数内部又创建了一个新的对象, 因此a指向另一个地址 不在是obj对象的地址

    • 所以a与obj指向的地址不同, a中修改name属性, 不影响obj中的name属性

  • 情况二: 引用传递, 但是对传入的对象进行修改

    • //声明一个函数
      function foo(a) {
      	a.name = "kobe";
      }
      //定义一个对象
      var obj = {
      	name: "why",
      };
      foo(obj);
      console.log(obj.name) //打印结果kobe
      
    • 打印结果改变为kobe

    • 原因是: 当obj作为参数传入函数时 相当于把obj的地址赋值给了变量a

    • 在函数内部修改a的name, 因为a与obj指向同一地址, 所以修改a的name属性, obj的name属性也会改变

函数中this指向

  • 函数中是有一个this的变量, this变量在大多数情况下会指向一个对象
    • this指向在函数定义的时候是确定不了的, 只有在函数执行的时候才能确定this到底指向
    • 一般情况下this指向的是函数的调用者
  • 情况一: 全局作用域或者普通的函数的默认调用, 那么this指向的就是全局对象window(定时器的this也是指向window)
    • 默认调用就是 函数名() 的形式
  • 情况二: 对象调用方法中, 谁调用指向谁
    • 情况三: 构造函数中, this指向构造函数的实例

批量创建对象方法 – 工厂函数

  • 工厂函数: 其实是一种常见的设计模式

  • 我们可以封装一个函数, 这个函数用于帮我们创建一个对象, 我们只需要重复调用这个函数即可

  • 例如: 学生系统中创建一系列学生(学生都有姓名, 学号, 年龄等, 但是具体的值又不相同)

    • 此时可以使用工厂函数的方法, 批量创建对象, 但又让他们的属性不一样

    • function createStudent(name, age, id) {
        var stu = {}
        stu.name = name
        stu.age = age
        stu.id = id
        return stu
      }
      var stu1 = createStudent("小明", 18, 116);
      var stu2 = createStudent("小王", 19, 136);
      var stu3 = createStudent("小红", 16, 126);
      console.log(stu1);
      console.log(stu2);
      console.log(stu3);
      

认识构造函数

  • 工厂的方法创建对象有一个比较大的问题: 我们在打印对象时, 对象的类型都是Object类型
    • 但是从某些角度来说, 这些对象应该有一个他们共同的类型
  • 什么是构造函数
    • 构造函数也称之为构造器, 通常是我们在创建对象时会调用的函数
    • 在其他面向对象的编程语言里面, 构造函数是存在于类中的一个方法, 称之为构造方法
    • 但是在JavaScript中构造函数不太一样, 构造函数扮演了其他语言中类的角色
  • 也就是在JavaScript中, 构造函数其实就是类的扮演者
    • 比如系统默认给我们提供的Date就是一个构造函数, 也可以看成一个类
    • ES5之前, 我们都是通过function来声明一个构造函数(类)的, 之后通过new关键字来对起进行调用
    • ES6之后, JavaScript可以像别的语言一样, 通过class来声明一个类

JavaScript中的类(ES5)

  • JavaScript中类的表示形式就是构造函数

    • 构造函数也是一个普通的函数, 从表现形式来说, 和千千万万个普通函数没有任何区别
    • 那么如果这么一个普通的函数被使用new操作符来调用了, 那么这个函数就称之为是一个构造函数
  • 如果一个函数被new操作符调用了, 那么他会执行如下操作

      1. 在内存中创建一个新的空对象
      2. 在这个对象内部的[[prototype]]属性会被赋值为该构造函数的prototype属性(这一点后面详细讲)
      3. 构造函数内部的this, 会指向创建出来的新对象
      4. 执行构造函数内部的代码(函数体代码)
      5. 如果构造函数没有明确返回非空对象, 则自动返回创建出来的新对象

批量创建对象方法 – 构造函数

  • 这种方式更符合JavaScript的思维方式(面向对象的思维方式)

  • 使用构造函数的方式批量创建学生

  • function Student(name, age, id) {
      //自动创建一个新的空对象, 且this执行这个空对象
      this.name = name;
      this.age = age;
      this.id = id;
      //自动返回this创建出来的新对象
    }
    var stu1 = new Student("小明", 18, 111);
    var stu2 = new Student("小红", 19, 112);
    console.log(stu1);
    console.log(stu2);
    

简单理解全局对象window

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

额外补充

  • 函数也是一个对象

    • 在创建一个函数的时候 也是在堆内存中开辟一块空间 而变量中保存的是地址
  • 引申出来的小知识

    • 由于函数也是一个对象 那么函数也可以通过 函数名.属性=属性值 的方法向函数添加属性4

    • //创建一个函数
      function foo() {}
      //向构造函数上添加属性
      foo.age = 18
      console.log(foo.age) //18
      
    • 而构造函数(类)上面添加函数, 称之为类方法

    • //创建一个构造函数
      function Dog() {}
      //向构造函数上添加函数
      Dog.running = function() {}
      Dog.running()
      

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/120156.html

(0)
seven_的头像seven_bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!