JS面向对象编程笔记(完):Object方法与模块化
# 开头
笔记视频内容源自B站JavaScript从入门到放弃 第十二章 面向对象编程 (opens new window),笔记为自行整理复习使用,欢迎一同学习交流,转载请通知原作者
# 四、Object方法
# 4.1、Object相关方法介绍
1、Object对象本身的方法
Object.keys()
Object.getOwnPropertyNames()
Object.getPrototypeOf()
Object.setPrototypeOf()
Object.create()
其他方法...
2、Object的实例方法
<body>
<script>
Object.prototype.syaHello = function () {
console.log("hello");
};
var obj = new Object();
obj.syaHello();
</script>
</body>
2
3
4
5
6
7
8
9
# 4.2、Object的静态方法
Object.keys()
:参数是一个对象,返回是一个数组,返回可枚举的属性
var arr = ['a','b','c'];
console.log(Object.keys(arr));
var obj = {
0:'e',
1:'f',
2:'g'
}
console.log(Object.keys(obj));
2
3
4
5
6
7
8
Object.getOwnPropertyNames()
:接收一个对象作为参数,返回了一个数组,包含了该对象自身的所有属性名,能将不可枚举的属性遍历
var arr = ['a','b','c'];
for(var key in arr) {
console.log(key);
}
console.log(Object.getOwnPropertyNames(arr));
var obj = {
0:'e',
1:'f',
2:'g'
}
console.log(Object.getOwnPropertyNames(obj));
2
3
4
5
6
7
8
9
10
11
Object.getPrototypeOf()
:参数是该对象,返回该对象的原型,也是获取原型对象的标准方法
function Fn() {}
var f1 = new Fn();
console.log(Object.getPrototypeOf(f1) === Fn.prototype);
//介绍几种特殊对象的原型
//空对象的原型是Object.prototype
console.log(Object.getPrototypeOf({}) === Object.prototype);
//Object的原型是null
console.log(Object.getPrototypeOf(Object.prototype) === null);
//函数的原型是Function
function f(){}
console.log(Object.getPrototypeOf(f) === Function.prototype);
2
3
4
5
6
7
8
9
10
11
Object.setPrototypeOf()
:接收两个参数,第一个是现有对象,第二个是原型对象
var a = {};
var b = {x:1};
Object.setPrototypeOf(a,b);
console.log(a.x);
console.log(Object.getPrototypeOf(a));
2
3
4
5
也可以使用该方法来实现new
的原理:
function F(){
this.foo = 'foo'
}
// var f = new F();
var f = Object.setPrototypeOf({},F.prototype);
F.call(f)
console.log(f);
console.log(f.foo);
2
3
4
5
6
7
8
Object.create()
:一个实例对象生成另一个实例对象,接收一个对象作为参数,然后以它为原型返回一个实例对象
var A = {
hello: function () {
console.log("hello");
}
};
var B = Object.create(A);
console.log(Object.getPrototypeOf(B));
B.hello()
2
3
4
5
6
7
8
Object.prototype.propertyIsEnumerable()
:判断某个属性是否可以遍历,只能判断实例对象本身的属性,对于继承来的属性和设置不可枚举的属性一律返回false
可枚举:可以遍历的属性
var obj = {};
obj.a = 123;
console.log(obj.propertyIsEnumerable('a'));
console.log(obj.propertyIsEnumerable('toString'));
for (var key in obj) {
console.log(obj[key]);
}
var arr = [1,2,3]
console.log(arr.propertyIsEnumerable('length'));
2
3
4
5
6
7
8
9
10
# 4.3、Object实例方法
Object.prototype.valueOf()
:返回当前对象的值,默认情况下返回对象本身
var obj = new Object();
//通过自定义Object.valueOf覆盖掉Object.prototype.valueOf()
obj.valueOf = function () {
return 2;
};
console.log(obj.valueOf() === obj);
console.log(1 + obj); //1[object Object]
2
3
4
5
6
7
Object.prototype.toString()
:返回一个对象的字符串形式,默认返回类型字符串
var obj2 = { a: 1 };
obj2.toString = function(){
return 'hello'
}
//如果没有定制toString方法就会是[object Object]
console.log(obj2.toString());
//自定义toString方法
console.log(obj2.toString()+' world');
var arr = [1,2,3]
console.log(arr.toString());
console.log('123'.toString());
console.log(
function () {
return 123;
}.toString()
);
console.log((new Date().toString()));
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Object.prototype.toLocaleString()
:针对地区做了一些定制
console.log((new Date().toLocaleString()));
Object.prototype.isPrototypeOf()
:用来判断该对象是否是另一个对象的原型
var o1 = {};
var o2 = Object.create(o1);
var o3 = Object.create(o2);
console.log(o2.isPrototypeOf(o3));
console.log(o1.isPrototypeOf(o3));
console.log(o3);
console.log(Object.prototype.isPrototypeOf({}));
console.log(Object.prototype.isPrototypeOf([]));
console.log(Object.prototype.isPrototypeOf(Object.create(null)));
console.log(Object.prototype.__proto__);
2
3
4
5
6
7
8
9
10
11
12
Object.prototype.hasOwnProperty()
:接收一个字符串作为参数,返回一个布尔值,表示该实例对象自身是否具有该属性,与继承来的属性无关,继承来的属性返回为false
console.log(obj.hasOwnProperty("a")); //true
console.log(obj.hasOwnProperty("b")); //false
console.log(obj.hasOwnProperty("toString")); //false
2
3
属性描述对象:一个对象内部的数据结构
Object.getOwnPropertyDescriptor()
:获取属性描述对象的默认配置,第一个参数是目标对象,第二个参数是一个字符型(目标对象的属性名)
Object.prototype.propertyIsEnumerable()
:查看某个对象的属性是否能被枚举(静态方法)
<body>
<script>
//属性描述对象:一个对象内部的数据结构
var obj = {};
obj.name = "chen";
obj.name = "chen2";
// console.log(obj.propertyIsEnumerable('name'));
// delete obj.name;
console.log(obj.propertyIsEnumerable('name'));
console.log(obj.name);
console.log(Object.getOwnPropertyDescriptor(obj,'name'));
/*
{
value:chen2,
writable:true,
enumerable:true,
configurable:true,
set:undefined,
get:undefined
}
*/
</script>
</body>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<body>
<script>
var arr = [1,2,3]
arr.length = 5
console.log(arr['length']);
console.log(delete arr.length);//false
console.log(Object.getOwnPropertyDescriptor(arr,'0'))
console.log(Object.getOwnPropertyDescriptor(arr,'length'))
console.log(Object.getOwnPropertyDescriptor(arr,'toString'))//不能用于继承的属性,只能用于对象自身的属性
</script>
</body>
2
3
4
5
6
7
8
9
10
11
Object.defineProperty()
:可以定义某个对象内部的相关属性,内部的参数分别为(属性所在的对象,属性名,属性描述对象)
<body>
<script>
//Object.defineProperty(属性所在的对象,属性名,属性描述对象)
var obj = Object.defineProperty({},'name',{
value:'chen',
writable:false,//可写性
enumerable:true,//可枚举性
configurable:false//是否能被删除
})
console.log(obj.name);
obj.name = 'chen2'
console.log(obj.name);//不可写
for(var key in obj){
console.log(obj[key]);
}
delete obj.name//配置不可删
console.log(obj);
</script>
</body>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Object.defineProperties()
:定义某个对象的内部属性
<body>
<script>
var obj = Object.defineProperties(
{},
{
p1: {
value: 123,
enumerable: true,
},
p2: {
value: "chen",
enumerable: false,
},
p3: {
// value:'hello',会报错,不能与get同时存在
get: function () {
return this.p1 + this.p2;
},
enumerable: true,
configurable: true,
},
}
);
console.log(obj);
console.log(obj.p1);
console.log(obj.p2);
console.log(obj.p3);
//一旦定义了取值函数get就不能同时定义value属性,否则会报错
var obj2 = Object.defineProperty({}, "p", {
// value:'123', value和get不能共存
get: function () {
return 456;
},
});
console.log(obj2.p);
//一旦定义了取值函数get就不能同时定义writable
var obj3 = Object.defineProperty({}, "a", {
// writable:true,
get: function () {
return 456;
},
});
console.log(obj3.a);
var obj4 = Object.defineProperty({}, "foo", {});
console.log(Object.getOwnPropertyDescriptor(obj4,'foo'));
for(var key in obj4){
console.log(obj4[key]);//无法被遍历
}
</script>
</body>
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# 4.4、属性描述对象中相关属性介绍
value
:属性的值
<body>
<script>
var obj = {};
obj.p = 123;
console.log(Object.getOwnPropertyDescriptor(obj, "p"));
Object.defineProperty(obj,'p',{value:456})
console.log(obj.p);
</script>
</body>
2
3
4
5
6
7
8
9
writable
:可写属性
<body>
<script>
"use strict";
//value
var obj = {};
obj.p = 123;
// console.log(Object.getOwnPropertyDescriptor(obj, "p"));
// Object.defineProperty(obj, "p", { value: 456 });
// console.log(obj.p);
//writable
Object.defineProperty(obj, "p", {
value: 890,
writable: false, //不可改变
});
console.log(obj.p);
// obj.p = 100;
console.log(obj.p); //没有变化,严格模式下甚至会报错
var pro = Object.defineProperty({}, "foo", {
value: "a",
writable: false,
});
var obj2 = Object.create(pro);
// console.log(obj2.foo);
// obj2.foo = 'b'
// console.log(obj2.foo);//严格模式下会报错
Object.defineProperty(obj2, "foo", {
value: "b", //覆盖原型对象的属性描述对象
});
console.log(obj2.foo);
</script>
</body>
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
28
29
30
31
32
enumrable
:可遍历性
如果enumrable
设置了false,通常以下三个操作不会取到该属性:
for...in
Object.keys()
JSON.stringify()
<body>
<script>
//enumrable可遍历性
//如果enumrable设置了false,通常以下三个操作不会取到该属性
//for...in
//Object.keys()
//JSON.stringify()
var obj = {};
Object.defineProperty(obj, "foo", {
value: 123,
enumerable: false,
});
console.log(obj.foo);
for (var key in obj) {
console.log(key); //无法获取
}
console.log(Object.keys(obj)); //[]
console.log(JSON.stringify(obj)); //{}
function A() {
this.name = "chen";
}
function B() {
A.call(this);
}
var b = new B();
Object.defineProperty(b, "age", {
value: 18,
enumerable: false,
});
console.log(b);
//注意:for...in循环可以遍历继承来的属性
for (var key in b) {
console.log(key);
}
//可以获取继承来的属性
console.log(Object.keys(b));
//getOwnPropertyNames可以获取继承来的和不可遍历的属性
console.log(Object.getOwnPropertyNames(b));
</script>
</body>
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
configurable
:可配置性,决定我们是否可以修改属性的描述对象
一旦设置configurable
为false,属性描述对象value
、writable
、enumrable
、configurable
都不能修改
<body>
<script>
//configurable 可配置性,决定我们是否可以修改属性的描述对象
//一旦设置configurable为false,属性描述对象value、writable、enumrable、configurable都不能修改
var obj = Object.defineProperty({}, "a", {
value: 1,
writable: false,
enumerable: false,
configurable: false,
});
//会1.报错:Cannot redefine property: a
// Object.defineProperty(obj, "a", {
// // value:2,
// // writable:true,
// // enumerable:true
// configurable: true,
// });
// console.log(Object.getOwnPropertyDescriptor(obj, "a"));
//2.注意:writable只要在false改为true的时候会静态失败,true改为false允许的
// var obj2 = Object.defineProperty({}, "p", {
// writable: true,
// configurable: false,
// });
// var obj2 = Object.defineProperty({}, "p", {
// writable: false,
// configurable: false,
// });
// obj2.p = 10;
// console.log(obj2);
// console.log(Object.getOwnPropertyDescriptor(obj2, "p"));
//3.value属性 只要writable和configurable有一个为true,就允许被修改
// var obj2 = Object.defineProperty({}, "p", {
// value: 1,
// writable: true,
// configurable: false,
// });
// var obj2 = Object.defineProperty({}, "p", {
// value: 10,
// });
// console.log(obj2);
// console.log(Object.getOwnPropertyDescriptor(obj2, "p"));
//configurable一旦被配置了true就可以摆删除,否则不能被删除
var obj = Object.defineProperties(
{},
{
a: {
value: 1,
configurable: true,
},
b: {
value: 2,
configurable: false,
},
}
);
delete obj.a;
delete obj.b;
console.log(obj);
</script>
</body>
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
存取器
:Object.defineProperty() get set
<body>
<script>
//Object.defineProperty() get set
var obj = Object.defineProperty({}, "p", {
get: function () {
return "getter";
},
set: function (value) {
// console.log('setter:'+value);
return; //设置无法赋值
},
});
console.log(obj.p);
obj.p = 123;
console.log(obj);
//简洁版
var obj2 = {
get p() {
return "getter";
},
set p(value) {
console.log("setter:" + value);
},
};
console.log(obj.p);
obj2.p = 123;
console.log(obj);
var obj3 = {
n:5,
get a(){
return this.n++;
},
set a(newValue){
if(newValue>this.n){
this.n = newValue
}else{
throw new Error('新的值必须大于当前的值')
}
}
}
console.log(obj3.a);
console.log(obj3.a);
obj3.a = 2;
console.log(obj3.a);
</script>
</body>
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# 4.5、浅拷贝
拷贝分为浅拷贝和深拷贝,操作拷贝之后的对象的数据的某个属性不会影响原始对象中的属性,这种拷贝就称为深拷贝。反之,有影响叫浅拷贝。
浅拷贝他不是直接赋值,浅拷贝新建了一个对象,将原来对象的属性都一一的赋值过来,赋值的是值,而不是引用,浅拷贝的赋值只复制了第一层的属性,并没有递归所有的值复制过来
<body>
<script>
//基本数据类型:按值来传递的
var a = 1;
var b = a;
b = 200;
console.log(b);
console.log(a);
//引用数据类型:按引用地址传递
var arr = [1,2,4];
var newArr = arr;
newArr.push(10);
console.log(arr);
console.log(newArr);
</script>
</body>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
可以看到数组两个值都发生了改变
<body>
<script>
//基本数据类型:按值来传递的
var a = 1;
var b = a;
b = 200;
console.log(b);
console.log(a);
//引用数据类型:按引用地址传递
var arr = [1, 2, 4];
var newArr = arr;
newArr.push(10);
console.log(arr);
console.log(newArr);
//拷贝分为浅拷贝和深拷贝
//操作拷贝之后的对象的数据的某个属性不会影响原始对象中的属性,这种拷贝就称为深拷贝
//反之,有影响叫浅拷贝
var obj = {
name: "chen",
age: 20,
hobby: "eat",
friend: {
name: "a",
age: 38,
},
};
function shadowCopy(toObj, fromObj) {
//来实现浅拷贝
for (var key in fromObj) {
toObj[key] = fromObj[key];
}
return toObj;
}
//浅拷贝他不是直接赋值,浅拷贝新建了一个对象,将原来对象的属性都一一的赋值过来,赋值的是值,而不是引用,浅拷贝的赋值只复制了第一层的属性,并没有递归所有的值复制过来
var newObj = shadowCopy({}, obj);
newObj.age = 30;
newObj.friend.name = "阿黄";
console.log(newObj);
console.log(obj); //friend中的name也一同改变了
</script>
</body>
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 4.6、深拷贝
深拷贝对目标对象的完全拷贝,不像浅拷贝那样只是复制了一层引用,就连值也复制过来
只要进行了深拷贝,它们老死不相往来,谁也不影响谁
<body>
<script>
//深拷贝对目标对象的完全拷贝,不像浅拷贝那样只是复制了一层引用,就连值也复制过来
//只要进行了深拷贝,它们老死不相往来,谁也不影响谁
var obj = {
name: "chen",
age: 20,
hobby: ["eat", "song"],
friend: {
name: "a",
age: 38,
hobby: "鸡汤",
friend: {
name: "阿黄",
age: 20,
hobby: "叫",
},
},
};
function deepCopy(to, from) {
//遍历from对象的所有属性,copy到to对象中
for (var key in from) {
//不便利原型链上的属性
// console.log(from.hasOwnProperty(key));
if (from.hasOwnProperty(key)) {
/*如果值是对象并且有值,再遍历对象*/
if (from[key] && typeof from[key] === "object") {
//区分是一般对象还是数组
to[key] = from[key].constructor === Array ? [] : {};
console.log(to[key]);
to[key] = deepCopy(to[key], from[key]);
} else {
//如果不是,直接赋值
to[key] = from[key];
}
}
}
return to;
}
var newObj = deepCopy({}, obj);
newObj.friend.name = "小红";
console.log(newObj);
console.log(obj);
</script>
</body>
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# 五、其他
# 5.1、前端模块化(ES5)
一个js文件就一个模块(业务逻辑封装)
module1.js
//这是字面量方式
// var module1 = new Object({
// count: 0,
// m1: function () {},
// m2: function () {},
// });
//IIFE立即执行函数
var module1 = (function () {
var count = 0;
var m1 = function () {console.log('m1');};
var m2 = function () {console.log('m2');};
return { m1: m1, m2: m2 };
})();
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<body>
<script src="js/module1.js"></script>
<script>
module1.m1();
module1.m2();
console.log(module1.count);//私有变量,undefined
</script>
</body>
2
3
4
5
6
7
8
# 5.2、放大模式和宽放大模式
使得模块更具有扩展性,宽放大模式还解决了依赖的顺序问题
module1.js
//IIFE立即执行函数
var module1 = (function () {
var count = 0;
var m1 = function () {
console.log("m1");
};
var m2 = function () {
console.log("m2");
};
return { m1: m1, m2: m2 };
})();
//放大模式
//宽放大模式
(function (mode) {
mode.m3 = function () {
console.log("m3");
};
return mode;
})(window.module1 || {});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 5.3、命名空间(ES5)
给当前变量对象造就自己的空间
**作用:**全局作用域下,避免变量污染:
namespace.js
//个人信息类
//姓名 性别
var namespace = (function(namespace){
//声明了一个顶层的命名空间
//个人信息类
namespace.PersonInfo = function(obj){
console.log(this);
obj = obj || {};
this.name = obj.name || ''
this.gender = obj.gender || '?'
}
//个人信息工具类
namespace.personInfoUtil = function(){
return {
//p形参是代指是哪个对象
show:function(p){
alert('姓名:'+p.name+",性别:"+p.gender)
}
}
}()
return namespace;
})(window.namespace || {})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
namespace_sub.js
namespace.sub = (function(sub){
//定义了一个子的命名空间
return sub;
})(window.namespace.sub || {})
2
3
4
<body>
<script src="js/namespace.js"></script>
<script src="js/namespace_sub.js"></script>
<script>
console.log(namespace);
var p1 = new namespace.PersonInfo({name:'chen',gender:'男'})
console.log(p1);
namespace.personInfoUtil.show(p1)
</script>
</body>
2
3
4
5
6
7
8
9
10
可以再进行完善一下:
namespace.js
//个人信息类
//姓名 性别
var namespace = (function (namespace) {
//声明了一个顶层的命名空间
//个人信息类:构造函数构建对象
namespace.PersonInfo = function (obj) {
console.log(this);
obj = obj || {};
this.name = obj.name || "";
this.gender = obj.gender || "?";
};
//这是称谓的方法
namespace.PersonInfo.prototype.getAppellation = function () {
var str = "";
if (this.gender === "男") {
str = "男士";
} else {
str = "女士";
}
return str;
};
//欢迎的方法
namespace.PersonInfo.prototype.getHello = function () {
var s = "Hello " + this.name + this.getAppellation();
return s
};
//个人信息工具类
namespace.personInfoUtil = (function () {
return {
//p形参是代指是哪个对象
show: function (p) {
alert("姓名:" + p.name + ",性别:" + p.gender);
},
};
})();
return namespace;
})(window.namespace || {});
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
28
29
30
31
32
33
34
35
36
37
38
namespace_sub.js
//动物园模块
namespace.sub = (function (sub) {
//定义了一个子的命名空间
//动物类
sub.Animal = function (name, color) {
this.name = name;
this.color = color;
};
sub.Animal.prototype.sleep = function () {
console.log("睡觉");
};
//猫类
sub.Cat = function (name, color) {
//继承属性
sub.Animal.call(this, name, color);
};
sub.Cat.prototype = Object.create(sub.Animal.prototype);
sub.Cat.prototype.constructor = sub.Animal;
return sub;
})(window.namespace.sub || {});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="js/namespace.js"></script>
<script src="js/namespace_sub.js"></script>
<script>
console.log(namespace);
var p1 = new namespace.PersonInfo({name:'chen',gender:'男'})
// console.log(p1);
var helloStr = p1.getHello()
console.log(helloStr);
// namespace.personInfoUtil.show(p1)
var c = new namespace.sub.Cat('小花','白色')
c.sleep();
console.log(c);
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24