JavaScript面向对象编程

107 阅读7分钟

面向对象编程

  1. 单例模式:单例模式就是一个普通对象
  2. 高级单例模式:高级单例模式就是一个函数返回了一个对象;比普通高级在于可以有自己的私有变量
  3. 工厂模式:就是一个普通函数批量产生一些单例
  4. 构造函数:通过 new 执行一个函数的时候会先开辟一个堆内存,然后把函数中的 this 指向这个堆内存,执行过程跟普通函数一样,执行完成之后,若 return 后是个引用数据类型,则 new 执行的返回结果就是 return 后面的内容,否则就是这个堆内存
         <script>
         function Person(name,age,sex){
             this.name = name;
             this.age = age;
             this.sex = sex;
         }
         var person = new Person("小明",12,0);
         console.log(person);
    
  5. prototype 原型:原型里面存放的都是这个类的共有属性
  6. 类:就是在 js 中的一些函数,只不过是通过 new 去执行
  7. 实例:就是通过 new 执行哪个函数得到的结果
  8. 原型是一个函数的固有属性,只有当使用 new 去执行这个函数的时候原型才能体现他的这个原型中的属性,是当前函数的所有实例的公用属性
  9. 所有函数的默认原型(固有属性)都会有一个属性 constructor 属性就是函数本身
  10. 所有的实例都会有一个属性__proto__;属性值是当前实例所属类的 prototype(原型)
  11. 实例去调用某些属性的时候,先去看看自己有没有这个私有属性,有的话就是用自己私有的,没有的话就去所属的原型上查找;(通过__proto__)找所属类的原型)
  12. 类的默认原型是 Object 类(基类)的实例,类的默认原型的__proto__是 Object 的 prototype
  13. 原型链是属性的查找机制:先在自己身上找这个属性,没有的话通过__proto__往所属类的原型上找,这个原型上没有的话,就接着通过这个原型的__proto__当前原型所属类的原型上去找,一直找到基类的原型。
  14. 类的原型上存放的属性,都是提供 Student 的实例去使用的公用属性
  15. new 的执行过程:先开作用域,开了一个堆内存,让函数中的 this 指向这个堆,然后形参赋值变量提升,然后代码从上到下执行
  16. return 的问题:return 后边跟一个引用类型则返回的就是写的这个引用类型,否则返回时这个堆内存(this-->实例)
  17. this 专有名词叫执行主体
  18. 严格模式"use strict"下 this 不指定就是 undefined
  19. 全局作用域下 var 或 function 出来的变量会同时给 window 这个对象增加对应的属性
  20. 函数都用 prototype 里面用来存储实力能够使用的共有属性,自带的(默认)的 prototype 上都有 constructor:指的是这个构造函数本身,所有的实例都有一个__proto__指的是所有属性类的 prototype
  21. 实例.xxx:先在自己身上查找这个属性,没有的话,通过__prot__向上层对象 A 查找,上层对象 A 若也没有这个属性,则接着通过__proto__向 A 的上层对象 B 中查找。一直找到基类(Object 类)de 原型(prototype),这时都没有对应的属性时,结果就是 undefined,因为基类的 prototype 的__proto__是 null,也就是说基类的 prototype 是最顶层
  22. 20220402120240
  23. 20220402120730
  24. constructor 这个属性之所以能用来判断数据类型,是因为实例调用了 constructor 都是调用的原型上的 constructor,指向就是构造函数本身
  25. A instanceof B:从 A 到基类的原型,这条原型链上有没有 B 的原型存在;

    特殊:只能用于引用数据类型,值类型不适用;

  26. 使用 hasOwnproperty 可以判断属性是否是私有的:返回值是 true 就是私有的,是 false 就是公有的p1.hasOwnproperty(k)
    //封装一个函数判断元素是否在公有属性上与hasOwnProperty作用相同
        function P1(name, age) {
            this.name = name;
            this.age = age;
        }
        let p1 = new P1("one", 24);
        P1.prototype.tos = "12";
        Object.prototype.hasPubProperty = function (key) {
            return (key in this) && !this.hasOwnProperty(key);
        }
    
  27. 所有的 this.xxx 的属性都是当前实例的私有属性
  28. 一般我们会把方法类的属性放到原型上
  29. 约定的规范:原型上的方法中的 this 保证是当前类的实例
  30. new 的执行过程:先开辟一个作用域,然后开辟一个堆内存,this 指向这个堆,形参赋值变量提升,代码执行。

