javascript(十 一)创建面向对象,什么是原型和原型链,面向对象的继承

面向对象

1.什么是面向对象

面向对象就是:把数据及对数据的操作方法放在一起,作为一个相互依存的整体——对象。对同类对象抽象出其共性,形成类。类中的大多数数据,只能用本类的方法进行处理。类通过一个简单的外部接口与外界发生关系,对象与对象之间通过消息进行通信。程序流程由用户在使用中决定。对象即为人对各种具体物体抽象后的一个概念,人们每天都要接触各种各样的对象,如手机就是一个对象。

2.面向对象

  • 在js中,有2种编程思维
    • 面向过程:
    • 面向对象:
  • 核心就是对象(类),对象的组成有2种 {“name”:ls, skill:function(){console.log(“敲代码”)} }
    • 属性:描述 静态的
    • 方法:行为 动态的
  • 面向对象三大特点
    • 封装
    • 继承
    • 多态

2.1创建面向对象

2.1.1字面量创建

缺点:创建单个

     var obj = {
            "name": "zs",
            "age": 23,
            "skill": function () {
                console.log("敲代码")
            }
        }
        console.log(obj);
        console.log(obj.age);//23
        obj.skill(); //敲代码
      
    </script>
2.1.2new关键字创建

缺点:代码冗余

 <script>
        // 创建张三的对象
        // 1.创建空对象
        var obj = new Object();
        console.log(obj);//{}
        // 2.添加属性和方法
        obj.name = "李四";
        obj.age = 20;
        obj.skill = function(){
            console.log("敲代码")
        }
        console.log(obj);
        obj.skill();
    </script>
2.1.3工厂模式创建

缺点:类别识别不明确,代码冗余 浪费内存

    <script>
        function createObj(name,age){ // name 张三 age 20
            //1.创建空对象
            var obj =   new Object();
            //2.添加属性和方法
            obj.name = name;
            obj.age = age;
            obj.skill = function(){
                console.log("敲代码")
            }
            // 3.出厂  设置返回值
            return obj;
        }
       var res1 = createObj("张三",20);
       console.log(res1);
       var res2 = createObj("李四",20);
       console.log(res2);
    </script>
2.1.4构造函数创建

缺点:浪费内存

如何创建构造函数

  • 构造函数的函数名首字母必须大写,为了和普通函数做区别
  • 方法和属性是直接给this
  • 必须使用new关键字进行调用 否则和普通函数没有区别

new操作符做了什么

  1. 隐式创建了一个空对象,让this指向这个空对象
  2. 执行构造函数中的代码
  3. 将实例化对象的__ proto __ 指向构造函数的prototype
  4. 隐式返回对象
 <script>
        // 创建构造函数
        function Person(name, age) {
            //new关键字 隐式创建了一个空对象 让this指向这个空对象
            //添加属性和方法  必须加给this
            this.name = name,
            this.age = age,
            this.skill = function () {
                    console.log("敲代码")
                }
            // new关键字 隐式返回这个对象
        }

        // 实例化对象(调用构造函数)  必须使用new关键字
        var res1 = new Person("张三", 20);
        console.log(res1);
	      var res2 = new Person("李四"10)</script>
2.1.5原型创建

缺点:不能传参

//创造构造函数
        function Person(){}
        // 将方法和属性添加到构造函数的prototype上 共享的属性和方法
        console.dir(Person);
        Person.prototype.name  = "张三";
        Person.prototype.age = 20;
        Person.prototype.skill = function(){
            console.log("敲代码")
        }
        console.dir(Person);

        // 实例化对象 
        var res1 =  new Person();
        console.log(res1);//实例化对象的__proto__指向构造函数的prototype
        console.log(res1.__proto__);

        var res2 = new Person();
        console.log(res2);
        console.log(res2.__proto__);
2.1.6混合创建

构造函数创建(传参)+原型创建(不传参)

 <script>
        /* name age传参  skill不传参 */
        // 创建Student构造函数
        function Student(name, age) {
            this.name = name
            this.age = age
        }
        // 原型创建   prototype
        Student.prototype.skill = function () {
            console.log("学习敲代码")
        }

        // 实例化对象 
        var res1 = new Student("张三", 20);
        console.log(res1);

        var res2 = new Student("李四", 15);
        console.log(res2);

        console.log(res1.name);//张三
        res.skill();//学习敲代码
        res.skill1();// 
    </script>

2.2什么是原型和原型链

  • 原型prototype:构造函数中用来存储共享的方法和属性的对象
  • 原型属性 __ proto __ :让实例化对象的__ proto__指向构造函数的prototype
        // Array是js内部创建好的构造函数
        console.dir(Array);
        // 实例化对象  实例化对象的__proto__指向构造函数的prototype
        var arr = new Array(1,2,3,4,5);
        console.log(arr);
        console.log(Array.prototype === arr.__proto__);//true

        console.dir(Array.prototype);//Array上共享的属性和方法


        var arr = new Array(1,2,3,4,5); //arr的__proto__ 指向Array的prototype
            
        var arr1 = new Array(4,5,6,6);// arr1的__proto__指向Array的prototype
  • 原型链:在创建构造函数和实例化对象的时候 自动形成一种查找关系

    先查找自身的属性和方法 然后再找__ proto__,再找构造函数 如果都找不到返回undefined

