zoukankan      html  css  js  c++  java
  • 34、JavaScript面向对象(内置构造函数&相关方法|属性|运算符&继承&面向对象)

    一、面向对象

    1.1 this的指向问题

    image

    要看清楚最终的函数调用者是谁。
    IIFE也被当做函数直接运行,IIFE的this都是window对象
    函数的arguments是类数组对象,比如传入的第0项参数是函数,让它运行:arguments0,函数中的上下文是arguments对象。还要知道函数的length和arguments.length的区别。

    1.2构造函数

    当一个函数用new运算符调用时,此时这个函数叫“构造函数”(constructor)

    构造函数四步走

    创建一个新空对象
    this绑定给这个空对象
    执行语句
    返回这个对象

    new 出来的所有对象拥有相同属性群,所以可以把构造函数当做类,被new出来的对象都是这个函数的实例。

    当构造函数里面有return语句时,注意:

    1.如果是return基本类型值,无视这个return,还是返回秘密创建的对象
    2.如果是return引用类型值,则不返回原来的对象,而是返回引用类型值

    原型链的知识:

    构造函数的prototype属性就是实例的__proto__属性。对于一个对象来说,__proto__这个属性叫做自己的原型对象,就是自己的原型链。这个原型链有查找功能,当obj.haha时,obj身上没有haha属性,此时会查找obj.__proto__身上有没有haha属性,并且会继续查找obj.proto.__proto__属性身上有没有haha属性……

    二、原型链

    ++ Object.prototype是所有对象原型链的终点 ++

    任何对象都有原型对象(proto),最终指向Object.prototype,但是Object.prototype很特殊,它的__proto__终点是null。

    JS中,对象是对象、函数、数组、正则等是对象,所有引用类型值都是对象,它们都有__proto__属性。

    甚至,xiaoming的__proto__也是一个对象,这个对象也有__proto__。

    var obj = {}
    console.log(obj.__proto__ === Object.prototype); //true
    

    Object()是内置的构造函数,所有的对象,可以认为是Object new出来的

    var obj = new Object();
    obj.name = "小明";
    obj.age = 12;
    console.log(obj)
    console.log(obj.__proto__ === Object.prototype);
    

    image

    “{}”对象的__proto__都指向Object.prototype,因为它都是Object new出来的。

    function People(name,age){
       this.name = name;
       this.age = age;
    }
    var xiaoming = new People("小明",12);
    console.log(xiaoming.__proto__ === People.prototype); //true
    console.log(xiaoming.__proto__.__proto__ === Object.prototype); //true
    console.log(xiaoming.__proto__.__proto__.__proto__);; //null
    

    image

    综上所述,xiaoming的原型People.prototype也有原型,小明完整的家谱:
    image

    Object.prototype是唯一一个没有__proto__的对象,其他所有对象、函数、数组、正则等都有__proto__

    三、内置构造函数

    JS内置了很多构造函数,它们也称为“基本类型值”、“引用类型值”的包装类。

    3.1引用类型值的构造函数

    引用类型值的构造函数:Object()、Function()、Array()、RegExp()

    3.1.1 Object()函数

    Object()是内置的构造函数,可以直接 new它,返回一个空对象,可以给这个空对象添加属性:

    var obj = new Object();
    obj.name = "小明";
    obj.age = 12;
    console.log(obj)
    console.log(obj.__proto__ === Object.prototype);
    

    等价于:

    var obj = {
    name:"小明",
    age:12
    }
    

    3.1.2 Function()函数

    所有function字面量都是它的实例

    function sum(a,b){
       alert(a+b);
    }
    sum(3,5);
    

    等价于:

    var sum = new Function("a","b","alert(a+b);alert('算完啦!')");
    sum(4,5)
    

    在new Function的时候,先罗列所有形参列表,最后一个参数是函数体,注意,参数都是字符串。

    console.log(sum.__proto__ === Function.prototype)
    

    image

    ++ 任何函数都是Function()构造函数的实例,Object也是Function的实例,Function自己也是自己的实例。Function自己new自己。 ++

    console.log(Object.__proto__ === Function.prototype);   //true
    console.log(Function.__proto__ === Function.prototype); //true
    

    image

    ++ Function和Object的关系: ++

    console.log(Function.prototype.__proto__ === Object.prototype);    // true
    console.log(Function.__proto__.__proto__ === Object.prototype);    // true
    console.log(Function.__proto__.__proto__ === Object.__proto__.__proto__);    // true
    console.log(Function.__proto__ === Object.__proto__);    // true
    

    image

    3.1.3 Array()函数

    Array()是系统内置的数组构造函数,任何的数组都是Array() new出来的。

    var arr = new Array();
    arr[0] = 100;
    arr[1] = 200;
    arr[2] = 300;
    console.log(arr);
    

    等价于:

    var arr = [100,200,300];
    

    函数能填参数,表示数组长度,但数组还是空数组:

    var arr = new Array(8);
    

    常用的数组方法,都定义在Array.prototype身上。
    image

    var arr = [3,3,4,4];
    console.log(arr.__proto__ === Array.prototype); //true
    console.log(arr.__proto__.__proto__ === Object.prototype); //true
    

    3.1.4 RegExp()函数

    任何正则表达式RegExp()函数的实例。

    var reg = /d/g;
    //等价于
    var reg = new RegExp("d","g");
    console.log(reg.__proto__ === RegExp.prototype); //true
    console.log(reg.__proto__.__proto__ === Object.prototype); //true
    

    3.2基本类型值“包装类”

    【Number()、String()、Boolean()】

    基本类型值的构造函数,被称为“包装类”。JS体系为了完整,所以就人为造出了这三个包装类,没有什么用。

    3.2.1 Number()函数

    用于创建数字对象:

    var a = new Number(3);
    console.log(a)
    console.log(typeof a)
    

    image
    用内置构造函数创建数字的时候,得到一个对象,这对象的原始值属性是:
    [[PrimitiveValue]]: 3  //这个属性不可被枚举。

    它和字面量创建数字的区别:

    var a = new Number(3);
    var b = 3;
    console.log(a == b);   //true
    console.log(a === b);  //false
    

    用Number()创建的对象,可以参与数学运算:
    Number的实例是一个对象,但这个对象一旦参数运算,将变为普通Number类型

    var a = new Number(3);
    a = a * 3
    console.log(a)
    console.log(typeof a)
    

    image
    Number()也可以用来把各种值转换为数组(不能转就是NaN),不需要用new调用。

    console.log(Number("12"));   //12
    console.log(Number("12年")); //NaN
    console.log(Number(""));     //0
    console.log(Number(false));  //0
    console.log(Number(true));   //1
    console.log(Number({}));     //NaN
    console.log(Number([]));     //0
    console.log(Number([1,2]));  //NaN
    

    任何需要转为数字的隐式转换,实际上就是在调用Number函数。

    3.2.2 String()函数

    var str = new String("我喜欢你");
    console.log(str)
    

    image
    String()也可以用来转换:

    console.log(String(123));   //"123"
    console.log(String(true));  //"true"
    console.log(String([]));    //""
    console.log(String([1,2,3])); //"1,2,3"
    console.log(String(NaN));  //"NaN"
    console.log(String({}));   //"[Object Object]"
    

    image

    3.2.3 Boolean()函数

    不管值是false还是true,都能通过if的验证,都是true。

    var b = new Boolean(false);
    console.log(b)
    console.log(typeof b);
    if(b){
       alert("真的");
    }
    

    3.3内置构造函数之间的关系

    就三句话,死记:

    1、“{}”对象是被Object new出来的。所以它的__proto__就会指向Object.prototype。
    2、任何函数都是Function new出来的实例,所以只要它是函数(构造函数也是函数),它的__proto__就会指向Function.prototype。
    3、Function是所有构造函数的妈,它自己也是自己的妈。
    小明不是Object new出来的,是People new的,它的__proto__指向People.prototype。

    image

    console.log(Object.__proto__.__proto__ === Object.prototype); //true
    console.log(Function.__proto__.__proto__ === Object.prototype); //true
    console.log(Object.__proto__ === Function.prototype);  //true
    console.log(Function.__proto__ === Function.prototype);  //true
    console.log(Array.__proto__ === Function.prototype);  //true
    console.log(RegExp.__proto__ === Function.prototype);  //true
    console.log(Number.__proto__ === Function.prototype);  //true
    console.log(String.__proto__ === Function.prototype);  //true
    console.log(Boolean.__proto__ === Function.prototype);  //true
    

    image

    四、相关的方法、属性、运算符

    4.1 hasOwnProperty()方法

    返回布尔值(true/false),用来检测某属性、某方法是不是在自己身上。

    function People(name){
       this.name = name;
    }
    People.prototype.sayHello = function(){
       alert("你好");
    }
    var xiaoming = new People("小明");
    console.log(xiaoming.hasOwnProperty("name"));     //true
    console.log(xiaoming.hasOwnProperty("sayHello")); //false
    console.log(xiaoming.hasOwnProperty("toString")); //false
    

    4.2 in运算符

    返回布尔值(true/false),in运算符可以检查某个对象有没有能力调用某属性、某方法,而不管这个属性或方法是否定义在自己身上,还是原型身上。

    字符串 in 对象
    
    function People(name){
       this.name = name;
    }
    People.prototype.sayHello = function(){
       alert("你好");
    }
    var xiaoming = new People("小明");
    console.log("name" in xiaoming);     //true
    console.log("sayHello" in xiaoming); //true
    console.log("toString" in xiaoming); //true
    

    4.3 constructor属性

    每一个函数的prototype对象都有一个constructor属性,指向构造函数。

    function People(name){
       this.name = name;
    }
    var xiaoming = new People("小明");
    console.log(People.prototype)
    console.log(People.prototype.constructor === People); //true
    console.log(People.prototype.hasOwnProperty("constructor")); //true
    console.log(xiaoming.constructor === People);  //true
    console.log(xiaoming.hasOwnProperty("constructor"));  //false
    

    image

    4.4 instanceof运算符

    返回布尔值,用来检查某个对象是不是某个函数的实例

    o instanceof F
    

    如果F.prototype在o的原型链上,返回true,否则返回false
    image

    function People(name){
       this.name = name;
    }
    var xiaoming = new People("小明");
    function Dog(){
    }
    console.log(xiaoming instanceof People); //true
    console.log(xiaoming instanceof Object); //true
    console.log(xiaoming instanceof Dog); //false
    

    小题目:

    console.log(Object instanceof Object); //true
    console.log(Function instanceof Function); //true
    console.log(Function instanceof Object); //true
    console.log(Number instanceof Function); //true
    console.log(Number instanceof Number); //false
    

    我们发现object.prototype是所有对象原型链的终点,所以我敢说任何原型X一定true。

    x instanceof Object
    // Function的prototype出现在自己的__proto__线上,所以是true
    console.log(Function instanceof Function); //true
    

    五、练习题

    5.1题目1

    function A(){}
        function B(){
            return new A(); //返回了引用类型值
        }
        A.prototype = B(); //返回了一个A的实例1
        B.prototype = new B();//返回了一个A的实例2
        var a = new A(); //返回了一个A的实例,赋给a
        var b = new B(); //返回了一个A的实例,赋给b
    
        console.log(a.__proto__ == b.__proto__);
        console.log(a instanceof A); //true 
        console.log(a instanceof B); // false
        console.log(b instanceof A); //true
        console.log(b instanceof B); //false
    

    image

    5.2题目2

    [].constructor.constructor.constructor.constructor.constructor.constructor.constructor.constructor.constructor.constructor.constructor.constructor.constructor.constructor.constructor.constructor.constructor.constructor.constructor.constructor.constructor.constructor.constructor.constructor.constructor === Function //true
    

    image

    六、继承

    6.1什么是继承

    计算机领域,关注两个类的关系,就是看属性群之间的关系。

    比如:人类和狗狗类属性群,难免会有交集,但是不完全重合,此时可以认为两个类没有任何关系。
    image
    看看人和学生的属性群:
    image
    | 小学生肯定是人,人的属性学生全有,人能做的事情学生都能做。
    l 但是反过来,小学生的属性和能力,人不一定有。

    “学生”细化了、精分了、更具体了“人”。
    “学生”的实例要比“人”的实例少。
    “学生”一定是人,但“人”不一定是学生。

    术语上,我们称“学生类”继承(extend)“人类”。人类叫“父类(超类)”,学生类叫“子类”。

    A继承了B,此时要意识到:
    A拥有B的所有属性和方法
    A的属性群比B大
    A丰富了B,A把B变得更具体,范围更小。

    6.2 JavaScript实现继承

    JavaScript实现两个类:People类,Student类,要求People类拥有的属性和方法,Student类的实例也要拥有People的属性和方法。Student还能丰富自己类的属性和方法,很简单,只要求巧妙设计原型链。

    //人类
    function People(name,age,sex){
       this.name = name;
       this.age = age;
       this.sex = sex;
    }
    People.prototype.sayHello = function(){
       alert("你好,我是" + this.name);
    }
    People.prototype.sing = function(){
       alert("都拉米发骚啦稀~~~");
    }
    // 学生类
    function Student(name,age,sex,id,banji,socre){
       People.apply(this, arguments)
       // this.name = name;
       // this.age = age;
       // this.sex = sex;
       this.id = id;
       this.banji = banji;
       this.socre = socre;
    }
    //下面这条语句可以实现继承
    Student.prototype = new People();
    Student.prototype.study = function(){
       alert(this.name + "在学习!");
    }
    var xiaoming = new Student("小明",12,"男",100001,"初三一班", 100)
    xiaoming.study();
    xiaoming.sayHello();
    xiaoming.sing();
    
    注:People.apply(this, arguments)和Student.prototype = new People();要在Student.prototype.*** = function(){}之前

    jQuery创始人John Resig写了一个小包,20多行代码,解决了JS继承恶心的问题。

    https://johnresig.com/blog/simple-javascript-inheritance/

    引包之后,这个包改变我们创建JS类的方式(和jQuery一样改变了写JS的方式)

    //人类
    var People = Class.extend({
       init : function(name,age,sex){
           this.name = name;
           this.age = age;
           this.sex = sex;
       },
       sayHello:function(){
           alert("你好,我是" + this.name);
       },
       sing:function(){
           alert("都拉米发骚啦希~~~");
       }
    })
    
    //学生类
    var Student = People.extend({
       init : function(name,age,sex,id,banji,socre){
           this._super(name,age,sex); //继承父类的属性
           // this.name = name;
           // this.age = age;
           // this.sex = sex;
           this.id = id;
           this.banji = banji;
           this.socre = socre;
       },
       study :function(){
           alert(this.name + "在学习!");
       }
    })
    var xiaoming = new Student("小明",12,"男",100001,"初三一班", 100)
    console.log(xiaoming)
    xiaoming.study();
    xiaoming.sing(); 
    

    七、上升到面向对象

    面向对象是一种编程思想,两个字就能概括:自治(自己管理自己),深入理解,就是封装。每个对象个体仅需要管理自己即可。
    面向对象初学阶段,当你遇见大量的结构、功能、性质、什么都一样的对象的时候,立刻想到用面向对象技术。

    现在要给大家一个思维定式,面向对象的时候怎么编程:
      思考初程序中有哪些类,在前期我们的业务仅仅只有一个类,后期类会有多个。
      每个类有哪些方法和属性,就是他们自己有什么功能
      每个类之间如何通信、交互数据、此时就要用到设计模式,比如中介者模式、发布订阅模式(观察者模式)。
      这个类怎么进行单元测试,如果保证自己这个类鲁棒,每个类都鲁棒了,整个程序就鲁棒。
    我们之前的编程叫“面向过程”,现在是“面向对象”编程(OO)

    7.1面向对象-红绿灯(案例)

    考虑一个问题,页面上要制作一个效果:100个红绿灯,点击某一个红绿灯,从红灯变黄灯,再次点击从黄灯变绿灯,再次点击就绿灯变红灯...

    有一个信号量,点击按钮之后,信号量变化0、1、2、0、1、2、0、1、2...然后让div的background-position进行变化,你要写100个信号量、100个盒子、100个事件。

    页面上出现的东西就是红绿灯,而且它们拥有相同的样子、性质、功能,所以可以让红绿灯设计成为一个类。

    每一个类负责什么:①状态量 ②DOM元素。

    每个JS对象中有两个属性,一个是状态属性,另一个是DOM对象

    简单的说,DOM对象现在成为JS对象的一个属性。

    这个类有哪些属性?
      DOM属性
      颜色属性
    
    哪些方法?
      初始化方法 init()
      换颜色方法 changeToColor()
      绑定事件方法 bindEvent()
    

    第一步:DOM结构和CSS样式,确保放一个div元素能看见灯出来了。

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8" />
        <title>Document</title>
        <style type="text/css">
         *{ margin: 0; padding: 0;}
         div.honglvdeng{
             position: relative;
              140px;
             height: 328px;
             background: url(./images/honglvdeng.jpg);
         }
        </style>
    </head>
    <body>
        <div id="box">
            <div class="honglvdeng"></div>
        </div>
    </body>
    </html>
    
    //第二步:创建红绿灯类
    //new Honglvdeng()时,会执行构造函数中的语句
    //所以在构造函数中创建一个DOM对象,然后让它上树
    function Honglvdeng(){
       //每一个类中有两个属性:状态量、DOM
       this.dom = null;
       //状态属性
       this.state = 0;
       //初始化方法
       this.init();
       //事件监听方法
       this.bindEvent();
    }
    //为了程序美观,整洁,好更改和可插拔性高,方便维护,将DOM和上树语句都写在初始化方法中
    Honglvdeng.prototype.init = function(){
       //创建DOM
       this.dom = document.createElement('div');
       //给DOM添加类名
       this.dom.className = 'honglvdeng';
       //上树
       document.getElementById("box").appendChild(this.dom);
    }
    
    //第三步:添加事件监听
    Honglvdeng.prototype.bindEvent = function(){
       //备份this,因为事件监听里面的this表示dom元素本身
       var self = this;
       this.dom.onmouseenter = function(){
           // 改变信号量
           // self.state++;
           // if(self.state > 2) self.state = 0
           //判断简写
           self.state = ++self.state % 3;
           self.dom.style.backgroundPositionX = -155 * self.state +'px';
       }
    }
    //第四步:实例化100个红绿灯
    var count = 100;
    while(count--){
       new Honglvdeng();
    }
    

    文章转载于mufengsm

  • 相关阅读:
    搜索优化
    ETL(Extract-Transform-Load的缩写,即数据抽取、转换、装载的过程)
    Tomcat7.0.22在Windows下详细配置过程
    maven 安装配置
    Venus wiki
    搜索引擎基本原理及实现技术——用户查询意图分析
    sql 表自连接
    select 多表查询
    select 嵌套
    Ioc和Aop扩展--多种方式实现依赖注入(构造注入,p命名空间注入,集合类型注入,注入null和注入空值)
  • 原文地址:https://www.cnblogs.com/zhongchao666/p/10966544.html
Copyright © 2011-2022 走看看