5-14、面向对象

面向对象

对代码的一种抽象,对外统一提供调用接口的编程思想

怎么来的:基于原型的面向对象中,对象则是依靠构造器利用原型构造出来的。

属性:事物的特性
方法:事物的功能
对象:事物的一个实例
原型:js函数中有prototype属性引入的一个对下个,即原型对象

闭包

闭包是指有权访问另一个函数作用域中的变量的函数

闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数)

在这里插入图片描述
c指向的是函数b,那么c();,就访问到了i。

特点:函数b 是在 a内嵌套的,函数a需要返回函数b
用途:1、读取函数内部的变量 2、让 i 变量的值 保留在内存中。

//也可以这样
	function a(){
      i = 999;
      function b(){
        return i;
      }
      return b;
    }
    c = a()();
    console.log(c);
//还可以这样
	function a(){
      i = 999;
      function b(){
        return i;
      }
      b();
      return b;
    }
    console.log(a());

案例2:

//已知存在内存中的证明案例
	function a(){
      i = 999;
      nadd = function(){
        i+=1;
      }
      function b(){
        console.log(i);
      }
      return b;
    }
    c = a();
    c();	// 999
    nadd();	//	这里执行了i+1
    c();	// 1000

优点:有利于封装,可以访问局部变量
缺点:内存占用浪费严重,内存泄漏,容易被黑客攻击

js字面式对象声明
var xxx = {
	属性名 : 属性值,
	属性名 : 属性值,
	属性名 : 属性值,
	...
	方法名称 : function(){},
	方法名称 : function(){},
	...
}

实际案例:

var xy = {
      name : "xy",
      age : 90,
      eat : function(fds){
        alert("我在吃" + fds);
      }
    }
    console.log(xy.name);
    xy.eat("汉堡");

第二种方法:
在这里插入图片描述
案例:

	var xy = new Object;
    xy.name = "xxx";
    xy.age = 99;
    xy.eat = function(fds){
      alert("我在喝" + fds);
    };
    console.log(xy.name);
    var a = xy.eat("可乐");   //这里不能直接调用,需要赋值
    console.log(a);

第三种方法:
js中构造方法声明对象:
在这里插入图片描述
案例:

    function xy (name,age,eat){
      this.name = name,
      this.age = age,
      this.eat = eat,
      this.show = function add(){
         console.log(this.name+ "--" +this.age + this.eat); 
      }
    }
    var obj1 = new xy("xy",99,"吃");
    console.log(obj1.name); //上面参数传进去了,这里可以用xy.
    obj1.show(); //show本身没有参数,那就要obj带着参数进去了
    
    var obj2 = new xy("xx",00,"喝");	//能够多次调用且不冲突
    obj2.show();
工厂模式

按照某种模式,可以不断地创造对象。
在这里插入图片描述

    function stus(name,age){
      var xy = new Object();
      xy.name = name; //xy.name属性 name参数
      xy.age = age;
      xy.run = function(){
        return this.name + this.age;
      }
      xy.say = function(){
        console.log("第二句。");
      }
      return xy;  //最后要返回一下对象
    }
    var stu1 = stus("张三",14);
    var stu2 = stus("李四",16);
    console.log(stu1.name); //张三
    stu1.say(); //第二句。
    alert(stu2.run());  //记得带括号
    //stu1 和 stu2 没有任何关联

执行的时候从上往下,先stus载入内存,然后执行stu1,然后找到stus,执行里边的语句,最后返回xy。

构造和工厂模式不同:
1、构造方法不会显示创建对象,将属性赋值给this,不需要return对象
2、工厂模式在方法内部创建Object对象,返回Object对象,属性和方法都是赋给Object对象。

js原型模式声明对象

prototype 可以利用它,在对象外部添加属性。
在这里插入图片描述
instanceof检查是否包含

    function xy (){}
    xy.prototype.name = "xx";
    xy.prototype.age = 16;
    xy.prototype.add = function(){
      console.log(this.name + this.age);
    }
    xy.prototype.qqq = function(){
      console.log("第二句话。");
    }
    var stu1 = new xy();
    stu1.qqq();

