04-OOP
# 1. 创建对象的两种方式
如何创建一个对象呢?
早期使用创建对象的方式最多的是使用Object类,并且使用new关键字来创建一个对象:这是因为早期很多JavaScript开发者是从Java过来的,它们也更习惯于Java中通过new的方式创建一个对象
后来很多开发者为了方便起见,都是直接通过字面量的形式来创建对象,这种形式看起来更加的简洁,并且对象和属性之间的内聚性也更强,所以这种方式后来就流行了起来
代码如下:
<script>
// 创建方式一:new Object创建
// Object 类 构造器 函数 函数也是类
// 函数有多种角色:1)普通函数 2)方法 3)对象 4)类
// 对象是属性的无序集合 操作集合 CRUD
let obj = new Object();
obj.name = "wc";
obj.age = 18;
obj.height = 1.88;
obj.running = function(){
console.log("running...");
}
console.log(obj.name);
console.log(obj.age);
obj.running();
// 创建方式二:通常字面量的形式
let obj2 = {
name:"xq",
age:20,
eat:function(){
console.log("eat...");
}
}
console.log(obj2.name);
obj2.eat();
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
- JS中内置的类,如下:
<script>
// Number是类,也叫构造器
// 通过new运算符,得到一个对象
// num1 百分之百是对象 var a = 110; a不是对象 a是number基本数据类型
// 引用数据类型存储在堆区 num1中存储了地址 指向那个堆
let num1 = new Number(110);
console.log(num1);
console.dir(num1); // dir是用来打印对象
</script>
2
3
4
5
6
7
8
9
<script>
// String是类 也叫构造器 构造函数
// let str1 = "hello"; // str1不是对象
// str1.toUppercase(); str1会包装成对象
let str = new String("hello");
console.log(str);
</script>
2
3
4
5
6
7
<script>
// let f = false; f不是对象
let b = new Boolean(true);
console.log(b);
</script>
2
3
4
5
<script>
let obj = new Object();
obj.name = "wc";
console.dir(obj);
</script>
2
3
4
5
<script>
let arr = new Array("wc","xq");
console.dir(arr);
</script>
2
3
4
- 补充一个运算符:instanceof 判断一个对象是否属于一个类,如下:
<script>
let obj = new Object();
obj.name = "wc";
// 判断obj是否是Object的对象
console.log(obj instanceof Object); // true
console.log(obj instanceof Number); // false
</script>
2
3
4
5
6
7
8
# 2. 一切都是对象
一切皆对象: 你需要证明,你在JS中的见到的所有的数据都是对象,这样,你才能说一切皆对象。
要证明,你见的所有数据都是对象:
- 所有的html元素都是对象
- 数组也是对象
- 函数也是对象
- 基本数据类型也可以是对象
- window也是对象 GO
- consolo也是对象
- {} 也是对象
- ...
2
3
4
5
6
7
8
- 代码如下:
<ul>
<li>html</li>
<li>css</li>
<li>js</li>
</ul>
<script>
// document.getElementsByTagName("li") 得到上面的三个li
// 得到的是一个伪数组 伪数组本质是对象
// 真数组:["wc","xq"]
// 真对象:{name:"wc"}
// 伪数组:{0:"wc"} {0:li,1:li,2:li}
let lis = document.getElementsByTagName("li")
console.log(lis);
console.dir(lis);
console.log("----------");
console.log(lis[0]);
console.log(lis[0] instanceof HTMLLIElement);
console.dir(lis[0]);
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script>
// 数组是对象
let arr = ["wc","xq"];
console.dir(arr);
arr.push("z3")
console.log(arr instanceof Array);
</script>
2
3
4
5
6
7
<script>
function gn(){
console.log("gn...");
}
gn.a = 1;
gn.b = 2;
gn.c = 3;
console.dir(gn);
gn.call(123)
console.log(gn instanceof Function);
</script>
2
3
4
5
6
7
8
9
10
11
<script>
let obj = {
name:"wc"
}
console.log(obj.name);
console.log(obj instanceof Object);
</script>
2
3
4
5
6
7
<script>
let num = 110; // num现在不是对象
console.log(num.toFixed(2)); // num在此处就包装了对象
let str = "hello";
console.log(str.toUpperCase());
</script>
2
3
4
5
6
7
<script>
window.alert("hello")
window.setTimeout(()=>{console.log("定时器~");},1000)
// console也是对象 log是这个对象中的方法
console.log("js");
// document也是对象 getElementById是方法
document.getElementById("box")
</script>
2
3
4
5
6
7
8
# 3. 对象是属性的无序集合
对象是属性的无序集合:
- 操作集合中的属性,说到操作,就是指"增删改查"。
- 基本操作代码如下:
<script>
let obj = {
name:"wc",
age:100,
run:function(){
console.log("run...");
}
}
// . 属性访问运算符
console.log(obj.name);
console.log(obj.age);
// [] 也可以访问对象中的属性
console.log(obj['name']);
console.log(obj['age']);
// 有些情况只能使用[]
// 对象中的属性名存储在一个变量中
let xxx = "name";
console.log(obj.xxx);
console.log(obj[xxx]);
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script>
// 对象中的键也可以是其它的数据类型
// 了解 正常情况下,键都是字符串类型
let obj = {
123:"wc",
true:18,
undefined:function(){
console.log("xxxx");
}
}
// console.log(obj.123); // 不行
console.log(obj[123]);
console.log(obj.true);
console.log(obj[true]);
obj.undefined();
obj[undefined]();
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<script>
let obj = {
name:"wc",
age:18,
jump:function(){
console.log("jump...");
}
}
// 访问一个对象上不存在的属性,得到的结果是und
// 数组这个对象上有length,对象上没有
console.log(obj.length); // undefined
// 能不能使用for循环去遍历对象
// for(let i=0; i<obj.length; i++){} // 不能
// 使用for in 可以遍历对象 key表示对象中的键
// 想获取值 不能obj.key obj[key]
for(let key in obj){
console.log(obj[key]);
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- 数组也是对象,遍历数组的方案,如下:
<script>
let arr = ["wc","xq","z3"];
// 遍历数组
// 问下面的代码中有没有块级作用域,有的话,有几个?
// 3个
for(let i=0; i<arr.length; i++){
console.log(arr[i]);
}
// forEach
arr.forEach(item=>console.log(item))
// for in
for(let key in arr){
// key是索引
console.log(arr[key]);
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<script>
let obj = {
name:"wc",
age:18
}
obj.name = "wc666"; // 修改obj中的属性
console.log(obj.name);
</script>
2
3
4
5
6
7
8
<script>
let obj = {
name:"wc",
age:18
}
// delete是一个运算符
delete obj.name;
// 访问一个对象上不存在的属性,结果是und
console.log(obj.name);
</script>
2
3
4
5
6
7
8
9
10
# 4. 对属性操作的控制
前面我们对对象中的属性做了一些基本的操作,现在,学习如何精细化操作对象中的属性。在前面我们的属性都是直接定义在对象内部,或者直接添加到对象内部的:
- 但是这样来做的时候我们就不能对这个属性进行一些限制:比如这个属性是否是可以通过delete删除的?这个属性是否在for-in遍历的时候被遍历出来呢?
如果我们想要对一个属性进行比较精准的操作控制,那么我们就可以使用属性描述符。
- 通过属性描述符可以精准的添加或修改对象的属性;
- 属性描述符需要使用 Object.defineProperty 来对属性进行添加或者修改;
- Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象;
可接收三个参数:
- obj要定义属性的对象;
- prop要定义或修改的属性的名称或 Symbol;
- descriptor要定义或修改的属性描述符;
返回值:
- 被传递给函数的对象。
属性描述符分类,属性描述符的类型有两种
- 数据属性(Data Properties)描述符(Descriptor)
- 存取属性(Accessor访问器 Properties)描述符(Descriptor)
# 4.1 数据属性描述符
数据数据描述符有如下四个特性:
[[Configurable]]:表示属性是否可以通过delete删除属性,是否可以修改它的特性,或者是否可以将它修改为存取属性描述符;
- 和数据属性描述符是一致的
- 当我们直接在一个对象上定义某个属性时,这个属性的[[Configurable]]为true;
- 当我们通过属性描述符定义一个属性时,这个属性的[[Configurable]]默认为false;
[[Enumerable]]:表示属性是否可以通过for-in或者Object.keys()返回该属性;
- 和数据属性描述符是一致的;
- 当我们直接在一个对象上定义某个属性时,这个属性的[[Enumerable]]为true;
- 当我们通过属性描述符定义一个属性时,这个属性的[[Enumerable]]默认为false;
[[Writable]]:表示是否可以修改属性的值;
- 当我们直接在一个对象上定义某个属性时,这个属性的[[Writable]]为true;
- 当我们通过属性描述符定义一个属性时,这个属性的[[Writable]]默认为false;
[[value]]:属性的value值,读取属性时会返回该值,修改属性时,会对其进行修改;
- 默认情况下这个值是undefined;
代码如下:
<script>
let obj = {
name:"wc",
age:18
}
// Object也是对象
// 第三个参数是属性描述符,对height这个属性进行描述
// 这样写,可以对height属性做精细化设置
Object.defineProperty(obj,"height",{
value:1.88
})
console.log(obj.height);
</script>
2
3
4
5
6
7
8
9
10
11
12
13
<script>
let obj = {
name:"wc",
age:18
}
// value 是给属性赋的值
// configurable:配置属性是否可以删除
Object.defineProperty(obj,"address",{
value:"BJ",
// configurable如果是false,表示此属性不能删除
configurable:false,
// enumerable表示属性是否可以被枚举 false表示不能枚举
enumerable: false,
// writable表示是否可以给属性重新赋值
writable:false
})
console.log(obj.address);
delete obj.address
console.log(obj.address);
for(let key in obj){
console.log(obj[key]);
}
obj.address = "SH";
console.log("----",obj.address);
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 4.2 存取属性描述符
数据数据描述符有如下四个特性
[[Configurable]]:表示属性是否可以通过delete删除属性,是否可以修改它的特性,或者是否可以将它修改为存取属性描述符;
- 和数据属性描述符是一致的;
- 当我们直接在一个对象上定义某个属性时,这个属性的[[Configurable]]为true;
- 当我们通过属性描述符定义一个属性时,这个属性的[[Configurable]]默认为false;
[[Enumerable]]:表示属性是否可以通过for-in或者Object.keys()返回该属性;
- 和数据属性描述符是一致的;
- 当我们直接在一个对象上定义某个属性时,这个属性的[[Enumerable]]为true;
- 当我们通过属性描述符定义一个属性时,这个属性的[[Enumerable]]默认为false;
[[get]]:获取属性时会执行的函数。默认为undefined
[[set]]:设置属性时会执行的函数。默认为undefined
代码如下:
<script>
let obj = {
name:"wc",
age:18,
// 行业潜规则:如果属性以_打头,表示私有属性
_address:"bj"
}
//
Object.defineProperty(obj,"address",{
enumerable:true, // 可以枚举
configurable:true, // 可以删除
get:function(){ // 获取器
// console.log("get...");
return this._address; // this表示obj
},
// 当给address设置新的值时会走设置器
set:function(value){ // 设置器 value就是设置的新值
// console.log("set...");
this._address = value;
}
})
// 当obj.address时,它会自动调用获取器
console.log(obj.address);
obj.address = "sh";
console.log(obj.address);
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 4.3 同时定义多个属性
- Object.defineProperties() 方法直接在一个对象上定义 多个 新的属性或修改现有属性,并且返回该对象。
<script>
let obj = {
_age:18,
}
Object.defineProperties(obj,{
name:{
value:"wc",
configurable:true,
enumerable:true,
writable:true
},
age:{
configurable:true,
enumerable:true,
get:function(){
return this._age;
},
set:function(value){
this._age = value;
}
}
})
console.log(obj.name);
console.log(obj.age);
obj.age = 20;
console.log(obj.age);
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<script>
let obj = {
_age:18,
// 在一个对象中,就可以直接写获取器和设置器
get age(){ // 获取器
return this._age;
},
set age(value){ // 设置器
this._age = value;
}
}
// 当obj.age时,会自动调用获取器
console.log(obj.age);
obj.age = 20;
console.log(obj.age);
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 4.4 获取属性描述符
获取对象的属性描述符:
- getOwnPropertyDescriptor
- getOwnPropertyDescriptors
<script>
let obj = {
_age: 18, // 不想让别人访问
}
Object.defineProperties(obj, {
name: {
value: "wc",
configurable: true,
enumerable: true,
writable: true
},
age: {
configurable: true,
enumerable: true,
get: function () {
return this._age;
},
set: function (value) {
this._age = value;
}
}
})
// 获取name属性的描述符
console.log(Object.getOwnPropertyDescriptor(obj,"name"));
// 获取对象的所有属性的描述符
console.log(Object.getOwnPropertyDescriptors(obj));
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 5. 字面量形式创建
现在要创建多个对象,有多个人,每个人都有自己的信息,此时,如何创建对象比较好?
- 字面量形式创建代码如下
<script>
let wc = {
name:"wangcai",
age:11,
height:140,
address:"bj",
eating:function(){ console.log("eating..."); },
runing:function(){ console.log("runing..."); }
}
let xq = {
name:"xiaoqiang",
age:12,
height:150,
address:"sh",
eating:function(){ console.log("eating..."); },
coding:function(){ console.log("coding..."); }
}
let z3 = {
name:"zhangsan",
age:128,
height:180,
address:"gz",
playing:function(){ console.log("playing..."); },
coding:function(){ console.log("coding..."); }
};
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
缺点:需要写大量的代码,并且多个代码都是重复的。
# 6. 工厂函数创建对象
- 使用工厂模式去创建对象,这是一种设计模式,这个工厂的主要作用主是产生我们需要的对象,代码如下:
<script>
// 工厂函数 目的:产生对象
function createPerson(name,age,height,address){
let p = {};
p.name = name;
p.age = age;
p.height = height;
p.address = address;
p.running = function(){ console.log(this.name + "在跑步~"); }
return p;
}
let wc = createPerson("wangcai",13,160,"bj"); wc.running();
let xq = createPerson("xiaoqiang",14,170,"gz"); xq.running();
let zs = createPerson("zhangsan",18,180,"sz"); zs.running();
console.log(wc instanceof Object);
console.log(xq instanceof Object);
console.log(zs instanceof Object);
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
对象和实例是同一个意思。一个对象是属于某个类的实例。
缺点:获取不到对象最真实的类型
# 7. 使用构造器创建对象
工厂函数创建对象不足:在打印出它的类型时,都是Object。我们想让每一个对象都有自己特有的类型,此时,就需要使用构造器来创建对象了。
构造器,是JS中特有的,类似一个类,也叫构造函数,英文叫constructor,需要通过new一个构造器,就可以产生一个对象。
new做了什么:
- 在构造器内部创建一个新的对象
- 这个对象内部的prototype属性会被赋值为该构造函数的prototype属性;
- 让构造器中的this指向这个对象
- 执行构造器中的代码
- 如果构造器没有返回对象,则返回上面的创建出来的对象
<script>
// 构造器,也就是类,首字母需要大写
function Fn(){
console.log("Fn...");
}
// Fn(); // 普通函数调用
// ()可加可不加
let wc = new Fn(); // obj对应的类是Fn
console.log(wc);
</script>
2
3
4
5
6
7
8
9
10
11
12
<script>
// 构造器
function Person(name,age,height,address){
this.name = name;
this.age = age;
this.height = height;
this.address = address;
this.runing = function(){ console.log(this.name + " running..."); }
}
let wc = new Person("wangcai",19,180,"bj"); wc.runing();
let xq = new Person("xiaoqiang",18,160,"gz"); xq.runing();
let zs = new Person("zhangsan",15,150,"sz"); zs.runing();
</script>
2
3
4
5
6
7
8
9
10
11
12
13
缺点:可能造成内存空间浪费。
上面创建对象的方案都有不足之处,有没有更加优雅的方案?
答:有