首页 > 代码库 > JS面向对象的三大特征
JS面向对象的三大特征
封装:
把对象属性隐藏在构造函数内部,不让外部程序(实例对象)直接访问,而是通过构造函数提供的方法来实现属性的访问和操作!
1 function Person(name, age) {
2 //把对象属性隐藏在构造函数内部
3 var pname = name; //李四
4 var page = age; //22
5 //提供对应的方法实现对属性的获取和设置
6 //定义一个公有方法,用来实现对pname的获取
7 this.getPname = function() {
8 return pname;
9 }
10 this.setPname = function(val) {
11 pname = val
12 }
13
14 }
15 var person1 = new Person("李四", 22);
16 //person1对象不能直接访问pname和page
17 //通过方法来设置属性的值
18 person1.setPname("王五");
19 //通过方法来获取属性的值
20 alert(person1.getPname());
继承:
1、子对象复制父对象的属性和方法
2、构造函数相关联实现继承
一、子对象复制父对象的属性和方法
1、浅拷贝,把父对象的属性和方法拷贝到子对象中
使用浅拷贝,如果改变子对象的基本数据类型,不会改变父对象的;但是如果赋值父对象的属性是一个对象或数组的时候,那么子对象方法改变,父对象也会发生改变;
1 //我们把person对象作为父对象
2 var person = {
3 language: "汉语",
4 run: function() {
5 alert("奔跑行为");
6 },
7 address: {
8 home: "home address",
9 office: "office address"
10 }
11 }
12 //把child作为子对象
13 var child = {
14 name: "张三",
15 age: 12
16 }
17 //可以通过for in循环拿到父对象的属性名和方法名
18 function extend(p, c) {
19 //p表示父对象 c表示子对象
20 for(var x in p) {
21 //把父对象的书写给子对象
22 //x表示父对象的每个属性
23 c[x] = p[x];
24 }
25 }
26 //继承之后,子对象和父对象的属性和方法不应该相互干扰
27 extend(person, child);
28 document.write("浅拷贝之后的子对象的书写和方法:<br>");
29 for(var x in child) {
30 document.write(x + " " + child[x] + "<br>");
31 }
32 document.write("<hr>");
33 //改变子对象的值
34 child.name = "李四";
35 child.language = "新的语言";
36 document.write("改变子对象属性之后:<br>");
37 for(var x in child) {
38 document.write(x + " " + child[x] + "<br>");
39 }
40 document.write("<hr>");
41 for(var x in person) {
42 document.write(x + " " + person[x] + "<br>");
43 }
44 document.write("<hr>");
45 document.write("<hr>");
46 //把子对象的address的值改变
47 child.address.home = "new home address";
48 child.address.office = "new office address";
49 document.write(child.address.home + " / " + child.address.office + "<br>");
50 document.write(person.address.home + " / " + person.address.office + "<br>");
2、深拷贝
深拷贝就是在浅拷贝的基础上的递归调用;是真正意义上实现对数组和对象的拷贝,如果拷贝的属性值是对象或数组,要递归调用,然后创建空对象或空数组;
1 /*
2 * 浅拷贝: 把父对象的属性和方法一一拷贝到子对象中,但是如果子对象拷贝父对象的属性是对象或数组的时候,
3 * 当我改变子对象拷贝过来的数组或对象的时候,父对象的数组和对象也跟着发生了改变
4 * 深拷贝: 深拷贝就是在浅拷贝的基础上的递归调用;是真正意义上实现对数组和对象的拷贝
5 * */
6 //我们把person对象作为父对象
7 var person = {
8 language: "汉语",
9 run: function() {
10 alert("奔跑行为");
11 },
12 address: {
13 home: "home address",
14 office: "office address"
15 },
16 hobby: ["足球", "篮球", "羽毛球"]
17 }
18 //把child作为子对象
19 var child = {
20 name: "张三",
21 age: 12
22 }
23
24 function extendDeely(p, c) {
25 //在拷贝父对象属性的时候,如果属性值是对象或数组,则另外的方式处理
26 for(var x in p) {
27 if(typeof p[x] === "object") {
28 //当属性是数组或对象的时候
29 //当我父对象属性的值是数组或对象的时候,那么就让子对象创建一个与之对应类型的属性
30 //比如我们拷贝address 让子对象的属性 = {} c.address = {}
31 //如果属性值是数组 创建一个[]
32 //如果属性值是对象 创建一个{}
33 // var arr1 = [1,2,3]; //Array构造构造函数的原型 Array
34 c[x] = (p[x].__proto__.constructor === Array) ? [] : {};
35 // c[x] 子属性 c[address] c[hobby]
36 // p[x] p[address] p[hobby]
37 extendDeely(p[x], c[x]);
38 } else {
39 c[x] = p[x];
40 }
41 }
42 }
43 extendDeely(person, child);
44 document.write("子对象拷贝过来以后:<br>");
45 document.write(child.address.home + " / " + child.address.office + " / " + child.hobby + "<br>");
46
47 child.address.home = "新的家庭住址";
48 child.address.office = "新的办公住址";
49 child.hobby[0] = "xxx";
50 document.write("子对象改变属性以后:<br>");
51 document.write(child.address.home + " / " + child.address.office + " / " + child.hobby + "<br>");
52 document.write("<hr>");
53 document.write("父对象的值:<br>");
54 document.write(person.address.home + " / " + person.address.office + " / " + person.hobby + "<br>");
3、构造函数绑定
在自构造函数中调用父构造函数,重新制定父构造函数中this的执行
call 或 apply 改变函数内部的执行上下文,简单一句话就是改变this的指向;
1 //父构造函数
2 function Person() {
3 this.language = "中文";
4 this.address = {
5 home: "home address",
6 office: "office address"
7 };
8 this.hobby = ["足球", "篮球", "羽毛球"]
9 }
10 //子构造函数
11 function Child() {
12 //在子构造函数中调用父构造函数
13 //call方法里面的this表示通过当前构造函数创建的实例对象
14 //this是Child构造函数创建的对象
15 Person.apply(this, arguments);
16 this.name = "张三";
17 this.age = 12
18 }
19 var c1 = new Child();
20 var p1 = new Person();
21
22 c1.language = "新的语言";
23 c1.address.home = "新的家庭住址";
24 c1.address.office = "新的办公地址";
25 c1.hobby[0] = "xxxxx";
26
27 for(x in c1) {
28 document.write(x + " " + c1[x] + "<br>");
29 }
30 document.write("<hr>");
31 for(x in p1) {
32 document.write(x + " " + p1[x] + "<br>");
33 }
二、构造函数相关联实现继承
1、子构造函数的prototype属性指向父构造函数的实例对象实现继承
1 //父构造函数
2 function Person() {
3 this.language = "中文";
4 this.address = {
5 home: "home address",
6 office: "office address"
7 };
8 this.hobby = ["足球", "篮球", "羽毛球"]
9 }
10 //子构造函数
11 function Child(name, age) {
12 this.name = name;
13 this.age = age;
14 }
15
16 //1.子构造函数的prototype属性指向父构造函数的实例对象实现继承
17 //把子构造函数完全指向 父构造函数,完全把Child.prototype给替换掉
18 Child.prototype = new Person();
19 //Child.prototype.constructor -->Person()
20 Child.prototype.constructor = Child;
21 //子对象是否能继承父对象的属性和方法
22 var c1 = new Child("张三", 12);
23 var p1 = new Person();
24 document.write("c1子对象的属性和方法:<br>");
25 for(var x in c1) {
26 document.write(x + " / " + c1[x] + "<br>");
27 }
28 document.write("<hr>");
29 c1.address.home = "新的家庭住址";
30 c1.hobby[0] = "xxxxx";
31 document.write("子对象的值:" + c1.address.home + " / " + c1.hobby + "<br>");
32 document.write("父子对象的值:" + p1.address.home + " / " + p1.hobby + "<br>");
/*
* 使用这种方式,子对象可以继承父对象属性和方法,而且子对象不会影响父对象
* 缺点:
* 不影响我创建对象,影响原型链的使用,因为我改变了子对象的构造函数原型对象,那么子对象构造函数原型对象就不执行Child构造函数,
* 而是执行Person
* 解决方法:
* 构造函数矫正/重新指向
* */
2、把子构造函数的prototype属性直接指向父构造函数的对象
子构造函数的原型对象 指向 父构造函数的原型对象
1 //2、把子构造函数的prototype属性直接指向父构造函数的对象
2 function Person() {
3
4 }
5 //把属性和方法放到原型对象中
6 Person.prototype.language = "中文";
7 Person.prototype.address = {
8 home: "home address",
9 office: "office address"
10 };
11 Person.prototype.hobby = ["足球", "篮球", "羽毛球"]
12 //子构造函数
13 function Child(name, age) {
14 this.name = name;
15 this.age = age;
16 }
17 //子构造函数的原型对象 指向 父构造函数的原型对象
18 Child.prototype = Person.prototype;
19 Child.prototype.constructor = Child;
20 //子对象是否能继承父对象的属性和方法
21 var c1 = new Child("张三", 12);
22 var p1 = new Person();
23 document.write("c1子对象的属性和方法:<br>");
24 for(var x in c1) {
25 document.write(x + " / " + c1[x] + "<br>");
26 }
27 //子对象是否影响父对象
28 document.write("<hr>");
29 c1.address.home = "新的家庭住址";
30 c1.hobby[0] = "xxxxx";
31 document.write("子对象的值:" + c1.address.home + " / " + c1.hobby + "<br>");
32 document.write("父子对象的值:" + p1.address.home + " / " + p1.hobby + "<br>");
33 /*
34 * 使用这种方式,子对象可以继承父对象属性和方法
35 * 缺点:
36 * 修改子对象会影响到父对象
37 * 子构造函数原型的constructor指向了父构造函数
38 *
39 * 解决:
40 * s构造函数矫正/重新指向
41 * */
混合模式
使用空对象作为中介
要实现继承: 需要拿到构造函数的属性和方法,还要拿到原型里面的属性和方法,而且子不能对父产生影响
实现思路:
1.在子构造函数中调用父构造函数
2.创建空构造函数
3.空构造原型指向父构造函数原型
4.子构造函数原型指向空构造函数实例对象
优点:
1.无论是构造函数里面的定义的还是原型中定义的,都可以获取到
2.修改子不影响父
1 //属性定义在函数中,方法定义在原型中
2 //父构造函数及原型
3 function Person() {
4 this.test = "test";
5 this.language = "汉语";
6 this.address = {
7 home: "home address",
8 office: "office address"
9 };
10 this.hobby = ["足球", "篮球"];
11 }
12 Person.prototype.xx = "xxxxx";
13 Person.prototype.run = function() {
14 alert("runrunrun");
15 }
16 //子构造函数
17 function Child(name, age) {
18 Person.call(this, arguments);
19 this.name = name;
20 this.age = age;
21 }
22
23 function F() {} ;//空构造函数
24 F.prototype = Person.prototype; //空构造函数原型指向 父构造函数原型
25 Child.prototype = new F(); //子构造函数原型 指向 空对象
26 Child.prototype.constructor = Child;
27 var c1 = new Child("zhangsan", 12);
28 var p1 = new Person();
29 c1.language = "新的语言";
30 c1.address.home = "新的家庭住址";
31 c1.xx = "new xxxxxx";
32 for(var x in c1) {
33 document.write(x + " " + c1[x] + "<br>");
34 }
35 document.write("<hr>");
36 for(var x in p1) {
37 document.write(x + " " + p1[x] + "<br>");
38 }
39 document.write(c1.address.home + " / " + p1.address.home);
40 /*
41 * 1.在子构造函数中,调用父构造函数(构造函数绑定)
42 * 2.使用空构造函数作为中介
43 * */
简洁方式:
直接使用Object.create()来实现
1.构造函数绑定
2.子构造函数原型 = Object.create(父构造函数原型)
1 function Person() {
2 this.test = "test";
3 this.language = "汉语";
4 this.address = {
5 home: "home address",
6 office: "office address"
7 };
8 this.hobby = ["足球", "篮球"];
9 }
10 Person.prototype.xx = "xxxxx";
11 Person.prototype.run = function() {
12 alert("runrunrun");
13 }
14 //子构造函数
15 function Child(name, age) {
16 Person.call(this, arguments);
17 this.name = name;
18 this.age = age;
19 }
20 Child.prototype = Object.create(Person.prototype);
21 var c1 = new Child("zhangsan", 12);
22 var p1 = new Person();
23 c1.language = "新的语言";
24 c1.address.home = "新的家庭住址";
25 c1.xx = "new xxxxxx";
26
27 for(var x in c1) {
28 document.write(x + " " + c1[x] + "<br>");
29 }
30 document.write("<hr>");
31 for(var x in p1) {
32 document.write(x + " " + p1[x] + "<br>");
33 }
34
35 document.write(c1.address.home + " / " + p1.address.home);
JS面向对象的三大特征