认识对象类型
在数据类型中我们知道还有一种特别的类型:对象类型
- 对象类型涉及到 JavaScript 的各个方面
- 对象类型是一种存储键值对(key-value)的更复杂的数据类型
为什么需要对象类型
- 基本数据类型可以存储一些简单的值,但是现实世界的事物抽象成程序时,往往比较复杂
- 比如一个人,有自己的特性(比如姓名、年龄、身高),有一些行为(比如跑步、学习、工作)
- 比如一辆车,有自己的特性(比如颜色、重量、速度),也有一些行为(比如行驶)
这时,我们需要一种新的数据类型将这些特性和行为组织到一起,这就是对象类型
- 对象类型可以使用 {...}
来创建,里面包含的是键值对("key:value"
)
- 键值对可以是属性和方法(在对象中的函数称之为方法)
- 其中 key 是字符串(也叫作属性名 property name,ES6之后也可以是 Symbol 类型)
- 其中 value 可以是任意类型,包括基本数据类型、函数类型、对象类型等
/*
函数/方法
函数(function):在JavaScript代码中通过function直接定义一个结构称之为函数
方法(method):如果将一个函数,放在对象中作为一个对象的属性,称之为方法
*/
// key:字符串类型。但是在定义对象的属性名时,大部分情况下是可以省略的
var person = {
name: 'why',
age: 18,
height: 1.87,
"my friend": {
name: "kobe",
age: 46
},
run: function () {
console.log('running')
},
eat: function () {
console.log('eat foods')
},
study: function () {
console.log('studying')
}
}
创建对象和使用对象
对象的创建方法有很多,包括三种
- 对象字面量(Object Literal):通过 {} 创建
- new Object + 动态添加属性
- new 其他类
属性之间是以逗号(comma)分隔的
对象的使用包括如下操作
- 访问对象的属性
- 修改对象的属性
- 添加对象的属性
- 删除对象的属性
var info = {
name: 'why',
age: 17,
friend: {
name: 'kobe',
age: 30
},
running: function () {
console.log('running~')
}
}
// 访问对象中的属性
/* console.log(info.name)
console.log(info.friend.name)
info.running() */
// 修改对象中的属性
/* info.age = 34
info.running = function () {
alert('I am running')
}
console.log(info.age)
info.running() */
// 给对象添加新的属性
info.height = 1.88
info.studying = function () {
console.log('I am studying')
}
console.log(info)
// 删除属性 delete 关键字(操作符)
delete info.age
delete info.name
console.log(info);
方括号和引用的使用
为什么需要方括号
- 对于如下这种属性的写法,JavaScript是无法理解的
这是因为点符号后面要求key是有效的变量标识符
- 不包括空格,不以数字开头,也不包含特殊符号(允许$和_)
这个时候我们可以使用方括号
- 方括号可以让我们在定义或者操作属性时更加的灵活
对象的遍历
对象的遍历(迭代):表示获取对象中所有的属性和方法
- Object.keys()
方法会返回一个由给定对象的其自身可枚举属性组成的数组
遍历方式一:普通 for 循环
var infoKeys = Object.keys(info)
for (var i = 0; i < infoKeys.length; i++) {
var key = infoKeys[i]
var value = infoKeys[key]
console.log(`key:${key}, value:${value}`)
}
遍历方式二: for in 遍历方法
for (var key in info) {
var value = info[key]
console.log(`key:${key},value: ${value}`)
}
栈内存和堆内存
程序时需要加载到内存中来执行的,我们可以将内存划分为两个区域: 栈内存和堆内存
- 原始类型占据的空间是在栈内存中分配的
- 对象类型占据的空间是在堆内存中分配的
值类型和引用类型
原始类型的保存方式:在变量中保存的是值本身
- 所以原始类型也被称为值类型
对象类型的保存方式:在变量中保存的是对象的 “引用”
- 所以对象类型也被称为引用类型
为什么需要this
在常见的编程语言中,几乎都有 this 这个关键字(有些语言使用的是self),但是 JavaScript中的 this 和常见的面向对象语言中的 this 不太一样
- 常见的面向对象编程语言,比如 java
、C++
、Swift
、Dart
等等一系列语言中,this
通常只会出现在类的方法中
- 也就是你需要有一个类,类中的方法(特别是示例方法)
// 函数中是有一个 this 的变量,this 变量大多数情况下会指向一个对象
// arguments 保存的是传入的所有的参数
// 情况一:如果普通函数被默认调用,那么this指向就是window
function foo (name, age) {
console.log(arguments)
console.log(this)
}
foo('abc', 123)
// 情况二:如果函数它是被某一个对象来引用并且调用它,那么this指向这个对象
/*
var obj = {
name: 'why',
running: function () {
console.log(this)
console.log(obj)
console.log(obj === this) // true
}
}
*/
// obj.running()
// var fn = obj.running
// fn() 默认调用,指向 window
function bar () {
console.log(this) // obj
}
var friend = {
}
var obj = {
name: 'why',
friend: friend,
bar: bar
}
obj.bar()
var info = {
name: 'why',
age: 18,
running: function () {
console.log('running~', this.name)
},
eating: function () {
console.log('eating~', this.name)
},
studying: function () {
console.log('studying~', this.name)
}
}
info.running()
info.eating()
info.studying()
javascript 中的 this 更加灵活,无论是它出现的位置还是它代表的含义
编写一个 obj 对象,有 this 和没有 this 的区别
- 没有 this 的话里面需要写上变量的名称,变量名修改了全部都需要修改
-
this指向什么
先掌握两个 this 的判断方法
- 以默认的方式调用一个函数,this 指向 windows
- 通过对象调用,this 指向调用的对象
类和对象的思维方式
我们先来思考一个问题:如果在开发中需要创建一系列相似的对象,应该如何操作
比如下面的例子
- 游戏中创建一系列的英雄(英雄具备的特性是相似的,比如都有名字、技能、价格、但是具体的值又不相同)
- 学习系统中创建一系列的学生(学生都有学号、姓名、年龄等,但是具体的值又不相同)
当然其中一种方式是我们手动创建一系列的对象:
这种方式是最笨的,创建相似的对象需要编写如此多重复的代码
- 需要可以批量创建对象但是又让它们的属性不一样
创建对象的方案=工厂函数
我们可以想到一种创建对象的方式:工厂函数
- 先封装一个函数,这个函数用于帮助我们创建一个对象,我们只需要重复调用这个函数即可
- 工厂模式是一种很常见的设计模式
构造函数
工厂方法创建对象有一个比较大的弊端,我们在打印对象时,对象的类型都是 Object 类型
- 但是从某些角度来说,这些对象应该都有一个它们共同的类型
我们先理解一下什么是构造函数
- 构造函数也称之为构造器(constructor
),通常是我们在创建对象时会调用的函数
- 在其他面向对象编程语言中,构造函数是存在于类中的一个方法,称之为构造方法
- 但是 JavaScript
中的构造函数有点不太一样,构造函数扮演了其他语言中类的角色
也就是在 JavaScript 中,构造函数其实就是类的扮演者
- 比如系统默认给我们提供的 Date 就是一个构造函数,也可以看成是一个类
- 在 ES5 之前,我们都过 function 来声明一个构造函数(类)的,之后通过 new 关键字来对其进行调用
- 在 ES6 之后,JavaScript 可以想别的语言一样,通过 class 来声明一个类。
那么什么是类(构造函数)呢?
- 现实生活中往往是根据一份描述、一个模板来创建一个实体对象的
- 编程语言也是一样,也必须先有一份描述,在这份描述中说明将来创建出来的对象有哪些属性(成员变量)和行为(成员方法)
比如在现实生活中,我们会如此来描述一些事物
- 比如水果 fruits 是一类事物的统称,苹果、橘子、葡萄等是具体的对象
- 比如人 person 是一类事物的统称,而 Jim、Lucy、Lily、李磊、韩梅梅是具体的对象
植物大战僵尸
JavaScript中的类(ES5)
JavaScript中类的表现形式就是构造函数
JavaScript中的构造函数是怎样的?
- 构造函数也是一个普通的函数,从表现形式上来说,和千千万万个普通函数没有任何区别
- 那么如果一个普通的函数被使用 new 操作符来调用了,这个函数就可以称之为是一个构造函数
如果一个函数被 new 操作符调用了,那么它会执行如下操作
1. 在内存中创建一个新的对象(空对象)
2. 这个对象内部的 [[prototype]]
属性会被赋值为该构造函数的 prototype 属性
3. 构造函数内部的 this,会指向创造出来的新对象
4. 执行函数的内部代码(函数体代码)
5. 如果构造函数没有返回非空对象,则返回创建出来的新对象
创建对象的方案-构造函数
我们通过构造函数来实现
// JavaScript 已经提供了我们可以更加符合JavaScript思维方式(面向对象的思维方式)的一种创建对象的规则
// 在函数中的 this 一般指向某个对象
function Student (name, age, height) {
this.name = name
this.age = age
this.height = height
this.running = function () {
console.log('running~')
}
}
// 在函数调用前面加上 new 关键字(操作符)
var stu1 = new Student('rin', 18, 1.88)
这个构造函数可以确保我们的对象时具有 Student 类型的(实际是 constructor 的属性)
事实上构造函数还有很多其他的特性
- 比如原型、原型链、实现继承的方案
- 比如 ES6中类、继承的实现
全局对象 window
// 浏览器中存在一个全局对象 object -> window
// 作用一:查找变量时,最终会找到 window 上
// 作用二:将浏览器全局提供给我们的变量、函数、对象,放在 window 对象上
// 作用三:使用 var 定义的变量默认会被添加到 window 上面
console.log(window)
// 使用 var 定义变量
var message = 'hello world'
function foo () {
// 自己的作用域
// alert('Hello World')
console.log(window.console === console)
// 创建一个对象
// var obj = new Object()
console.log(window.Object === Object)
// DOM
console.log(document)
}
foo()
Q.E.D.