new 返回值:不是引用的话就是默认返回 this

  1. call 执行函数并且改变函数 this 指向f.call(xxx,a,b,c)//a,b,c是传给f的实参,call 中的第一个参数是用来修改函数中的 this 指向,call 从第二个参数开始就是传给函数的实参
  2. 实现构造函数 new 方法
     function Person(name, age, sex) {
         this.name = name;
         this.age = age;
         this.sex = sex;
     }
     function myName(C, ...ages) {
         //new的特性是开辟一个新堆空间
         let obj = {};
         //让实例指向他的构造函数
         obj.__proto__ = C.prototype;
         //改变this指向指向他开辟的空间obj
         let a = C.call(obj, ...ages);
         //判断构造函数执行结构,如果是引用类型就返回它执行结构,如果是值类型就返回this
         return typeof a == "object" ? a : obj;
     }
     let p1 = new Person("小康", 12, 1);
     let p2 = myName(Person, "小康", 12, 1)
  1. 手写一个 instanceof 方法
     var ary = [];
     function myInstance(a, b) {
         //判断a.prototype是否存在并且a.__proto__ !== b.prototype
         while (a.__proto__&&a.__proto__ !== b.prototype) {
         a = a.__proto__;
             if (a.__proto__ === null) {
                 return console.log("没有")
             }
         }
         return true;
     }
     console.log(myInstance(ary, Array));
     console.log(myInstance(ary, Object));
  1. 运算符优先级(从高到底) 20220404174647
  2. 将类数组转化为数组的方法
  • 用...展开符[...arguments]
  • 结合 call 来实现:[].slice.call(div1)先通过数组找到 slice 方法,通过 call 让这个 slice 执行,并且把其中的 this 变成类数组
  • 创建一个新数组,把新数组中的内容循环放在新的数组中
  • Array.from(xxx)返回一个数组项目跟 xxx 一样
  1. Object.keys(obj)用来循环对象,返回值是将对象属性名放在一个数组中
  2. 捕捉报错的方法try{要执行可能会报错的代码}catch(形参){catch小括号中的形参对应的是上面运行错误信息;上面运行出错,就会走到这个catch,但是不会影响主体代码执行}执行 try 中的代码如果报错就执行 catch 大括号中的代码
  3. this 指向问题
  • 全局下的 this 是 window
  • 自执行函数 this 是 window
  • 事件绑定对应函数中的 this 是绑定的元素
  • 其他看点,点前面是谁 this 就是谁,没有点就是 window
  • new 执行类的时候,其中 this 是实例
  • 类原型上的方法中的 this 是实例
  • call 可以改变函数中 this 指向 f.call(111)
    • apply bind 都是改变函数中 this 指向的
  • 箭头函数中没有 this,他当中使用的 this 是上级作用域的 this
  1. apply:apply 的功能和 call 一样;区别在于 call 执行的时候给函数传参是散开写的,apply 执行的时候给函数传参是一个集合的方式
  2. bind 和 call 的用法相同但是使用 bind 函数不会立即执行,而是会返回一个新函数,新函数执行的时候会让 fn 执行并且把 fn 中的 this 换成指定的 this,后面的参数传给 fn 的实参,新函数执行的时候会给新函数传参,会拼接到 bind 传入实参的后面let res1 = fn.bind(obj,1,2,3,4,5);
  3. call 不能改变 this 的情况:1、箭头函数;2、bind 的返回值
  4. Object.create 执行会返回一个空对象,这个对象的__protp__是指向 create 的参数var obj = Object.create(Array);//obj的__proto__指向Array
  5. class 是 ES6 新增的一个用来创造类的方式,创造出来的类不能当做普通函数执行;

class 创造原型使用 eat(){},class 中没法添加值类型

   class Person {
       //创造一个名为Person的类
        constructor(name, age, sex) {
            //constructor是class规定的属性
             console.log(arguments);
             //设置私有属性
             this.name = name;
             this.age = age;
             this.sex = sex;
         }
         //私有属性height
         height = 1;
         //建立名为eat的原型,形参food
         eat(food){
             console.log(`${food}`)
         }
         //static声明的是静态属性,指的是Person自己的属性
         static qqq = 888;
         static ttt = 999;
         static yyy = 666;
    }
    let p1 = new Person("小孔", 12, 0);
    console.log(Person.qqq);//输出Person内置的静态属性qqq,它的实例是不能使用静态属性
  1. 函数的三种角色:普通函数、对象、类
  2. Array.isArray(xxx):判断 xxx 是不是一个数组
  3. Array.from(xxx):把类数组 xxx 转成一个数组,返回值就是这个数组
  4. Object.keys(xxx):把 xxx 这个对象的中的所有私有属性名拿出来组成一个数组返回
        //将obj私有属性都拼接起来
        Object.prototype.qq = 123;
        var obj = {
            name: "大哥",
            age: 13
        }
        function obje(one) {
            var h1 = document.getElementById("h1");

            var a = Object.keys(one);
            var str = "";
            a.forEach(itm => {
                str += `${itm}${one[itm]};`;
            });
            h1.innerText = str;
        }
        obje(obj)

  1. 在页面插入并点击排序
var list = [{
         name: "小红",
         age: 10
     },
     {
         name: "小红2",
         age: 120
     },
     {
         name: "小红3",
         age: 140
     },
     {
         name: "小红4",
         age: 105
     },
     {
         name: "小红5",
         age: 106
     }
     ];
     var ul = document.getElementsByTagName("ul")[0];
     var but = document.getElementsByTagName("button");
     class Proson {
         constructor(ul, but) {
             this.ul = ul;
             this.but = but;
         }
         butt(array) {
             this.one(array);
             this.but[0].onclick = () => {
                 this.one(this.srop(array));
             }
             this.but[1].onclick = () => {
                 this.one(this.srop1(array));
             }
         }

         one(array) {
             var str = "";
             array.forEach(ele => {
                 str += `<li>姓名是${ele.name};年龄是${ele.age};</li>`
             });
             this.ul.innerHTML = str;
         }
         srop(array) {
             return array.sort((a, b) => {
                 return a.age - b.age;
             })
         }
         srop1(array) {
             return array.sort((a, b) => {
                 return b.age - a.age;
             })
         }

     }

     var but = new Proson(ul, but);
     but.butt(list)