1. 对象类型的使用
1.1 认识对象类型
基础数据类型可以存储一些简单的值,但是现实世界的事物抽象
成程序时,往往比较复杂。
- 比如
一个人
,有自己的特性
(比如姓名、年龄、身高),有一些行为
(比如跑步、学习、工作) - 比如
一辆车
,有自己的特性
(比如颜色、重量、速度),有一些行为
(比如行驶)
- 对象类型:是一种
存储键值对(key-value)
的更复杂的数据类型,我们需要使用对象类型
将特性和行为组织到一起- 由
{……}
创建,里面包含键值对"key: value"
键值对
可以是属性和方法
(在对象中的函数称之为方法)- 其中
key是字符串
(也叫做属性名property name ,ES6之后也可以是Symbol类型,后续学习),但是在定义对象的属性名时,大部分情况下引号都是可以省略的
- 其中
value可以是任意类型
,包括基本数据类型、函数类型、对象类型等
- 由
/*
两个术语: 函数/方法
函数(function): 如果在JavaScript代码中通过function默认定义一个结构, 称之为是函数.
方法(method): 如果将一个函数放到对象中, 作为对象的一个属性, 那么将这个函数称之为方法.
*/
function foo() {
}
// key: 字符串类型, 但是在定义对象的属性名时, 大部分情况下引号都是可以省略的
var person = {
// key: value
name: "why",
age: 18,
height: 1.88,
"my friend": {
name: "kobe",
age: 30
},
run: function() {
console.log("running")
},
eat: function() {
console.log("eat foods")
},
study: function() {
console.log("studying")
}
}
1.2 创建对象
-
对象的属性用
逗号,分隔
-
对象的创建方法有很多,包括三种:
-
对象字面量(Object Literal)
:通过{ }
-
new Object + 动态添加属性
-
new 其他类 构造函数
-
1.3 对象的常见操作
// 1.定义了一个对象
var info = {
name: "why",
age: 18,
friend: {
name: "kobe",
age: 30
},
running: function() {
console.log("running~")
}
}
-
访问
对象的属性
-
修改
对象的属性
-
添加
对象的属性
-
删除
对象的属性
1.4 方括号和引号的使用
在对象中属性名(键)是字符串类型
,但是在定义对象的属性名时, 大部分情况下引号都是可以省略的。当我们用点符号去访问多词属性,JavaScript是无法理解的。
访问对象的两种方式
对象名.属性名
:- 点符号要求 key 是有效的变量标识符
- 并且不包含空格,不以数字开头,也不包含特殊字符(允许使用 $ 和 _ )
对象名["属性名"]
- 中括号访问键名时需要搭配引号访问,即字符串 ,也可以放入值为字符串的变量名
- 方括号的操作在定义或者操作属性时更加的灵活
1.5 对象的遍历
对象的遍历(迭代):表示获取对象中所有的属性和方法。
遍历对象的方法
-
普通for循环
:主要用到该方法,Object.keys() 方法会返回一个由一个给定对象的自身可枚举属性组成的数组。// 1.普通for循环 var infoKeys = Object.keys(info) for (var i = 0; i < infoKeys.length; i++) { var key = infoKeys[i] var value = info[key] console.log(`key: ${key}, value: ${value}`) }
-
for in 遍历方法
:// 2.for..in..: 遍历对象 for (var key in info) { var value = info[key] console.log(`key: ${key}, value: ${value}`) }
2. 值类型和引用类型
2.1 栈内存和堆内存
- 我们知道程序是需要加载到内存中来执行的,我们可以将内存划分为两个区域:
栈内存和堆内存
原始类型
占据的空间是在栈内存
中分配的对象类型
占据的空间是在堆内存
中分配的
备注:目前我们先掌握堆和栈的概念即可,后续我们会学习图中的其他知识
2.2 值类型和引用类型
-
原始类型也被称之为
值类型
:即在变量中保存的是值本身
-
以对象类型也被称之为
引用类型
:在变量中保存的是对象的“引用”,即地址
2.3 五种典型现象
-
现象一: 两个对象的比较
-
现象二: 引用的赋值
-
现象三: 值传递
-
现象四: 引用传递, 但是在函数中创建了一个新对象, 没有对传入对象进行修改
-
现象五: 引用传递, 但是对传入的对象进行修改
3. this 关键字
3.1 为什么需要this
- 在常见的编程语言中,几乎都有this关键字,但是在JavaScript中的this和常见的面向对象语言中的this不太一样
- 常见面向对象的编程语言中,比如Java、C++、Swift、Dart等等一系列语言中,
this通常只会出现在类的方法中,首先需要有一个类,类中的方法(特别是实例方法)中,this代表的是当前调用对象
- JavaScript中:this更加灵活,无论是它出现的位置还是它代表的含义,
下面我们开始了解JavaScript中this的指向
- 常见面向对象的编程语言中,比如Java、C++、Swift、Dart等等一系列语言中,
我们来看一下编写一个obj的对象,有this和没有this的区别:
3.2 函数中this的指向
函数中是有一个this的变量, this变量在大多数情况下会指向一个对象,我们也知道除了this外,还有一个arguments对象保存传入的所有参数
目前掌握两个this的判断方法:
1. 在全局环境
下,this指向window
;
// 情况一: 如果普通的函数被默认调用, 那么this指向的就是window
// function foo(name, age) {
// console.log(arguments)
// console.log(this)
// }
// foo("abc", 123)
function sayHello(name) {
console.log(this)
}
sayHello() //window
- 通过
对象调用
,this指向调用的对象
// 情况二: 如果函数它是被某一个对象来引用并且调用它, 那么this会指向这个对象(调用的那个调用)
var obj = {
name: "why",
running: function() {
console.log(this)
// console.log(obj)
// console.log(this === obj)
}
}
obj.running() //obj
this指向练习题:
var obj = {
name: "why",
running: function() {
console.log(this)
// console.log(obj)
// console.log(this === obj)
}
}
obj.running() //obj
// 考验题目
// 1.题目一:
var fn = obj.running
fn() // window
// 2.题目二:
function bar() {
console.log(this)
}
var obj = {
name: "why",
bar: bar
}
obj.bar()// obj对象
bar()// window对象
3.3 this在开发中的作用
当我们的对象中的方法执行是,需要一些参数,如果我们有很多方法都需要参数,那我们手动的去一个个添加是很麻烦的。
我们可以利用this的特性,即通过对象调用,指向的是调用的对象,即可以从对象中取值
4. 类和对象
4.1 类和对象的思维方式
- 如果需要在开发中创建一系列的相似的对象,我们应该如何操作呢?比如:
- 游戏中创建
一系列的英雄
(英雄具备的特性是相似的,比如都有名字、技能、价格,但是具体的值又不相同) - 学生系统中创建
一系列的学生
(学生都有学号、姓名、年龄等,但是具体的值又不相同)
- 游戏中创建
4.2 创建对象的方案
-
方案一(不可行):直接创建一系列的对象
-
方案二 工厂函数:我们可以
封装一个函数
,这个函数用于帮助我们创建一个对象
,我们只需要重复调用这个函数即可,工厂模式其实是一种常见的设计模式
分析:工厂方法创建对象有一个比较大的问题:我们在打印对象时,对象的类型都是Object类型
,我们希望从某些角度来说,创建的对象应该他有一个他们共同的类型
,因此,我们需要看一下另外一种模式:构造函数的方式
- 通过工厂函数创建的对象:因为{}默认时是通过new Object()生成的空对象,Object也是一个构造函数或类
- 通过构造函数创建的对象
4.3 认识构造函数
构造函数(也称 构造器 constructor): 我们通过调用构造函数来创建对象
,在其他编程语言中,构造函数是存在于类中的一个方法,称之为构造方法。但是在JavaScript中的构造函数有点不一样,构造函数其实就是类的扮演者
- 在ES5之前,我们都是通过
function来声明一个构造函数(类)
的,之后通过new关键字来对其进行调用
- 在ES6之后,
JavaScript可以像别的语言一样,通过class来声明一个类
像之后我们会学到内置对象,也可以称内置类,比如像系统默认给我们提供的Date就是一个构造函数,也可以看成是一个类
4.4 类和对象的关系
- 现实生活中:我们往往是根据
一份描述/一个模板创建一个实体对象
的,比如水果是统称,而苹果、橘子、葡萄等是具体的对象。 - 编程语言中也是一样: 首先必须有
一份描述
, 在这份描述中说明将来创建出来的对象有哪些属性(成员变量)和行为(成员方法)
4.5 构造函数的使用
需要使用 this关键字
以及 new关键字
- 在函数中的this一般指向某一个对象
如果一个函数被new操作符调用
- 在内存中创建一个新的对象(空对象);
- 这个对象内部的[[prototype]]属性会被赋值为该构造函数的prototype属性 (后续了解)
- 构造函数内部的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)
接下来,我们可以用构造函数的方式来实现一下批量创建学生。
4.5 构造函数(类)的总结
我们前面说过,在JavaScript中类的表示形式就是构造函数。
构造函数与普通函数的区别:
- 构造函数也是一个
普通的函数
,从表现形式来说,和千千万万个普通的函数
没有任何区别 - 如果这么一个
普通的函数被使用new操作符
来调用了,那么这个函数就称之为是一个构造函数
构造函数与普通函数的命名:在ES5之前为了区分构造函数和普通函数我们一般这么操作
- 构造函数:使用大驼峰命名
- 普通函数:使用小驼峰命名
// 创建一系列的对象
// 构造函数的名称: 使用大驼峰
function Person() {
}
// 普通函数: 使用小驼峰
function sayHello() {
}
// 构造函数创建对象
var p1 = new Person()
console.log(p1)
// 平时创建普通的对象
// new Object()
var obj1 = {}
var obj2 = new Object()
var obj3 = new Person()