2.3this的指向

1.this的指向问题
  • 在事件处理函数中 this指向当前触发事件的对象(标签)
  • 在Object数据类型中的方法中 this指向所在方法的对象
  • 在普通函数中 this指向window
  • 在构造函数中 this指向实例化对象
<script>
        // 1.在事件处理函数中  this指向当前触发事件的对象(标签)
        var btn1 = document.getElementById("btn1");
        btn1.onclick = function(){
            console.log(this);//
        }
        // 2.在Object数据类型中的方法中  this指向所在方法的对象
        var obj = {
            "name":"zs",
            "eat":function(){
                console.log(this);//obj
            }
        }
        obj.eat();


        // 3.在普通函数中  this指向window
        function fun1(){
            console.log(this);//window
        }
        fun1();

        // 4.在构造函数中  this指向实例化对象
        function Person(name){
            this.name = name,
            this.skill = function(){
                console.log(this);//实例化对象
            }
        }
        var res =  new Person("ls");
        res.skill();
    </script>
2.如何改变this指向
  • call(this的新指向,参数1,参数2…) 参数是分开写的 用逗号分隔
  • apply(this的新指向,[参数1,参数2…])
  var obj = {
            "name": "小红",
            "age": 10,
            "getName": function () {
                console.log(this.name);
            }
        }
        obj.getName();// 小红  this指向 obj

        var obj1 = {
            "name": "小明"
        }
        obj.getName.call(obj1);//this指向obj1  小明

        function fun1() {
            console.log(this)
        }
        fun1();//this指向window
        fun1.call(document.documentElement);
        fun1.call(1);//
        fun1.call("123");//


        function fun2(a, b) {
            console.log(a, b, this);
        }
        fun2(100, 200);// 100 200 window
        fun2.call(obj1, 100, 200);// 100 200 obj1:{name:"小明"}

        // 详细检测数据类型   
        console.log(Object.prototype.toString.call("124"))
        console.log(Object.prototype.toString.call(123))
        console.log(Object.prototype.toString.call(true))
        console.log(Object.prototype.toString.call(undefined))
        console.log(Object.prototype.toString.call(null))
        console.log(Object.prototype.toString.call({}))
        console.log(Object.prototype.toString.call([]))
        console.log(Object.prototype.toString.call(function () { }))
        console.log(Object.prototype.toString.call(new Date()))
        console.log(Object.prototype.toString.call(/\d/gi))
				
				//instanceof 检测当前数据是否属于某个构造函数
        function Person() {
            this.name = "ls"
        }
        var res = new Person();
        // 检测res是否由Person构造函数创建的
        console.log(res instanceof Person);//true 
        // apply(this的新指向,[参数1,参数2......])
        function fun3(a,b){
            console.log(this,a,b)
        }
        fun3();//window
        fun3.apply(obj1,[100,200]);// obj1 100 200
        
				//利用Math.max和Math.min求数组中最大的值和最小的值
        var arr = [100,80,120,60];
        //  Math.max(序列1,序列2.....)
        var a =  Math.max.apply("12344",arr);
        console.log(a);
        var a = Math.min.apply("123",arr);
        console.log(a);

3.面向对象的选项卡

    <script>
        // var btn = document.getElementsByTagName("button");
        // var odiv = document.getElementsByTagName("div");
        // for(var i = 0;i<btn.length;i++){
        //     btn[i].index =i;
        //     btn[i].onclick = function(){
        //         for(var j = 0;j<odiv.length;j++){
        //             odiv[j].style.display = "none";
        //         }
        //         odiv[this.index].style.display = "block";
        //     }
        // }
        /*
            面向对象的选项卡
                创建面向对象构造函数   面向对象都是由属性和方法组成的
                标签作为对象的属性    点击事件和for循环作为对象的方法
        */

        function SwitchTab() {
            // 添加属性
            this.btn = document.getElementsByTagName("button");
            this.odiv = document.getElementsByTagName("div");
            var that = this;// 在这里this还是指向实例化对象的
            // 入口方法 添加方法
            this.init = function () {
                for (var i = 0; i < this.btn.length; i++) {
                    this.btn[i].index = i;
                    this.btn[i].onclick = function () {
                        // console.log(this);//在事件处理函数中 this指向当前触发事件的对象
                        // 如何让this指向实例化对象
                        that.changeItem(this.index);//想让this指向实例化对象
                    }
                }
            }
            // 入口方法调用
            this.init();
            this.changeItem = function (index) {
                for (var j = 0; j < this.odiv.length; j++) {
                    this.odiv[j].style.display = "none";
                }
                // this.index  想让this指向触发对象  但是现在这个this指向实例化对象
                this.odiv[index].style.display = "block";
            }
        }
        // 实例化对象
        new SwitchTab();
    </script>

