面向对象编程

目录

为什么要使用面向对象

对象是什么

生成对象

 JavaScript_new命令

JavaScript_Prototype原型

 构造函数的缺点

prototype 原型的作用

JavaScript_实例_静态属性和方法

实例方法和静态方法

实例属性和静态属性

JavaScript_proto__属性

 JavaScript_constructor属性

JavaScript_原型链

JavaScript_instanceof运算符

JavaScript_Object对象的相关方法

Object.getPrototypeOf()

Object.setPrototypeOf()

Object.create()

Object.getOwnPropertyNames()

Object.prototype.hasOwnProperty()

JavaScript_对象的继承

JavaScript_多重继承

JavaScript_严格模式

设计目的

启用方法

显式报错


面向对象编程(Object Oriented Programming,缩写为 OOP)是目前主流的编程范式

为什么要使用面向对象

《大话设计模式》中大鸟给小菜讲的故事非常经典:

“话说三国时期,曹操带领百万大军攻打东吴,大军在长江赤壁驻扎,军船连成一片,眼看就要灭掉东吴,统一天下,曹操大悦,于是大宴众文武,在酒席间,曹操诗性大发,不觉吟道:‘喝酒唱歌,人生真爽……’众文武齐呼:‘丞相好诗!’于是一臣子速命印刷工匠刻版印刷,以便流传天下。”

“样张出来给曹操一看,曹操感觉不妥,说道:‘喝与唱,此话过俗,应改为‘对酒当歌’较好!’于是此臣就命工匠重新来过。工匠眼看连夜刻版之工,彻底白费,心中叫苦不迭。只得照办。”

“样张再次出来请曹操过目,曹操细细一品,觉得还是不好,说:‘人生真爽‘太过直接,应改问语才够意境,因此应改为‘对酒当歌,人生几何……’当臣子转告工匠之时,工匠晕倒……”

大鸟:“小菜你说,这里面问题出在哪里?”

小菜:“是不是因为三国时期活字印刷还未发明,所以要改字的时候,就必须要整个刻板全部重新刻。”

大鸟:“说得好!如果是有了活字印刷,则只需更改四个字就可,其余工作都未白做。岂不妙哉。

一、要改,只需更改要改之字,此为可维护

二、这些字并非用完这次就无用,完全可以在后来的印刷中重复使用,此乃可复用

三、此诗若要加字,只需另刻字加入即可,这是可扩展

四、字的排列其实可能是竖排可能是横排,此时只需将活字移动就可做到满足排列需求,此是灵活性好。”

“而在活字印刷术出现之前,上面的四种特性都无法满足,要修改,必须重刻,要加字,必须重刻,要重新排列,必须重刻,印完这本书后,此版已无任何可再利用价值。”

小菜:“是的,小时候我一直奇怪,为何火药、指南针、造纸术都是从无到有,从未知到发现的伟大发明,而活字印刷仅仅是从刻版印刷到活字印刷的一次技术上的进步,为何不是评印刷术为四大发明之一呢?原来活字印刷是思想的成功,面向对象的胜利。”

对象是什么

要了解面向对象编程,要先了解对象是什么?

对象其实是一个抽象概念的具体实例,例如:

  1. 人类:张三
  2. 动物:猫

抽象概念:人类、动物 特别常见称呼:类、模板

对象:张三、猫 特别常见称呼:实例对象

生成对象

对象是一个抽象概念的具体实例,那么我们要生成一个对象,需要有一个模板,表示某一类实物的共同特征,然后对象根据这个模板生成。

JavaScript 语言使用构造函数(constructor)作为对象的模板。所谓”构造函数”,就是专门用来生成实例对象的函数。

构造函数就是一个普通的函数,但具有自己的特征和用法

function People(){
    this.name = "张三"
}

温馨提示

构造函数的首字母大写:例如People中的P是大写的

构造函数的特点有两个

  1. 函数体内部使用了this关键字,代表了所要生成的对象实例
  2. 生成对象的时候,必须使用new命令
function People(){
    this.name = "张三"
}

var p = new People();
p.name // 张三

 JavaScript_new命令

new命令的作用,就是执行构造函数,返回一个实例对象

使用new命令时,根据需要,构造函数也可以接受参数

this关键字

