A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© Clover_ZhuWQ 初级黑马   /  2017-10-29 17:55  /  731 人查看  /  0 人回复  /   1 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 Clover_ZhuWQ 于 2017-10-29 19:01 编辑

第一章 Javascript面向对象(上)

本章主要对Javascript面向对象的用法做详细讲解。


1.1 什么是对象

    什么是对象?对象是对属性、方法的封装。每一个对象中,都是以属性名:属性值的形式存在。
    一言以蔽之:对象就是键值对的集合。

1.2   1.2  什么是面向对象
    这个问题很难回答,但这也是面试中最常见的问题。

    要搞清楚这个问题,必须弄清楚面向对象和面向过程的区别:

    面向过程:完成一件事情的所有步骤都亲力亲为,一步一步来完成。例如要吃饺子,就必须自己剁馅儿、和面、擀饺子皮儿、包饺子...
    面向对象:完成一件事情,直接去找能做这件事的人。例如吃饺子,直接找老婆(如果没有就去饺子馆吧)。


    面向过程:是事件的执行者。
    面向对象:是指挥者。


一言以蔽之:面向对象就是对面向过程的封装!

1.3  为什么要面向对象
    问这个问题,其实就是在问面向对象有什么好处,有哪些好处呢?

    1)  提高代码的复用性,简化开发
    2)  使代码结构清晰
    3)  在JS中有一个特殊的作用:防止代码污染


    什么是代码污染?请看下面的实例代码:

   
[HTML] 纯文本查看 复制代码
<!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>Title</title>
    </head>
    <body>
    <script>
    // 演示:代码污染
    // 引入第一个JS文件
    function sum(a,b) {
         return a + b; 
    }
    </script>
    <script>
     // 引入第二个JS文件
    // 定义一个变量sum
     var sum = 100;
    </script>

    <script>
    // 调用求和函数进行求和
   console.log(sum(12, 5));
   </script>
   </body>
   </html>


  如何解决代码污染?
  我们可以把定义的方法放到对象中,然后用对象调用自己的方法:
   
[HTML] 纯文本查看 复制代码
<!DOCTYPE html>
   <html lang="en">
   <head>
    <meta charset="UTF-8">
    <title>Title</title>
    </head>
   <body>
    <script>
   // 演示:代码污染
    // 引入第一个JS文件
   // 在对象里面定义一个求和函数
   var itcast = {
        sum : function(a,b) {
         return a + b;
       }
   };
   </script>
  <script>
  // 引入第二个JS文件
   // 定义一个变量sum,因为sum函数已经变成了对象的属性
   var sum = 100;
   </script>

   <script>
    // 调用求和函数进行求和
   console.log(itcast.sum(12, 5));
   </script>
  </body>
   </html>

1.4  创建对象的两种方式

    需求:创建一个人,要求有姓名、年龄、性别、sayHello方法

    1.4.1     对象的直接量   
l  做法:
       在定义对象的时候,直接使用JSON格式来定义一个JS对象,并且为这个对象赋值属性和方法,称为对象的直接量(字面量)
       这种方式创建的对象,默认是Object类型的对象

l  案例代码:
   
[JavaScript] 纯文本查看 复制代码
    <script>
        // 演示:通过对象的直接量的形式来创建对象
        // 需求:创建一个人,要求有姓名、年龄、性别、sayHello方法
        // JSON结构
        var p = {
            // 属性
            name : 'Jack',
            age : 38,
            gender : '妖',
            // 方法
            sayHello : function() {
                // this 当前对象
                console.log('Hello, I am: ' + this.name);
            }
        };

        // 访问属性以及方法
        console.log(p.name);
        console.log(p.age);
        console.log(p.gender);

        p.sayHello();
    </script>

l   效果
            
l  缺陷
n  只能使用1次,无法重复利用,每次都要编写相同代码创建新对象
l  使用场景
n  对象只需要一次使用的时候。比如作为传递参数;
n  自己封装JS框架(函数)的时候。

