面向对象与面向过程

面向对象:站在指挥者的角度(重点在执行的结果)

面向过程:站在执行者的角度(重点在执行过程)

面向对象不是替代面向过程,而是封装了面向过程

编程思想=面向对象+面向过程

面向对象的三大特征:

封装:用在对象封装

继承:在js中,是指对象之间的关系(比如父类与子类的继承关系:子类可以使用父类是的方法或数据)

多态:在js中没有多态的概念,在强类型语言中才有

强类型语言:强制类型定义的语言,一旦某一个变量被定义类型,如果不进行强制转换,则它永远就是该数据类型了,强类型语言包括Java、.net 、Python、C++等语言

弱类型语言:弱类型定义的语言,某一个变量被定义类型,该变量可以根据环境变化自动进行转换,不需要经过显性强制转换。弱类型语言包括vb 、PHP、javascript等语言

强弱类型语言之间的区别:是否会隐性的进行变量类型转变

弱类型语言速度比强类型快一些,但是强类型语言的严谨性能避免不必要的错误

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        div,
        p {
            height: 100px;
            width: 500px;
            background-color: #f99;
            margin: 10px;
        }
    </style>
</head>

<body>
    <div></div>
    <div></div>
    <div></div>
    <p></p>
    <p></p>
    <p></p>
    <p></p>
    <p></p>
    <script>
        // 面向过程的做法
        // 1.获取元素-div
        /*  var divs = document.querySelectorAll('div')
             // 2.for循环遍历,给每一个div添加边框
         for (let i = 0; i < divs.length; i++) {
             divs[i].style.border = '10px solid #ccc'
         }
         // 1.获取元素-p
         var ps = document.querySelectorAll('p')
             // 2.for循环遍历,给每一个div添加边框
         for (let i = 0; i < ps.length; i++) {
             ps[i].style.border = '10px solid #ccc'
         } */




        // 以上代码实现了功能,但是代码大量重复
        // 封装代码
        // 获取元素
        /* function getEle(ele) {
            return document.querySelectorAll(ele)
        }
        // 设置边框
        function setBorder(ele) {
            for (let i = 0; i < ele.length; i++) {
                ele[i].style.border = '10px solid #ccc'
            }
        }
        // 确定元素,添加样式
        setBorder(getEle('div'))
        setBorder(getEle('p')) */



        // 上面封装的函数解决了代码重复的问题,造成全局污染的问题
        // 面对对象的做法
        var tools = {
            // 获取元素
            getEle: function(ele) {
                return document.querySelectorAll(ele)
            },
            // 设置边框
            setBorder: function(ele) {
                    for (let i = 0; i < ele.length; i++) {
                        ele[i].style.border = '10px solid #ccc'
                    }
                }
                // ...设置字体大小,实现动画效果
        }
        tools.setBorder(getEle('div'))
        tools.setBorder(getEle('p'))
    </script>
</body>

</html>

创建对象的四种方式

1.Object创建对象(new Object) ——存在缺点:麻烦

2.对象字面量 ——简单些,存在的问题:不能批量的创建对象

3.工厂函数 (这是普通函数,不是构造函数)————可以批量创建对象,存在的问题:无法识别类型

4.自定义构造函数

构造函数:1.函数首字母大写(规范),2.必须要和new一起使用

给创建出来的对象添加属性和方法,通过this添加

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <!-- 创建对象的方式 -->
    <script>
        // 1.Object创建对象 ——存在缺点:麻烦
        /* var obj = new Object()
        obj.name = 'zs'
        obj.age = 20
        obj.sayHi = function() {
            console.log('hello');
        }
        console.log(obj.name, obj.age);
        obj.sayHi() */
        // 2.对象字面量 ——简单些,存在的问题:不能批量的创建对象
        /* var obj = {
            name: 'zs',
            age: 20,
            sex: '男',
            sayHi: function() {
                console.log('hello');
            }
        }
        console.log(obj.name, obj.age, obj.sex);
        obj.sayHi() */
        // 3.工厂函数 (这是普通函数,不是构造函数)————可以批量创建对象,存在的问题:无法识别类型
        /* function person(name, age, sex) {
            var obj = {
                name: name,
                age: age,
                sex: sex,
                sayHi: function() {
                    console.log('hello');
                }
            }
            return obj
        }
        var p1 = person('zs', 20, '男')
        p1.sayHi()
        console.log(p1);
        console.log(p1.name); */
        // 4.自定义构造函数
        // 构造函数:1.函数首字母大写(规范),2.必须要和new一起使用
        // 给创建出来的对象添加属性和方法,通过this添加
        function Person() {
            this.name = 'zs'
            this.age = 22
            this.sayHi = function() {
                console.log('hi');
            }
        }
        var p2 = new Person()
        p2.sayHi()
        console.log(p2);
    </script>