在构造函数中的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. 为什么要使用面向对象
                    1. 易维护
                    2. 可复用
                    3. 可扩展
                    4. 灵活性
                
                2. 面向对象的代码编写
                    1. 理解面向对象的模板(构造函数)
                    2. 通过模板生成一个实例对象(new关键字)

        */

        /*
            this关键字:
                1. 事件中this指向DOM元素
                2. 闭包中的this指向window
                3. 定时器中的this指向window
                4. 对象中的this指向调用者
                5. 改变this指向:call\apply\bind
                6. 构造函数中的this,指向当前实例对象
        */


        function Person(name,age){
            this.name = name;
            this.age = age;
        }

        var zhangsan = new Person("张三",20);
        var lisi = new Person("李四",22);

        console.log(zhangsan);
        console.log(lisi);

    </script>
    
</body>
</html>

JavaScript_Prototype原型

 构造函数的缺点

JavaScript 通过构造函数生成新对象,因此构造函数可以视为对象的模板。实例对象的属性和方法,可以定义在构造函数内部

function People(name,age) {
    // 属性
    this.name = name;
    this.age = age;
    // 方法;
    this.sayHello = function(){
        console.log("Hello");
    }
}

var p1 = new People("张三",20);
var p2 = new People("李四",30);

通过构造函数为实例对象定义属性和方法,虽然很方便,但是有一个缺点。同一个构造函数的多个实例之间,无法共享属性和方法,从而造成对系统资源的浪费

console.log(p1.sayHello === p2.sayHello); // false

这个问题的解决方法,就是 JavaScript 的原型对象(prototype)

prototype 原型的作用

prototype原型对象的所有属性和方法,都能被实例对象共享。也就是说,如果属性和方法定义在原型上,那么所有实例对象就能共享,节省了内存。

怎么为对象指定原型呢,JavaScript 规定,每个函数都有一个prototype属性,指向一个对象

function People() { }
console.log(People.prototype);

对于普通函数来说,该属性基本无用。但是,对于构造函数来说,生成实例的时候,该属性会自动成为实例对象的原型。

function People(name,age) {
    // 属性
    this.name = name;
    this.age = age;
}

// 原型属性
People.prototype.color = "黄种人";
// 原型方法
People.prototype.sayHello = function(){
    console.log("Hello");
}

var p1 = new People("张三",20);
var p2 = new People("李四",30);
console.log(p1.sayHello === p2.sayHello); // true
console.log(p1.color === p2.color); // true

p1p2共享了原型属性和方法

原型对象的属性不是实例对象自身的属性。只要修改原型对象,变动就立刻会体现在所有实例对象上。

People.prototype.color = "白种人";
p1.color  // 白种人
p2.color  // 白种人

如果实例对象自身就有某个属性或方法,它就不会再去原型对象寻找这个属性或方法

p1.color = "白种人"
console.log(p1.color); // "白种人"

总结一下,原型对象的作用,就是定义所有实例对象共享的属性和方法。

JavaScript_实例_静态属性和方法

实例方法和静态方法

在JavaScript中有静态方法和实例方法,静态方法是函数自己定义的,而实例方法是通过原型来定义。它们的区别是静态方法是可以直接用类名.方法名去调用的,而实例方法是不可以的,它必须要用实例才可以去调用实例.方法名

function Person(name){
    this.name = name;
}

// 实例方法
Person.prototype.getName = function(){
    console.log(this.name);
}

// 静态方法
Person.getAge = function(age){
    console.log(age);
}

var person = new Person("itbaizhan");
person.getName();
Person.getAge(10);

JavaScript内置的Array方法则分为实例方法和静态方法

var arr = [10,20,30];
// 实例方法
arr.push(40);
// 静态方法
Array.isArray(arr);

实例属性和静态属性

实例属性和静态属性与方法类似,实例属性是通过实例对象调用的实例对象.属性,静态属性是通过类名调用的类名.属性

function Person(name){
    // 实例属性
    this.name = name;
}

// 静态属性 
Person.age = 20;

var person = new Person("itbaizhan");
console.log(person.name);
console.log(Person.age);

JavaScript_proto__属性

实例对象__proto__属性(前后各两个下划线),返回该对象的原型。该属性可读写

function People(){}
var p = new People();

console.log(p.__proto__);

对象的prototype属性等同于实例对象的__proto__属性

function People(){}
var p = new People();
console.log(p.__proto__ === People.prototype); // true

温馨提示

根据语言标准,__proto__属性只有浏览器才需要部署,其他环境可以没有这个属性

 JavaScript_constructor属性

prototype对象有一个constructor属性,默认指向prototype对象所在的构造函数

function Person(){}
console.log(Person.prototype.constructor === Person);  // true

 constructor属性的作用是什么呢