1.4.2     构造函数创建对象
l  构造函数创建对象的一般步骤:
1)        通过function指定一个构造函数,与普通函数没差别,只是约定首字母大写以区分
2)        然后在构造函数中通过this.属性名=属性值来完成对象属性初始化。
3)        最后通过new关键字创建对象(实例)
l  案例代码:
[JavaScript] 纯文本查看 复制代码
    <script>
        // 使用构造函数的形式创建对象
        // 1. 定义一个构造函数(类),构造函数其实就是普通的函数,构造函数首字母大写
        function Person(name,age,gender) {
            // 2. 在构造函数里面使用this.属性名=属性值的形式来初始化属性
            this.name = name;
            this.age = age;
            this.gender = gender;
            this.sayHello = function() {
                console.log('Hello, I am : ' + this.name);
            }
        }
        // 3. 使用new关键字来创建对象(实例)
        var p = new Person('Jack',38,'妖');
        console.log(p.name);
        p.sayHello();

        var p2 = new Person('Rose',18,'female');
        console.log(p2.name);
        console.log(p2.age);
        console.log(p2.gender);
        p2.sayHello();

        console.log(p.name == p2.name); // false
        console.log(p.sayHello == p2.sayHello); // false
    </script>
l  效果:
   

问题:对象中的函数是否相同?
测试:
[JavaScript] 纯文本查看 复制代码
        // 两个对象中的sayHello方法是否相等
        console.log(p.sayHello == p2.sayHello); // false

结果:
     
      事实上,查看内存中对象的属性,发现p1p2中各有一份 sayHello 方法:   
结论:
       通过上面的实验我们发现,对象中的所有属性,包括定义的方法,都是每个对象独有的,不会共享。
属性不共享可以理解,但是方法都是一样的,如果也不共享,那么就是对内存的极大浪费。

l  优势:
u  对象可以重复创建
l  缺点:
u  对象中相同的方法,无法实现共享,内存占用高

1.1   1.5  对象的原型(prototype

1.5.1     原型的概念
l  什么是prototype ?
在JS中,每一个构造函数都会有一个prototype(也是一个对象)属性,任何由同一个构造函数创建的对象,都会共享这个prototype对象,并且可以具备这个prototype的所有属性(成员)。

   
   
1.5.2     原型的基本使用
l   没有设置原型的时候
一开始,我们定义一个空的构造函数,并且只给出了name 属性,尝试打印对象的 age属性,结果是undefined。因为没有该属性
案例代码:

      
[AppleScript] 纯文本查看 复制代码
    <script>
        // 演示:原型的基本使用
        // 定义构造函数
        function Person(name) {
            this.name = name;
        }

        var p = new Person('Jack');
        console.log(p.name);
        console.log(p.age);

        var p2 = new Person('Rose');
        console.log(p2.name);
        console.log(p2.age);
    </script>


结果:
打印控制台中是undefined的。
      

查看对象属性也是只有name属性,没有age:

l   使用原型给构造函数添加新的属性

在对象定义后,使用prototype添加新属性:
[JavaScript] 纯文本查看 复制代码
    <script>
        // 演示:原型的基本使用
        // 定义构造函数
        function Person(name) {
            this.name = name;
        }

        // 通过原型去添加一个共享属性
        Person.prototype.age = 20;

        var p = new Person('Jack');
        console.log(p.name);
        console.log(p.age);

        var p2 = new Person('Rose');
        console.log(p2.name);
        console.log(p2.age);
    </script>

打印控制台已经有结果了:


此时查看对象的属性:

发现p1和p2对象中虽然依然没有age,但是构造函数Person的prototype中已经有age了,又因为对象会共享prototype的成员,所以每个对象都可以使用age属性了!

l   给对象从原型中继承的属性赋值

尝试修改对象中,继承自prototype的属性值age:

      
[JavaScript] 纯文本查看 复制代码
        // 尝试给从对象继承过来的属性赋值
        p.age = 30;
        // 我们发现p.age 是新值,而p2还是原来的值
        console.log(p.age);  // 30
        console.log(p2.age); // 20


       结果:p1age已经变为30 p2没变
         

         问题,既然是共享的属性,那么为什么p1修改了,p2值没变?
         查看内存中的状态:
   

我们发现:prototype中的age没有改变,而是在对象p1中新增了自己的age属性,并且赋值为30

结论:当原型和对象自身都有相同属性时,对象中的属性优先!

l   总结
1)        当我们访问对象中的属性时,默认会先在当前对象中查找;如果在当前对象找不到,就会去对象的prototype中查找
a)        事实上,prototype也是对象,因此在prototype对象中找不到,会到prototype对象的prototype中找,逐级向上形成“原型链”,原型链的最顶级的是Object.prototype
b)       那么Object.prototype也是对象,再向上还有吗?没有了,再向上是null。Object.prototype就是原型链的根
2)        修改对象的属性,不会修改prototype中的属性,而是在对象中创建新的属性