第二种方法:

//json形式
    function xy (){}
    xy.prototype={
      name : "xx",
      age : 18,
      add : function(){
        console.log("第一句话。");
      }
    }
    var stu1 = new xy();
    stu1.add();
js中混合模式声明对象

在这里插入图片描述
在这里插入图片描述

对象的遍历

遍历对象的数组,对象可以当作数据处理 for in

    var ren = {
      name: 'xy',
      age: 16,
      dome: function (param) {
        document.write('xxxxx');
      }
    }
    for(var i in ren){	//i是属性或方法名称
      console.log(ren[i]);	//取得是属性的值,或者是方法的定义代码
    }

使用构造函数声明的对象要实例化之后才可以进行遍历。

对象的储存

在这里插入图片描述

封装

把对象内部数据和操作细节进行隐藏。

//闭包案例
	function a(){
      i = 999;
      function b(){	//特权方法
        return i;
      }
      return b;
    }
    c = a()();
    console.log(c);

封装案例:

封装案例
js中处处是对象,面向对象的第一步当然就是封装了,由于Js中没有类的概念,所以封装起来也比较麻烦,下面介绍两种js的封装。

1、使用约定优先的原则,将所有的私有变量以_开头

       /**
        * 使用约定优先的原则,把所有的私有变量都使用_开头
        */
       var Person = function (no, name, age)
       {
           this.setNo(no);
           this.setName(name);
           this.setAge(age);
       }
       Person.prototype = {
           constructor: Person,
           checkNo: function (no)
           {
               if (!no.constructor == "string" || no.length != 4)
                   throw new Error("学号必须为4位");
           },
           setNo: function (no)
           {
               this.checkNo(no);
               this._no = no;
           }, getNo: function ()
           {
               return this._no;
           }, setName: function (name)
           {
               this._name = name;
           }, getName: function ()
           {
               return this._name;
           }, setAge: function (age)
           {
               this._age = age;
           }, getAge: function ()
           {
               return this._age;
           }, toString: function ()
           {
               return "no = " + this._no + " , name = " + this._name + " , age = " + this._age;
           }
       };
       var p1 = new Person("0001", "鸿洋", "22");
       console.log(p1.toString());        //no = 0001 , name = 鸿洋 , age = 22
       p1.setNo("0003");
       console.log(p1.toString());      //no = 0003 , name = 鸿洋 , age = 22
       p1.no = "0004";
       p1._no = "0004";
       console.log(p1.toString());    //no = 0004 , name = 鸿洋 , age = 22

看完代码,是不是有种被坑的感觉,仅仅把所有的变量以_开头,其实还是可以直接访问的,这能叫封装么,当然了,说了是约定优先嘛,这种方式还是不错的,最起码成员变量的getter,setter方法都是prototype中,并非存在对象中,总体来说还是个不错的选择。如果你觉得,这不行,必须严格实现封装,那么看第二种方式。

2、严格实现封装

        /**
         *  使用这种方式虽然可以严格实现封装,但是带来的问题是get和set方法都不能存储在prototype中,都是存储在对象中的
         * 这样无形中就增加了开销
         */
        var Person = function (no, name, age)
        {
            var _no , _name, _age ;
            var checkNo = function (no)
            {
                if (!no.constructor == "string" || no.length != 4)
                    throw new Error("学号必须为4位");
            };
            this.setNo = function (no)
            {
                checkNo(no);
                _no = no;
            };
            this.getNo = function ()
            {
                return _no;
            }
            this.setName = function (name)
            {
               _name = name;
            }

            this.getName = function ()
            {
                return _name;
            }

            this.setAge = function (age)
            {
                _age = age;
            }
            this.
                    getAge = function ()
            {
                return _age;
            }

            this.setNo(no);
            this.setName(name);
            this.setAge(age);
        }
        Person.prototype = {
            constructor: Person,
            toString: function ()
            {
                return "no = " + this.getNo() + " , name = " + this.getName() + " , age = " + this.getAge();
            }
        }
        ;
        var p1 = new Person("0001", "鸿洋", "22");
        console.log(p1.toString());        //no = 0001 , name = 鸿洋 , age = 22
        p1.setNo("0003");
        console.log(p1.toString());      //no = 0003 , name = 鸿洋 , age = 22
        p1.no = "0004";
        console.log(p1.toString());    //no = 0003 , name = 鸿洋 , age = 22