通俗的讲,就是为了将实例原型对象暴露出来, 比如你写了一个插件,别人得到的都是你实例化后的对象, 如果别人想扩展下对象,就可以用 constructor.prototype 去修改或扩展原型对象

  
    <script>

        // function Person(name){
        //     this.name = name
        // }


        // // 获得到原型
        // console.log(Person.prototype.constructor);

        var person;
        (function(){

            function Person(){
                this.name = "张三";
                this.age = 20;
            }

            Person.prototype.getName = function(){
                console.log(this.name);
            }

            person = new Person();

        })();


        person.getName()
        // 问题:现在这个库缺少了一个方法,getAge的方法

        // Person.prototype.getAge = function(){

        // }

        person.constructor.prototype.getAge = function(){
            console.log(this.age);
        }

        person.getAge();

    </script>

JavaScript_原型链

JavaScript 规定,所有对象都有自己的原型对象(prototype)。一方面,任何一个对象,都可以充当其他对象的原型;另一方面,由于原型对象也是对象,所以它也有自己的原型。因此,就会形成一个“原型链”(prototype chain):对象到原型,再到原型的原型...

如果一层层地上溯,所有对象的原型最终都可以上溯到Object.prototype,即Object构造函数的prototype属性。也就是说,所有对象都继承了Object.prototype的属性。这就是所有对象都有valueOftoString方法的原因,因为这是从Object.prototype继承的。

function Person(){}
console.log(Person.prototype.__proto__);
console.log(Object.prototype);

那么,Object.prototype对象有没有它的原型呢?回答是Object.prototype的原型是nullnull没有任何属性和方法,也没有自己的原型。因此,原型链的尽头就是null

console.log(Object.prototype.__proto__); // null

读取对象的某个属性时,JavaScript 引擎先寻找对象本身的属性,如果找不到,就到它的原型去找,如果还是找不到,就到原型的原型去找。如果直到最顶层的Object.prototype还是找不到,则返回undefined。如果对象自身和它的原型,都定义了一个同名属性,那么优先读取对象自身的属性,这叫做“覆盖”(overriding)

function Person(){}

Person.prototype.toString = function(){
    return "这是Person的toString方法"
}

var person = new Person();
person.toString()

JavaScript_instanceof运算符

instanceof运算符返回一个布尔值,表示对象是否为某个构造函数的实例

function Person(){}
var person = new Person();
console.log(person instanceof  Person);

由于instanceof检查整个原型链,因此同一个实例对象,可能会对多个构造函数都返回true

var d = new Date();
d instanceof Date // true
d instanceof Object // true

instanceof运算符的一个用处,是判断值的类型

var arr = [10,20,30];
var obj = {};
console.log(arr instanceof Array);
console.log(obj instanceof Object);

温馨提示

instanceof运算符只能用于对象,不适用原始类型的值。

var str = 'itbaizhan';
str instanceof String // false
 <script>

        function Person(){}

        var p = new Person();

        var p1 = [];

        console.log(p instanceof  Person); // true:当前p是Person的实例对象
        console.log(p1 instanceof Person); // false:p1不是Person的实例对象

        /*
            typeof:检测基本数据类型:number、string、boolean
            isArray:检测数组:true,false
            instanceof:检测对象:true,false
        */

        // intanceof会检测整个原型链
        console.log(p instanceof Object); // true
        console.log(p1 instanceof Object); // true

        // var num = 10;// instanceof:只能检测对象
        // console.log(num instanceof Number); //false

        var arr = [];
        console.log(arr instanceof Array);


    </script>

JavaScript_Object对象的相关方法

Object.getPrototypeOf()

Object.getPrototypeOf方法返回参数对象的原型。这是获取原型对象的标准方法

<script>

        function Person(){}

        Person.prototype.getName = function(){}

        var p = new Person()

        // 获取实例对象的原型对象
        console.log(Object.getPrototypeOf(p));

        console.log(Person.prototype);

        // 不推荐使用
        console.log(p.__proto__);


    </script>

 

Object.setPrototypeOf()

Object.setPrototypeOf方法为参数对象设置原型,返回该参数对象。它接受两个参数,第一个是现有对象,第二个是原型对象

  <script>

        var sxt = {};
        var itbaizhan = {
            teacher:"老师",
            getAge:function(){
                console.log(13);
            }
        }

        Object.setPrototypeOf(sxt,itbaizhan);

        sxt.getAge()
        console.log(sxt.teacher);


    </script>

 

Object.create()

JavaScript 提供了Object.create()方法,让一个对象继承另一个对象的属性和方法