3)        某些浏览器支持__proto__属性,与prototype是同一个东西,但是某些浏览器还不支持。所以一般不要用

      
1.5.3     通过原型改造构造函数
   为了减少对象内存占用,我们一般创建构造函数的时候,会把经常变化的属性在构造函数中声明,每个对象都有自己的属性。然后把公用的不变化的东西(通常是函数)通过原型来添加。
  
[JavaScript] 纯文本查看 复制代码
    <script>
        // 通过原型改造构造函数
        // 1. 定义一个构造函数(类),构造函数其实就是普通的函数,构造函数首字母大写
        function Person(name,age,gender) {
            // 2. 在构造函数里面使用this.属性名=属性值的形式来初始化属性
            this.name = name;
            this.age = age;
            this.gender = gender;
        }
        // 通过原型去添加一个共享方法
        Person.prototype.sayHello = function() {
            console.log('Hello, I am :' + this.name);
        };

        // 3. 使用new关键字来创建对象(实例)
        var p = new Person('Jack',38,'妖');
        console.log(p.name);
        p.sayHello();

        var p2 = new Person('Rose',18,'female');
        console.log(p2.name);
        console.log(p2.age);
        console.log(p2.gender);
        p2.sayHello();

        console.log(p.name == p2.name); // false
        console.log(p.sayHello == p2.sayHello); // false
    </script>


       验证结果:
   
每个对象都可以调用sayHello方法,证明共享了方法。 各个对象的sayHello方法判断是相等的。证明内存中只有一份。

总结构造函数创建对象的方法:
1、 构造函数里面写变化属性。
2、 原型上去添加方法。



1.5.4    通过原型扩展JS原生对象
事实上不仅仅我们自己定义的对象有原型,JS中原生的对象也有原型。我们可以通过原生对象的原型对原生的JS进行扩展!

原生的JS中,Array对象并没有sum方法,尝试调用报错:

   
[JavaScript] 纯文本查看 复制代码
    // 使用原型来扩展原生JS对象
    var arr = [10,20,30];
    // 直接调用arr.sum方法,报错
    console.log(arr.sum()); // sum is not a function


      

       我们使用原型进行扩展:
   
[JavaScript] 纯文本查看 复制代码
    <script>
        // 使用原型来扩展原生JS对象
        var arr = [10,20,30];
        // console.log(arr.sum());

        // 封装一个对数组求和的方法
        function sum(arr) {
            // 求和
            var res = 0;
            for (var i = 0; i < arr.length; i++) {
                res += arr[i];
            }
            return res;
        }
        console.log(sum(arr));
        // 不好 不够智能
        // 通过原型给数组添加一个sum的方法
        Array.prototype.sum = Array.prototype.sum || function() {
            var res = 0;
            for (var i = 0; i < this.length; i++) {
                res += this[i];
            }
            return res;
        };
        console.log(arr.sum());
        console.log(arr2.sum());

    </script>


结果:






更多图片 小图 大图
组图打开中,请稍候......

0 个回复

您需要登录后才可以回帖 登录 | 加入黑马