4.面向对象的继承

4.1原型继承

缺点:不能传参 引用数据类型会一改全改

<script>
        /*
            创建一个大学生类
                name  age sex  id   skill必杀:谈恋爱
            创建一个小学类
                name  age  sex id  skill必杀:找妈妈
        */
       //父类构造函数
        function Student(name, age, sex, id) {
            this.name = name
            this.age = age
            this.sex = sex
            this.id = id
            this.arr = [1,2,3,4]
        }
        // 子类构造函数
        function CollegeStudent() {
            this.skill = function () {
                console.log("谈恋爱")
            }
        }
        //子类构造函数
        function SmallStudent() {
            this.skill = function () {
                console.log("找妈妈")
            }
        }
        // 设置继承  子构造函数的prototype = 父类构造函数的实例化对象
        CollegeStudent.prototype =  new Student("张三",20,"男",1);

        // 实例化子类对象  大学生1
        var res1 =  new CollegeStudent();
        console.log(res1);
        console.log(res1.name);//"张三"
        console.log(res1.name1);// undefined
        console.log(res1.arr);//["1","2","3","4"]
        res1.arr[3] = "哈哈";
        console.log(res1.arr);//["1","2","3","哈哈"]

        // 大学生2
        var res2 = new CollegeStudent();
        console.log(res2);
        console.log(res2.arr);//["1","2","3","哈哈"]
        // 缺点:不能传参    引用数据类型会一改全改
    </script>

4.2对象冒充继承

优点:能解决传参问题 一改全改
缺点:父类上的prototype上的属性和方法不能继承

<script>
        // 父类
        function Student(name,age,sex,id){
            this.name = name;// this指向子类
            this.age = age;
            this.sex = sex;
            this.id = id;
            this.arr = [1,2,3,4];

        }
        Student.prototype.classId = "0711web";
        Student.prototype.eat = function(){console.log("都爱吃食堂饭")}
        //子类
        function CollegeStudent(name,age,sex,id){
            //子类对象.name = name
            // 调用父类 对象冒充继承
            Student.call(this,name,age,sex,id);//this指向子类
            this.skill = function(){
                console.log("谈恋爱")
            }
        }
        /* 
            优点:能解决传参问题 一改全改
            缺点:父类上的prototype上的属性和方法不能继承
        */
        // 实例化对象 
       var res =  new CollegeStudent("张三",20,"男",1);
       console.log(res);
       console.log(res.classId);
    </script>

4.3组合继承

  • 组合继承:原型继承+对象冒充继承
 <script>
        /*
            组合继承:原型继承+对象冒充继承
        */
        //父类构造函数
        function Student(name, age, sex) {
            this.name = name;
            this.age = age
            this.sex = sex;
            this.arr = [1,2,3,4];
        }
        Student.prototype.classid = "0711web";
        Student.prototype.eat = function () {
            console.log("爱吃食堂的饭")
        }
        //子类构造函数
        function SmallStudent(name,age,sex){
            // 对象冒充继承
            Student.call(this,name,age,sex);
            this.skill = function(){
                console.log("找妈妈")
            }
        }
        // 原型继承  子类的prototype = 父类的实例化对象
        SmallStudent.prototype =  new Student("如花",18,"女")

        // 实例化子类对象
        var res = new SmallStudent("张三",10,"男");
        console.log(res);
        console.log(res.name);//张三
        res.eat();//爱吃食堂的饭
    </script>

4.4寄生式组合继承

  • 原型继承 + 对象冒充继承
 <script>
        /*
            组合继承:原型继承+对象冒充继承
        */
        //父类构造函数
        function Student(name, age, sex) {
            this.name = name;
            this.age = age
            this.sex = sex;
            this.arr = [1,2,3,4];
        }
        Student.prototype.classid = "0711web";
        Student.prototype.eat = function () {
            console.log("爱吃食堂的饭")
        }
        //子类构造函数
        function SmallStudent(name,age,sex){
            // 对象冒充继承
            Student.call(this,name,age,sex);
            this.skill = function(){
                console.log("找妈妈")
            }
        }
        // 原型继承  子类的prototype  = Object.create(原型对象) 复制父类的原型对象
        SmallStudent.prototype = Object.create(Student.prototype);
        SmallStudent.prototype.constructor = SmallStudent;

        // 实例化子类对象
        var res = new SmallStudent("张三",10,"男");
        console.log(res);
        console.log(res.name);//张三
        res.eat();//爱吃食堂的饭
        
    </script>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值