var sxt = {
    teacher: function () {
        console.log('hello');
    }
};
var itbaizhan = Object.create(sxt);
itbaizhan.teacher() // hello

 Object.create()方法生成的新对象,动态继承了原型。在原型上添加或修改任何方法,会立刻反映在新对象之上

var sxt = { t: 10 };
var itbaizhan = Object.create(sxt);
sxt.t = 20;
itbaizhan.t // 20

除了对象的原型,Object.create()方法还可以接受第二个参数。该参数是一个属性描述对象,它所描述的对象属性,会添加到实例对象,作为该对象自身的属性

var sxt = {
    java: {
        value: "全体系"
    },
    web: {
        value: '大前端'
    }
};

var itbaizhan = Object.create(sxt, {
    python:{//必须是对象形式
       value:"全方向"
    }
});

console.log(itbaizhan.java.value);
console.log(itbaizhan.python);//全方向  注意调用形式

 

Object.getOwnPropertyNames()

Object.getOwnPropertyNames方法返回一个数组,成员是参数对象本身的所有属性的键名,不包含继承的属性键名

Object.getOwnPropertyNames(Array)
// ['length', 'name', 'prototype', 'isArray', 'from', 'of']

 

Object.prototype.hasOwnProperty()

对象实例的hasOwnProperty方法返回一个布尔值,用于判断某个属性定义在对象自身,还是定义在原型链上

 <script>

        console.log(Object.getOwnPropertyNames(Array));

        console.log(Array.hasOwnProperty("length")); // true
        console.log(Array.hasOwnProperty("toString")); // false

    </script>

JavaScript_对象的继承

面向对象编程很重要的一个方面,就是对象的继承。A 对象通过继承 B 对象,就能直接拥有 B 对象的所有属性和方法。这对于代码的复用是非常有用的。

大部分面向对象的编程语言,都是通过“类”(class)实现对象的继承。传统上,JavaScript 语言的继承不通过 class,而是通过“原型对象”(prototype)实现

温馨提示

ES6版本已经提供了 class 语法

 <script>

        function Person(name,age){
            this.name = name;
            this.age = age;
        }

        Person.prototype.getName = function(){
            console.log(this.name);
        }

        Person.prototype.getAge = function(){
            console.log(this.age);
        }




        function Student(name,age,learn){
            // 继承属性:调用父类的构造函数
            Person.call(this,name,age);
            this.learn = learn;
        }
        // 继承父类的方法
        for(var p in Person.prototype){
            Student.prototype[p] = Person.prototype[p];
        }

        Student.prototype.getLearn = function(){
            console.log(this.learn);
        }

        // 就近原则
        Student.prototype.getName = function(){
            console.log("姓名:"+this.name);
        }

        var student = new Student("张三",19,"it");
        student.getAge()
        student.getName()
        student.getLearn()


    </script>

JavaScript_多重继承

 JavaScript 不提供多重继承功能,即不允许一个对象同时继承多个对象。但是,可以通过变通方法,实现这个功能

 <script>


        function Sxt(){
            this.hello = "hello"
        }

        Sxt.prototype.getHello = function(){
            console.log("Hello");
        }


        function Itbaizhan(){
            this.world = "world"
        }

        Itbaizhan.prototype.getWorld = function(){
            console.log("World");
        }


        function Sum(){
            Sxt.call(this);
            Itbaizhan.call(this);
        }
        // 继承Sxt
        // Sum.prototype = Object.create(Sxt.prototype);
        for(var p in Sxt.prototype){
            Sum.prototype[p] = Sxt.prototype[p];
        }
        // 继承Itbaizhan
        Object.assign(Sum.prototype,Itbaizhan.prototype);

        // 指定constructor
        Sum.prototype.constructor = Sum;

        console.log(Sum.prototype);

        var s = new Sum();
        console.log(s.hello);
        console.log(s.world);
        s.getHello();
        s.getWorld();

    </script>

JavaScript_严格模式

除了正常的运行模式,JavaScript 还有第二种运行模式:严格模式(strict mode)。顾名思义,这种模式采用更加严格的 JavaScript 语法。

同样的代码,在正常模式和严格模式中,可能会有不一样的运行结果。一些在正常模式下可以运行的语句,在严格模式下将不能运行。

设计目的

早期的 JavaScript 语言有很多设计不合理的地方,但是为了兼容以前的代码,又不能改变老的语法,只能不断添加新的语法,引导程序员使用新语法。

