zoukankan      html  css  js  c++  java
  • 函数——面向对象(oop&&构造函数&原型链)

      一、概念

      面向对象:对于软件开发模式有两种,一种是面向对象,一种是面向过程。面向过程:只完成自己所需要的操作,但是这种设计缺少可维护性。面向对象:本质上是组件化的设计(模块化设计),方便局部维护但是设计上的要求规范比较多,也就是模块化的设计最重要的就是标准,以及整个项目的整体把控。

      面向对象的概念实际上有以下几个特点:封装性,保护内部的操作对外不可见;继承性:相当于一代代的传承问题;多态性:在一个范围内的定义改变。js可以模拟实现继承和封装,但不能模拟实现多态,所以js是基于事件,基于对象的语言。

      面向对象编程(Object Oriented Programming)

        简称"OOP",是一种编程开发思想,它将真实世界的各种复杂关系抽象成一个个对象,然后由对象之间的分工合作完成对现实世界的模拟。

      类和对象的概念:

        类:具有相同特征(属性)和行为(方法)的集合。如:人类->:属性:姓名、三围、星座  方法-> 吃、喝

        对象:从类中拿出具有确定属性值和方法的个体叫做对象。可以这样说,对象是包含属性和方法的集合,万物皆对象。

        二者关系:类是抽象的,对象是具体的,类是对象的抽象化,对象是类的具体化。

      二、创建对象

        1. 用字面量创建,不利于复用代码。

        <script>
            var person = {
                name: 'davina',
                age: 20,
                play: function () {
                    console.log('write code');
                }
            }
            console.log(person); //{name: "davina", age: 20, play: ƒ}
    
            person.heigth = '170CM';
            console.log(person);  //{name: "davina", age: 20, heigth: "170CM", play: ƒ}
        </script>

        可以另外添加属性和方法,添加属性即为描述对象的特征,属性的值为非函数的任意数据类型,添加的方法是实现对象的一些功能,方法的值为函数。

      2、构造函数

        在js中,构造函数就是一个用来生成对象的函数,所有的对象都是由它创建。构造函数首字母一般大写,它与普通函数的区别是是否由new操作符调用 。new是语法糖,使用new操作符调用构造函数时,会创建一个新对象,将this指向该对象(将构造函数的作用域赋给新对象)。

        在普通函数执行的基础上加了“new xxx()”,这样的话就不是普通执行了,而是构造函数执行,当前的函数名称为“类名”,接收的返回结果是当前类的一个实例,实例由构造函数生成,平时用的都是实例化对象。简单来说,通过构造函数new出来的的对象叫做实例,创建对象的过程叫做实例化。

    function Fn(){
        //......
    }
    let f = new Fn();//Fn是类,f是类的一个实例
    console.log(f);
    let f2 = new Fn();//f2也是Fn的一个实例,f2和f是独立分开的
    function Func() { }
    let f = Func();
    console.log(f);//undefined
    
    /* 
     *把它当做是普通函数执行(形成私有上下文,初始化作用域链,初始化this,初始化arguments,形参赋值,变量提升,代码执行......)
      *f获取的是函数的返回结果(函数中没有return,所以f为undefined)
     *
     */
    function Func() { }
    let f = new Func();
    console.log(f);//Func{}
    /* 
     * 这是构造函数执行,当做类来执行,此时Func被称为是'类',返回的结果(f)被称为当前‘类’的一个‘实例’,它是一个实例对象
     */

        构造函数和普通函数的联系与区别:

        1.构造函数和普通函数执行大体上是一致的(它具备普通函数的一面)

        2.区别:在初始化作用域链后和初始化this前,首先默认创建一个对象(这个对象就是当前类的实例),让上下文中的this指向这个对象

        3 . 构造函数执行,不写return,浏览器会默认返回创建的实例,但是如果我们自己写了return分为两种情况:

          1).return的是一个基本值,返回的结果依然是类的实例,没有受到影响

                     2).如果返回的是引用值,则会把默认返回的实例覆盖,此时接收到的结果就不在是当前类的实例了。所以构造函数执行的时候 ,尽量减少return的使用,防止覆盖实例。

        4.用new调用的函数,这个函数就是一个用来创建对象的函数即构造函数,它得到的结果永远是一个对象,不管函数有无返回值。

    function Func(x, y) {
        // num只是当做普通函数执行的时候,给私有上下文中设置的私有变量,和实例对象没有关系,只有this是实例对象,所以this.XXX=XXX才和实例有关系
        let num = x + y;
        this.x = x;
        this.y = y;
    
        // return {
        //     name: 'xxx'
        // }; //=>返回基本类型值,f2依然是创建的实例对象;如果自己返回的就是一个引用值,一切以自己返回的为主,此时的f2={name:'xxx'}而不再是当前类的实例了
    }
    let f2 = new Func(10, 20);
    console.log(f2);
    function Person(name) {
      this.name = name;
    }
    Person.prototype.inscription = function () {
      console.log("人可生蚁而美如神!");
    };
    Person.prototype.introduce = function () {
      console.log("my name is "  +  this.name);
    };
    /* 
     * _new:创建一个类的实例
     * params:
     *    Func:创建实例的这个类
     *    剩余的参数都是给Func这个函数传递的实参
     */
    function _new(Func, ...args) {
      //1.创建一个类的实例(对象.__proto__ === 类.prototype 
       //Object.create(xxx)创建一个空对象,且把xxx作为当前对象的原型链指向
      let obj = Object.create(Func.prototype);
      //2.把类当作普通函数执行(this指向的是实例对象)
        //let result = Func(); 把Func当做普通函数执行返回给result
        //let result = Func(...args);传参
        //let result = Func.call(obj,...args);把 this指向当前的实例obj
      let result = Func.call(obj, ...args);
      //3. 构造函数执行,不写return,浏览器会默认返回创建的实例,但是如果我们自己写了return分为两种情况,return的是基本数据类型还是引用数据类型
        // 当typeOf reslut的结果为object或者是function时,那结果为引用值,返回的是这个引用值,否则返回的是obj
      if (result !== null && /^(object|function)$/.test(typeof result)) {
        return result;
      }
      return obj;
    }
    let davina = _new(Person, "davina");
    
    davina.inscription(); //人可生蚁而美如神!
    davina.introduce();// my name is davina
    console.log(davina instanceof Person); //true

      instanceof 可以检测当前对象是否为某个类的实例

    //语法:实例 instanceof 类 返回boolean类型
    f2 instanceof (Func);
       每一个对象(包含实例对象)都有很多属性和方法,在自己堆内存中存储的都是私有的属性方法,基于__proto__原型链查找类prototype原型上的都是共有的属性方法。一个对象身上有另一个对象身上的属性或方法,这种具有的方式就叫做继承,生成的实例具有构造函数身上的属性和方法。(不光是实例可以继承构造函数,对象与对象也是可以继承的)。

      三、原型及原型链

       prototype(原型):每一个函数(es6箭头函数除外)都具备prototype属性(原型属性),它属性值是一个对象。指向了当前函数所在的引用地址。在这个对象中会存储当前类的公共属性和方法;

       constructor:在prototype的堆内存中,如果是浏览器为其默认开辟的堆内存,会存在一个内置的属性constructor,属性值就是当前类本身;

       __proto__:每一个对象都有一个内置属性:__proto__(原型链属性),属性值是当前实例所对应类的prototype。

       原型链:调用当前实例对象的某个属性(成员访问),先看自己私有属性中是否存在,存在调用的就是自己私有的;不存在,则默认按照__proto__找所属类prototype上的公有属性和方法;如果还没有,再基于prototype上的__proto__继续向上级查找,直到找到Object.prototype为止

       hasOwnProperty:检测某个属性是否为对象的私有属性

    /* 
     * 函数数据类型:普通函数、类(内置类/自定义类)、箭头函数  => 都是Function的实例
     * 对象数据类型:普通对象、数组对象、正则对象、日期对象、实例对象、函数对象(函数也是一个对象,也像普通对象一样,有自己的键值对)、类的prototype也是对象  => 都是Object的实例
     */
    function Fn() {
        this.x = 100;
        this.y = 200;
        this.getX = function () {
            console.log(this.x);
        }
    }
    Fn.prototype.getX = function () {
        console.log(this.x);
    }
    let f1 = new Fn();
    console.log(f1.getX === Fn.prototype.getX);
    console.log(f1.getX());
    console.log(f1.__proto__.getX());

      重定向原型指向:在我们重定向原型指向的时候,之前原型上的内容会丢失,包含constructor,为了保证结构的完整性,我们一般要手动设置constructor属性。

    function fun(){
        this.a=0;
        this.b=function(){
            alert(this.a);
        }
    }
    fun.prototype={
        b:function(){
            this.a=20;
            alert(this.a);
        },
        c:function(){
            this.a=30;
            alert(this.a)
        }
        fun.prototype.constructor = fun;
    }
    var my_fun=new fun();
    my_fun.b();
    my_fun.c();

       内置类的原型上扩展方法:因为内置类的原型上会默认很多常用的方法,但是在真实的项目开发中这些内置的方法往往不足以完成开发要求,所以我们向内置类的原型上扩展方法,这样调用起来比较的方便。在方法执行的时候,方法中的this就是当前处理的那个实例。

      在向内置类的原型上扩展方法时我们要注意以下几点:

        1):  扩展的方法名字最好设置前缀,如MyXXX,防止自己扩展的方法替换了内置的方法;

        2):  this的结果一定是对象数据类型值,所以向基本数据类型的原型上扩展方法,方法被执行时,方法中的this不在是基本类型,但是还是按照原始的方式处理即可

        3):  如果返回的结果依然是当前类的实例,还可以继续调用当前类原型上其它的方法(如果不是自己类的实例,可以掉用其它类原型上的方法) => “链式写法”

      对于一个对象来说,它的属性方法(私有的/公有的)存在“枚举”的特点:在for in循环的时候是否可以遍历到,能遍历到的是可枚举的,不能遍历到的是不可枚举的。

    String.prototype.queryURLParams = function queryURLParams(key) {
        // this -> 当前要处理解析的URL
        let obj = {};
        this.replace(/([^?&=#]+)=([^?&=#]+)/g, (_, $1, $2) => obj[$1] = $2);
        this.replace(/#([^?&=#]+)/g, (_, $1) => obj['_HASH'] = $1);
        return typeof key === "undefined" ? obj : obj[key];
    };
    let url = "https://support.google.com/chrome/?p=help&ctx=keyboard";
    let result = url.queryURLParams();
    console.log(result);
    
    String.prototype.indexOf = function indexOf() {
        return 'OK';
    };
    console.log(url.indexOf('?'));
    (function () {
        function handleNum(num) {
            num = Number(num);
            return isNaN(num) ? 0 : num;
        }
        Number.prototype.plus = function plus(num) {
            num = handleNum(num);
            return this + num;
        };
        Number.prototype.minus = function minus(num) {
            num = handleNum(num);
            return this - num;
        };
    })();
    
    let n = 10;
    let m = n.plus(10).minus(5);
    console.log(m); //=>15
    // obj.__proto__=Object.prototype;
    // func是可枚举的属性了
    Object.prototype.func = function func() {};
    let obj = {
        name: 'davina',
        age: 18
    };
    for (let key in obj) {
        // 在遍历的时候,对于原型上扩展的公共属性方法,我们过滤掉,只遍历对象中私有的属性方法(必须是可枚举的)
        if (!obj.hasOwnProperty(key)) break;
        console.log(key);
    } 

      四、关于Object与Function 

             let arr = [10,20,30];原型分析图如下:

      从中我们可以出:

        Object做为一个类(一个函数)它是Function的一个实例,Function虽然是函数(类)但它也是一个对象,所以它也是Object的一个实例,即:

        Object.__proto__.__proto__ === Object.prototype

      在js中的任何除了基本类型值外的实例,最后都可以基于自己的__proto__找到Object.prototype。也就是所有的值都是Object的实例,所以万物皆是对象。

      五、es6中新增class关键字创建类

        用class关键字声明一个类,首字母大写

        语法规范:

        类里面的constructor函数,存放的是类的共有属性,可以接受传递过来的参数,同时返回实例对象,只要new生成实例时,就会自动的调用这个函数,如果不写这个函数,类会自动生成这个函数;生成实例的new不能省略;创建类时,类名后面不加小括号,生成实例时,类后面加小括号;构造函数里不需要加function(类里面的所有函数都不需要添加function);多个函数方法之间不需要用逗号分隔。

        <!-- <script>
           class name[extends]{
               //calss body
           }
           extends为继承,是个可选的参数
        </script> -->

        强调的是:声明的类还是一个构造函数,共享方法直接写,它会自动的放在prototype上,共享属性需要写在constructor里

        <script>
            //es6的写法  创建一个Person类
            class Person {
                //类的共有属性放到constructor中
                constructor(name, age) {
                    this.name = name;
                    this.age = age;  //共享属性
                }
                say() {          //共享方法  自动放在prototype
                    console.log('人可生如蚁而美如神!')
                }
            }
    //利用类创建对象 const [person1, person2] = [new Person('davina', 20), new Person('lisa', 21)]; //这是两个实例 person1.sex = 'male'; console.log( typeof Person, //function 构造函数,虽说是类,但本质上还是构造函数 Person.prototype.constructor === Person, // true person1.__proto__ === Person.prototype, // true person1 instanceof Person, //true person2.constructor == Person, ///true Object.getOwnPropertyNames(person1),// ["name", "age", "sex"] 找实例身上自己的属性   person1.hasOwnProperty('say'), //false say是放在prototype里,继承而来,并不是实例身上自有的 Object.keys(Person.prototype), // [] 内置身上的方法一般是不可以被枚举 ) // class Person{} //SyntaxError: Identifier 'Person' has already been declared 不能重复的声明 //构造函数和实例放在一起 const MyPerson = new class { move() { console.log(12); } } MyPerson.move(); //12 </script>
  • 相关阅读:
    JS-Date日期内置对象
    JS-string内置对象
    MyBatis的事务处理
    MyBatis的简单操作
    MyBatis第一个项目示例
    CSS-盒子模型
    百分比布局的使用
    使用TabLayout快速实现一个导航栏
    彻底理解android中的内部存储与外部存储
    Eclipse的LogCat总是自动清空怎么办?
  • 原文地址:https://www.cnblogs.com/davina123/p/12023907.html
Copyright © 2011-2022 走看看