</body>

</html>

new 做了四件事

1.创建了一个空对象

2.将this指向创建的空对象

3.执行构造函数内的代码

4.把新创建的对象给返回出去

自定义构造函数后创建对象的问题

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        // 自定义构造函数创建对象的问题
        function Person(name, age) {
            this.name = name
            this.age = age
            this.sayHi = function() {
                console.log('hello');
            }
        }
        var p1 = new Person('李四', 20)
            /* p1.sayHi()
            console.log(p1.name, p1.age); */
        var p2 = new Person('张三', 22)
            /* p2.sayHi()
            console.log(p2.name, p2.age); */
            // 复杂数据类型的相等比较,比较的是地址,两个对象的sayHi方法是否是同一个函数
        console.log(p1.sayHi == p2.sayHi); //false,说明不是同一个地址,每个方法有各自的地址(各自占一份内存)
        // 经过上面的比较,得知构造函数创建对象存在的问题:浪费了内存,在内存中只需要一份sayHi方法即可
    </script>
</body>

</html>

构造函数创建对象存在的问题:浪费内存

术语:

1.实例(实例对象):通过构造函数创建出来的对象(实例可以有多个)

2.实例化:构造函数创建对象的过程

3.成员:对象中的属性和方法的统称

解决构造函数创建对象的问题

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        // 当成普通的函数,可以解决内存浪费的问题,但是又造成了全局污染的问题(方法名有可能会同名)
        function sayHi() {
            console.log('hello');
        }

        function run() {
            console.log('running');
        }
        // 使用对象来封装,减少全局污染的问题
        var obj = {
                sayHi: function() {
                    console.log("hello");
                },
                run: function() {
                    console.log('running');
                }
            }
            // 自定义构造函数
        function Person(name, age, sex) {
            this.name = name
            this.age = age
            this.sex = sex
            this.sayHi = obj.sayHi
            this.run = obj.run
        }
        var zs = new Person('zs', 20, '男')
        console.log(zs);
        var ls = new Person('ls', 26, '男')
        console.log(ls);
        console.log(zs.sayHi == ls.sayHi); //true,说明两个方法指向同一个地址(obj对象中的方法)
    </script>
</body>

</html>

小结:解决的思想:把构造函数中的方法提取到构造函数的外面,可以放在一个对象里面当成员,这样内存只会那有一份方法

原型

1.任何函数,都有prototype属性

2.函数的prototype属性值是一个对象,把这个对象叫做原型(原型对象)

3.原型的作用:通过构造函数创建的实例对象,可以直接访问构造函数的prototype属性上的任意成员

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>原型</title>
</head>

<body>
    <script>
        // 自定义构造函数
        function Person() {}
        // 给原型对象添加color属性
        Person.prototype.color = 'red'
            // 把sayHi方法添加到原型中
        Person.prototype.sayHi = function() {
            console.log('hello');
        }
        Person.prototype.run = function() {
            console.log('running');
        }
        console.log(Person.prototype);
        // p1是Person的实例对象
        var p1 = new Person()
        p1.sayHi()
        p1.run()
        console.log(p1.color);
        // console.log(p1);
        // p2也是Person的实例对象
        var p2 = new Person()
        p2.sayHi()
        p2.run()
        console.log(p2.color);
        // console.log(p2);
        //p1的sayHi和p2的sayHi方法是同一个方法,都是来源于Person的原型对象上的sayHi方法
        console.log(p1.sayHi == p2.sayHi); //true,说明指向同一个地址
    </script>
</body>

</html>
小结:

解决构造函数创建对象造成的内存浪费问题的最终解决方法:使用原型(prototype)

原型:函数的prototype属性

原型的作用:构造函数创建的实例对象,可以直接访问构造函数的prototype属性上的任意成员,解决了构造函数创建对象造成的内存浪费