• 486 原型及原型链模式:3个重要知识点,从面向对象角度来讲解内置类,hasOwnProperty,原型链方法中的THIS问题,基于内置类的原型扩展方法


    滴滴考察原型、原型链面试题

    Function.prototype.a = () => {
      console.log(1)
    }
    Object.prototype.b = () => {
      console.log(2)
    }
    function A () {}
    const a = new A()
    a.a() // 报错
    a.b() // 2
    A.a() // 1
    

    3个重要知识点

    【构造函数是函数类型,实例对象是对象。】

    1. 每一个函数数据类型的值,都有一个天生自带的属性:prototype(原型),这个属性的属性值是一个对象(“用来存储实例公用属性和方法”)

      • 普通的函数
      • 类(自定义类和内置类)
    2. 在prototype这个对象中,有一个天生自带的属性:constructor,这个属性存储的是当前函数本身

      Fn.prototype.constructor === Fn // true
      
    3. 每一个对象数据类型的值,也有一个天生自带的属性:__proto__,这个属性指向“所属类的原型prototype”

      • 普通对象、数组、正则、Math、日期、类数组等等
      • 实例也是对象数据类型的值
      • 函数的原型prototype属性的值也是对象类型的
      • 函数也是对象数据类型的值
    /*
     * 类:函数数据类型
     * 实例:对象数据类型的
     */
    function Fn() {
      /*
       * NEW执行也会把类当做普通函数执行(当然也有类执行的一面)
       *   1.创建一个私有的栈内存
       *   2.形参赋值 & 变量提升
       *   3.浏览器创建一个对象出来(这个对象就是当前类的一个新实例),并且让函数中的THIS指向这个实例对象  => “构造函数模式中,方法中的THIS是当前类的实例”
       *   4.代码执行
       *   5.在我们不设置RETURN的情况下,浏览器会把创建的实例对象默认返回 
       */
      this.x = 100;
      this.y = 200;
      this.say = function () { }
    }
    
    Fn.prototype.eat = function () {
      console.log('吃饭睡觉打豆豆');
    }
    
    Fn.prototype.say = function () { }
    
    var f1 = new Fn();
    var f2 = new Fn();
    


    原型链查找机制

    1.先找自己私有的属性,有则调取使用,没有继续找
    2.基于__proto__找所属类原型上的方法(Fn.prototype),如果还没有则继续基于__proto__往上找...一直找到Object.prototype为止


    从面向对象角度来讲解内置类


    hasOwnProperty

    检测某一个属性名是否为当前对象的私有属性

    “in” :检测这个属性是否属于某个对象(不管是私有属性还是公有属性,只要是它的属性,结果就为TRUE)

    // 自己堆中有的就是私有属性,需要基于__proto__查找的就是公有属性(__proto__在IE浏览器中(EDGE除外)给保护起来了,不让我们在代码中操作它)
    let ary = [10, 20, 30];
    console.log('0' in ary);  // => TRUE
    console.log('push' in ary); // => TRUE
    console.log(ary.hasOwnProperty('0'));  // => TRUE
    console.log(ary.hasOwnProperty('push')); // => FALSE,"push"是它公有的属性,不是私有的
    
    // => TRUE,是公有属性,还是私有属性,需要看相对谁来说的
    console.log(Array.prototype.hasOwnProperty('push')); 
    console.log(Array.prototype.hasOwnProperty('hasOwnProperty')); // => FALSE
    console.log(Object.prototype.hasOwnProperty('hasOwnProperty')); // => TRUE   
    

    检测某个属性是否为对象的公有属性:hasPubProperty

    方法:是它的属性,但是不是私有的

    // 基于内置类原型扩展方法
    Object.prototype.hasPubProperty = function (property) {
      // => 验证传递的属性名合法性(一般只能是数字或字符串等基本值)
      let x = ["string", "number", "boolean"],
        y = typeof property;
      if (!x.includes(y)) return false;
      // => 开始校验是否为公有的属性(方法中的THIS就是要校验的对象)
      let n = property in this,
        m = this.hasOwnProperty(property);
      return n && !m;
    }
    console.log(Array.prototype.hasPubProperty('push')); // => FALSE
    console.log([].hasPubProperty('push')); // => TRUEa
    

    原型链方法中的THIS问题

    /*
     * 面向对象中有关私有/公有方法中的THIS问题
     *   1.方法执行,看前面是否有点,点前面是谁THIS就是谁 【确定this指向】
     *   2.把方法总的THIS进行替换 
     *   3.再基于原型链查找的方法确定结果即可
     */
    
    function Fn() {
      // => this:f1这个实例
      this.x = 100;
      this.y = 200;
      // 构造函数中有自己的属性、方法,则用构造函数中的,不用原型对象上的
      this.say = function () {
        // 普通函数的this,看谁调用的
        console.log(this.x);
      }
    }
    
    Fn.prototype.say = function () {
      console.log(this.y);
    }
    
    Fn.prototype.eat = function () {
      console.log(this.x + this.y);
    }
    
    Fn.prototype.write = function () {
      this.z = 1000;
    }
    
    let f1 = new Fn;
    f1.say(); // => this: f1  => console.log(f1.x)   => 100
    f1.eat(); // => this: f1  => console.log(f1.x + f1.y)   => 300
    
    // => this: f1.__proto__   => console.log(f1.__proto__.y),不找私有属性,在原型上找,原型上没有y属性,Object的原型上也没有   => undefined
    f1.__proto__.say();
    
    // => this: Fn.prototype   => console.log(Fn.prototype.x + Fn.prototype.y) ,undefined + undefined =>  NaN
    Fn.prototype.eat();
    
    // => this: f1   => f1.z=1000   => 给f1设置一个私有的属性z=1000
    f1.write();
    
    // => this: Fn.prototype   => Fn.prototype.z=1000   => 给原型上设置一个属性z=1000(属性是实例的公有属性)
    Fn.prototype.write();
    console.log(f1.z) // 1000
    
    


    基于内置类的原型扩展方法:数组去重

    /*
     * 基于内置类的原型扩展方法 
     *   在内置类原型上的方法,类所对应的实例可以直接调取使用,例如:实例.方法()  ary.push()
     *   如果我们也把自己写的方法放到原型上,那么当前类的实例也可以直接这样调取使用了,很方便
     * 
     * 但是也有需要注意的地方
     * 	 1.自己扩展的方法不能影响原有内置的方法(我们自己设置的方法最好加前缀: 如my)
     *   2.扩展方法中的THIS一般都是当前类的实例(也就是要操作的值):实例.方法()
     */
    
    // 补充
    let obj = { aa: 11, bb: 22 }
    console.log(obj.cc) // undefined
    
    ~ function () {
      /*
       * myUnique : 实现数组去重
       *   @params
       *   @return
       *      [Array] 去重后的数组
       * by 666 on 20190805
       */
      function myUnique() {
        // 此时没有传递要操作的ARY进来,但是方法中的THIS是当前要操作的数组,因为是该数组调用该方法:ARY.MYUNIQUE()
        let obj = {};
        for (let i = 0; i < this.length; i++) {
          let item = this[i];
          // 如果obj中没有item这一项,就是undefined,不等于undefined,说明有了
          if (typeof obj[item] !== 'undefined') {
            // (1)删除重复项,会把这一项后面的所有项都往前提一位,性能差;
            // (2)下一轮循环,i++,就会空出一位,防止出现塌陷问题,i--;
            // (3)最后一项拿过来,替换当前项,当前项就不能用了,然后把最后一项删除
            this[i] = this[this.length - 1];
            this.length--;
            i--;
            continue; // 存在了,就不往里存了
          }
          obj[item] = item;
        }
        obj = null;
        // 保证当前方法执行完返回的结果依然是ARRAY类的一个实例
        return this;
      }
      // => 扩展到内置类的原型上
      Array.prototype.myUnique = myUnique;
    }();
    
    let ary = [12, 23, 13, 12, 23, 24, 34, 13, 23];
    // ary.myUnique(); 返回去重后的数组(也是ARRAY类的实例)
    // ary.sort((a, b)  =>  a - b); 返回排序后的数组
    // => 链式写法(保证返回值依然是当前类的实例 一般都会RETURN THIS)
    // ary.myUnique().sort((a, b)  =>  a - b).reverse().slice(2).push('珠峰').concat(12);// => Uncaught TypeError: ary.myUnique(...).sort(...).reverse(...).slice(...).push(...).concat is not a function  执行完push返回的是一个数字(新增后数组的长度),不是数组了,不能在继续使用数组的方法
    ary.myUnique().sort((a, b) => a - b).reverse();
    console.log(ary);
    
    
    /* Array.prototype.push = function () {
      console.log("哈哈哈");
    }
    let ary = [1, 2, 3];
    ary.push(100); // => "哈哈哈"
    console.log(ary); // => 数组没变*/
    
    
    // --------------------------
    
    
    // 补充:我之前去重的老写法
    function myUnique(arr) {
      let obj = {}
      arr.forEach((item, index) => {
        obj[item] = item
      })
      // {12: 12, 13: 13, 23: 23, 24: 24, 34: 34, 嘻: "嘻", 好: "好", 哈: "哈", 呵: "呵"}
      console.log(obj)
      arr = []
      for (let k in obj) {
        arr.push(obj[k])
      }
      return arr
    }
    
    let arr = ['嘻', '好', 12, 23, 13, 12, 23, 24, 34, 13, 23, '哈', '呵', '嘻', '好', '哈', '呵'];
    let res = myUnique(arr)
    console.log(res) // [12, 13, 23, 24, 34, "嘻", "好", "哈", "呵"]
    

  • 相关阅读:
    iOS开发——高级篇——iOS抽屉效果实现原理
    iOS开发——高级篇——FMDB 数据库简单使用
    iOS开发——高级篇——多线程dispatch_apply
    iOS开发——基础篇——get和post请求的区别
    iOS开发——高级篇——线程同步、线程依赖、线程组
    谈谈程序员最讨厌做的事
    iOS开发——高级篇——iOS 项目的目录结构
    iOS开发——高级篇——Runtime实际应用
    程序猿如何“智斗”产品经理
    iOS开发——基础篇——iOS开发 Xcode8中遇到的问题及改动
  • 原文地址:https://www.cnblogs.com/jianjie/p/13199653.html
Copyright © 2020-2023  润新知