JS面向对象的三大特征

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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面向对象的三大特征的主要内容,如果未能解决你的问题,请参考以下文章

面向对象三大特征是啥?

“面向对象”的三大特征是啥,各自的定义是啥?

java面向对象的三大特征?

Java面向对象三大特征之继承和多态

谈面向对象的三大特征

7 面向对象的三大特征