【笔记】JS的对象
前言
JS的对象学习笔记
JS的面向对象学习笔记
对象的分类
内建对象
- 由ES标准定义的对象,在任何的ES的实现中都可以使用
- 比如:String、Number、Boolean、Function、Object、Math
宿主对象
- 由JS的运行时环境所提供的对象
- 在浏览器中运行的JS代码主要指由浏览器提供的对象,比如:BOM对象、DOM对象
自定义对象
- 由开发人员自己创建的对象
创建对象
通过字面量创建对象
1 | var obj = {}; |
- 多个属性和方法之间用
,分隔,最后一个属性或方法末尾可以添加,,也可以不添加,
1 | var obj = { |
- 如果键名中不包含特殊符号,可以省略引号
- 如果值是函数,可以省略
function关键字和键定义
1 | var obj = { |
通过工厂函数创建对象(ES5)
- 通过工厂设计模式定义函数来创建对象
1 | function fn(key1, key2) { |
通过构造函数创建对象(ES5)
如果一个函数,最终的目的是通过
new关键字创建对象,那么这个函数被称为构造函数- 构造函数中只需要直接定义属性和方法即可,不需要创建对象和返回对象,实质上和通过工厂函数创建对象原理相同
- 为了与普通函数区分,通常将构造函数的函数名首字母大写
new关键字在创建对象时,自动执行了以下步骤- 在内存中创建一个新的空对象
- 对象的隐式原型指向构造函数的显式原型
- 对象的this指向这个对象
- 执行构造函数内的代码来添加属性和方法,并定义属性初始值
- 自动返回这个新对象
通过Object类构造函数创建对象
1 | var obj = new Object(); |
通过自定义构造函数创建对象
1 | function Fn(key1, key2) { |
通过类创建对象(ES6)
- 类名首字母大写
- 类中必须通过
this关键字调用当前类实例的属性或方法 new关键字在创建对象时,自动执行了以下步骤- 在内存中创建一个新的空对象
- 对象的隐式原型指向构造函数的显式原型
- 对象的this指向这个对象
- 执行构造方法内的代码来添加属性,并定义属性初始值
- 自动返回这个新对象
constructor()方法为构造方法- 构造方法通过形参为对象初始化属性值
- 如果不写构造方法,类中会有默认的构造方法
- JS中的构造方法只能有一个,不能定义多个,因为JS中没有方法的重载
1 | class Cls { |
通过Object的静态方法create()创建对象(ES5)
null:定义对象指向的原型对象
1 | var obj = Object.create(null); |
实例属性和实例方法
实例属性
构造函数中定义实例属性(ES5)
- 构造函数中通过
this关键字定义实例属性
1 | function Fn(key1, key2) { |
类中定义实例属性(ES6)
- 类中通过构造方法,并通过
this关键字定义实例属性
1 | class Cls { |
实例属性的操作
新增或修改属性
- 如果属性名不存在,则新增属性并赋值
- 如果属性名已存在,则修改该属性的值
1 | obj.key = "value"; |
删除属性
1 | delete obj.key; |
获取属性值
1 | var result = obj.key; |
判断属性是否存在
1 | var exist = "key" in obj; |
实例方法
构造函数中定义实例方法(ES5)
- 在构造函数中定义的函数是实例方法,这种实例方法可以在对象中直接访问,这种实例方法在每次对象创建时都会创建
1 | function Fn(key1, key2) { |
类定义实例方法(ES6)
- 在类中定义的函数是实例方法,这种实例方法可以在对象中直接访问
- 在类中定义方法时不需要使用
function关键字修饰,方法之间不需要添加,分隔
1 | class Cls { |
静态属性和静态方法
构造函数中定义静态属性和静态方法(ES5)
1 | function Fn() {} |
类定义静态方法(ES6)
- 类中静态方法的this指向类本身
1 | class Cls { |
对象的属性描述符
为单个属性定义描述符
1 | var obj = { key: "value" }; |
1 | var obj = { _key: "value" }; |
为多个属性定义描述符
1 | var obj = { key1: "value", key2: "value" }; |
获取指定属性的描述符
1 | var obj = { key: "value" }; |
获取所有属性的描述符
1 | var obj = { key1: "value", key2: "value" }; |
不可添加属性
1 | var obj = { key: "value" }; |
密封对象
- 不可添加和删除属性
- 实质上是
preventExtensions()和configurable: false的组合
- 实质上是
1 | var obj = { key: "value" }; |
冻结对象
- 不可添加、删除、修改属性
- 实质上是
seal()和writable: false的组合
- 实质上是
1 | var obj = { key: "value" }; |
继承
- 既可以继承自定义的类,也可以继承内置类
原型与原型链(ES5)
显式原型与隐式原型
- 每当构造函数被定义,默认有
prototype属性prototype属性指向一个新对象,这个新对象就是原型对象- 原型对象中有默认有
constructor属性,这个属性默认指向构造函数 - 构造函数的原型对象是显式原型
- 显示原型可以通过
prototype属性来访问
- 每当通过构造函数创建了一个对象,默认有
[[Prototype]]内部属性[[Prototype]]内部属性指向对应构造函数的显示原型- 对象的原型对象是隐式原型
- JS中函数也是对象,所有构造函数也有隐式原型,其隐式原型指向的是Object的显式原型
- 隐式原型可以通过
Object.getPrototypeOf()和Object.setPrototypeOf()来访问,部分浏览器也可以使用__proto__属性来访问,但是并不是所有浏览器都有这个属性
- 显示原型与隐式原型完全相等,指向的是同一个对象
获取构造函数的显式原型
1 | function Fn() {} |
修改构造函数的显式原型
1 | function Fn() {} |
获取对象的隐式原型
1 | function Fn() {} |
修改对象的隐式原型
1 | function Fn() {} |
原型链
Object是所有类的父类,Object的显示原型是所有函数的显示原型的上级,Object的显示原型没有上级
每当访问对象的属性或方法时
- 如果被访问的属性或方法在对象中存在,则直接访问对象的属性或方法
- 如果被访问的属性或方法在对象中不存在,则访问对象隐式原型的属性或方法
- 如果被访问的属性或方法在对象的隐式原型中也不存在,则访问Object的显示原型
- 如果被访问的属性或方法在Object的显示原型中也不存在,则报错
对象通过原型链访问上级属性
1 | function Fn() {} |
对象通过原型链访问上级方法
1 | function Fn() {} |
通过原型定义实例方法(ES5)
- 在构造函数的显式原型中定义的函数是实例方法,这种实例方法可以在对象中直接访问,这种实例方法在每次对象创建时不会重复创建
1 | function Fn() {} |
通过原型链实现继承(ES5)
1 | function Father() {} |
通过类实现继承(ES6)
- ES6中通过类实现继承是一种语法糖,本质底层还是通过原型链实现的继承
1 | class Father {} |
super
继承构造方法
- 子类中通过
super()调用父类构造方法,super()方法必须写在子类构造方法的最前面,因为super()方法需要在使用this之前使用
1 | class Father { |
继承普通方法
- 子类通过
super关键字调用父类的普通方法
1 | class Father { |
继承静态方法
- 子类通过
super关键字调用父类的静态方法
1 | class Father { |
方法的重写
- 子类重写与父类同名的方法
1 | class Father { |
判断属性是否是对象的自身属性
1 | function Fn() { |
判断属性是否是对象的自身属性或父级属性
- 通过
in关键字,根据属性名,判断属性是否是对象的自身属性或父级属性
1 | function Fn() {} |
遍历自身属性和父级属性
1 | function Fn() {} |
判断构造函数的显示原型是否出现在指定实例对象的原型链上
- 通过
instanceof关键字,根据构造函数,判断构造函数的显示原型是否出现在指定实例对象的原型链上
1 | function Fn() {} |
判断对象是否出现在指定实例对象原型链上
- 通过
isPrototypeOf()函数,根据对象,判断对象是否出现在指定实例对象原型链上
1 | function Fn() {} |
访问器(ES6)
通过
getter()方法和setter()方法定义对象的访问器- 每当属性值被获取时,会自动调用
getter()方法 - 每当属性值被修改时,会自动调用
setter()方法
- 每当属性值被获取时,会自动调用
访问器操作的属性可以未定义
类的访问器方法
1 | class Cls { |
对象的访问器方法
直接在对象中定义访问器
1 | var obj = { |
通过属性修饰符定义访问器
1 | var obj = { |
遍历对象的属性
获取对象的所有属性名
- 返回对象的所有属性名数组
1 | var obj = { key: "value" }; |
根据对象的所有属性名遍历对象的所有属性值
for…i
1 | var obj = { key: "value" }; |
for…in
1 | for (var key in obj) { |
包装类型
JS为了能为基本类型添加属性和方法,为常见基本类型定义了对应的包装类型,如:
String、Number、Boolean、Symbol、Bigintnull、undefined没有包装类型当通过原始类型数据调用属性或方法时,浏览器自动执行了以下步骤
- 根据原始类型数据值,创建一个包装类型对象
- 调用包装类型的属性或方法,返回一个新的值
- 为了优化包装类型的创建和销毁,浏览器可能会进行优化,对于原始类型数据调用某些包装类型的方法或函数时,浏览器可能不会创建对象而是直接返回结果
- 销毁刚刚创建的包装类型对象
包装类型创建的值与字面量创建的值
- 通过包装类型创建的值,通过
typeof得到的数据类型为对象类型
1 | var value = new String(""); |
- 通过字面量创建的值,通过
typeof得到的数据类型为基本类型
1 | var value = ""; |