认识对象类型
- 在数据类型中我们提到还有一种特别的类型:对象类型。
- 对象类型涉及到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操作符调用了, 那么他会执行如下操作
-
- 在内存中创建一个新的空对象
- 在这个对象内部的[[prototype]]属性会被赋值为该构造函数的prototype属性(这一点后面详细讲)
- 构造函数内部的this, 会指向创建出来的新对象
- 执行构造函数内部的代码(函数体代码)
- 如果构造函数没有明确返回非空对象, 则自动返回创建出来的新对象
-
批量创建对象方法 – 构造函数
-
这种方式更符合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