严格模式是从 ES5 进入标准的,主要目的有以下几个。

  • 明确禁止一些不合理、不严谨的语法,减少 JavaScript 语言的一些怪异行为。
  • 增加更多报错的场合,消除代码运行的一些不安全之处,保证代码运行的安全。
  • 提高编译器效率,增加运行速度。
  • 为未来新版本的 JavaScript 语法做好铺垫。

总之,严格模式体现了 JavaScript 更合理、更安全、更严谨的发展方向。

启用方法

进入严格模式的标志,是一行字符串use strict

'use strict';
  1. use strict放在脚本文件的第一行,整个脚本都将以严格模式运行
  2. use strict放在函数体的第一行,则整个函数以严格模式运行
<script>
  'use strict';
  console.log('这是严格模式');
</script>
function strict() {
  'use strict';
  return '这是严格模式';
}

显式报错

eval、arguments 不可用作标识名

严格模式下,使用eval或者arguments作为标识名,将会报错。下面的语句都会报错

'use strict';
var eval = 17;
var arguments = 17;

函数不能有重名的参数

正常模式下,如果函数有多个重名的参数,可以用arguments[i]读取。严格模式下,这属于语法错误

function f(a, a, b) {
  'use strict';
  return a + b;
}

全局变量显式声明

正常模式中,如果一个变量没有声明就赋值,默认是全局变量。严格模式禁止这种用法,全局变量必须显式声明

'use strict';

v = 1; // 报错,v未声明

for (i = 0; i < 2; i++) { // 报错,i 未声明
  // ...
}

function f() {
  x = 123;
}
f() // 报错,未声明就创建一个全局变量

禁止 this 关键字指向全局对象

禁止使用 with 语句

arguments 不再追踪参数的变化

保留字

为了向将来 JavaScript 的新版本过渡,严格模式新增了一些保留字(implements、interface、let、package、private、protected、public、static、yield等)。使用这些词作为变量名将会报错

function package(protected) { // 语法错误
  'use strict';
  var implements; // 语法错误
}

 选项卡(面向对象实现)

<!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>
        .tab input {
            background: #f1f1f1;
            border: 1ps solid #ff0000;
        }

        .tab .active {
            background: #e9f212;
        }

        .tab div {
            display: none;
            width: 300px;
            height: 250px;
            padding: 10px;
            background: #e9f212;
            border: 1px solid #ff0000;
        }
    </style>

    <script>

        function Tab(id) {
            var tabBox = document.getElementById(id);
            // 属性
            this.tabBtn = tabBox.getElementsByTagName("input");
            this.tabDiv = tabBox.getElementsByTagName("div");

            for (var i = 0; i < this.tabBtn.length; i++) {
                this.tabBtn[i].index = i;
                var _this = this;
                this.tabBtn[i].onclick = function () {
                    _this.clickBtn(this);
                }
            }
        }

        Tab.prototype.clickBtn = function (btn) {
            for (var j = 0; j < this.tabBtn.length; j++) {
                this.tabBtn[j].className = "";
                this.tabDiv[j].style.display = "none";
            }
            this.className = "active";
            this.tabDiv[btn.index].style.display = "block"
        }


        // window.onload = function () {
        //     new Tab("tabBox1")
        //     new Tab("tabBox2")
        // }


    </script>

    <script>

        // 每一个选项卡在加载之后,都知道这个选共享卡是干嘛,或者是输出专业
        function ItbaizhanTab(id,effect){
            Tab.call(this,id);
            this.effect = effect;
        }

        for(var t in Tab.prototype){
            ItbaizhanTab.prototype[t] = Tab.prototype[t] 
        }

        ItbaizhanTab.prototype.getEffect = function(){
            console.log(this.effect);
        }


        window.onload = function(){
            var t1 = new ItbaizhanTab("tabBox1","bz程序员主流专业");
            var t2 = new ItbaizhanTab("tabBox2","bz程序员参考");

            t1.getEffect();
            t2.getEffect();
        }

       


    </script>
</head>

<body>


    <div id="tabBox1" class="tab">
        <input type="button" value="web" class="active">
        <input type="button" value="Java">
        <input type="button" value="Python">
        <div style="display: block;">React、Vue</div>
        <div>SpringBoot、SpringMVC</div>
        <div>flask</div>
    </div>

    <div id="tabBox2" class="tab">
        <input type="button" value="书籍" class="active">
        <input type="button" value="视频">
        <input type="button" value="文档">
        <div style="display: block;">sxt出品java</div>
        <div>sxt视频,bz视频</div>
        <div>课堂笔记文档</div>
    </div>



</body>

</html>

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值