看上面的代码,去掉了this.属性名,严格的实现了封装,只能通过getter,setter访问成员变量了,但是存在一个问题,所有的方法都存在对象中,增加了内存的开销。

3、以闭包的方式封装

        /**
         *  使用这种方式虽然可以严格实现封装,但是带来的问题是get和set方法都不能存储在prototype中,都是存储在对象中的
         * 这样无形中就增加了开销
         */
        var Person = (function ()
        {
            var checkNo = function (no)
            {
                if (!no.constructor == "string" || no.length != 4)
                    throw new Error("学号必须为4位");
            };
            //共享变量
            var times = 0;

            return function (no, name, age)
            {
                console.log(times++);    // 0 ,1 , 2
                var no , name , age;
                this.setNo = function (no)
                {
                    checkNo(no);
                    this._no = no;
                };
                this.getNo = function ()
                {
                    return this._no;
                }
                this.setName = function (name)
                {
                    this._name = name;
                }

                this.getName = function ()
                {
                    return this._name;
                }

                this.setAge = function (age)
                {
                    this._age = age;
                }
                this.
                        getAge = function ()
                {
                    return this._age;
                }

                this.setNo(no);
                this.setName(name);
                this.setAge(age);
            }
        })();
        Person.prototype = {
            constructor: Person,
            toString: function ()
            {
                return "no = " + this._no + " , name = " + this._name + " , age = " + this._age;
            }
        }
        ;
        var p1 = new Person("0001", "鸿洋", "22");
        var p2 = new Person("0002", "abc", "23");
        var p3 = new Person("0003", "aobama", "24");


        console.log(p1.toString());        //no = 0001 , name = 鸿洋 , age = 22
        console.log(p2.toString());      //no = 0002 , name = abc , age = 23
        console.log(p3.toString());    //no = 0003 , name = aobama , age = 24


上述代码,js引擎加载完后,会直接执行Student = 立即执行函数,然后此函数返回了一个子函数,这个子函数才是new Student所调用的构造函数,又因为子函数中保持了对立即执行函数中checkNo(no) ,times的引用,(很明显的闭包)所以对于checkNo和times,是所有Student对象所共有的,创建3个对象后,times分别为0,1,2 。这种方式的好处是,可以使Student中需要复用的方法和属性做到私有且对象间共享。

原型继承

原型和原型链

原型:是利用prototype 添加属性和方法

原型链:JS在创建对象(不论是普通对象还是函数对象)的时候,都有一个叫做_proto_的内置属性,用于指向创建它的函数对象的原型对象prototype

通过原型和原型链引出继承的概念

在这里插入图片描述
在这里插入图片描述
实例:
在这里插入图片描述
完整案例:
在这里插入图片描述
在这里插入图片描述

原型继承:

示例:
在这里插入图片描述

构造函数的继承

在子类内部构造父类的对象实现继承
在这里插入图片描述
call:调用一个对象的一个方法,以另一个对象替换当前对象
apply:应用某一对象的一个方法,用另一个对象替换当前对象

关键词:
instanceofdelete、call、apply、arguments、callee、this

instanceof
判断变量是否是对象的实例

var arr = new Array();
alert(arr instanceof Array);  //true
alert(arr instanceof Object); 

delete
删除对象的属性 (不能删除对象、变量、原型链中的属性)

function arr(){
  this.name = "xy"
}
var obj = new arr();
console.log(obj.name);  //xy
delete obj.name;
console.log(obj.name);  //undefined

callee
返回正在执行的function对象

arguments

apply

对象冒充

将父类的属性和方法一起传给子类作为特权属性和特权方法

在这里插入图片描述
walk属性继承不到,其他的都可以。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值