第十六章 new和instanceof手写实现

99 阅读2分钟

一、复习

  • 所有的 this.xxx的属性都是当前实例的私有属性
  • 约定的规范,原型上的方法中的this保证是当前类的实例
  • 一般我们会把方法类的属性放到原型上

【选项卡过渡写法】

function Tab(id) {
            //所有的 this.xxx的属性都是当前实例的私有属性
            //一般我们会把方法类的属性,放到原型上
            let ele = document.getElementById(id);
            let buttons = ele.getElementsByTagName('button')
            let divs = ele.getElementsByTagName('div')
            this.divs = divs;
            for(var i=0;i<buttons.length;i++){
                buttons[i].n = i;
                buttons[i].onclick = function(){
                    for(var j=0;j<divs.length;j++){
                        divs[j].style.display = 'none';
                    }
                    //console.log(this.n);
                    divs[this.n].style.display = 'block';
                }
            }
        }
        Tab.prototype.hideDivs = function(){

        }
        Tab.prototype.init = function(index){
            for(var i=0;i<this.divs.length;i++){
                this.divs[i].style.display = 'none';
            }
            this.divs[index].style.display = 'block'
        }
        let tab1 = new Tab('box')
        tab1.init(1)
  • 作用域:变量提升
    私有作用域:形参赋值 和声明过的
    上级作用域:只看函数是在哪个作用域形成的
  • 作用域链:变量的查找机制
  • 原型:创建类的时候会用到,当一个对象去调用一个属性的时候
  • 数字或者字符串能不能调用pop?
    数字是Number类的实例,pop是在Array.prototype上存在,调用不到
  • let p1 = new Person();//不需要实参的时候,可以省略()
 function Tab(id) {
            // 所有的 this.xxx的属性都是当前实例的私有属性
            // 一般 我们会把方法类的属性 放到原型上
            // this.qqq = function () {}
            // this ---> 
            let ele = document.getElementById(id)
            let buttons = ele.getElementsByTagName('button')
            let divs = ele.getElementsByTagName('div')
            this.divs = divs
            for (var i = 0; i < buttons.length; i++) {
                buttons[i].n = i;
                let _this = this; 
                buttons[i].onclick = function () {
                    _this.hideDivs(this.n)
                    /* for (var j = 0; j < divs.length; j++) {
                      // 让所有的div消失
                      divs[j].style.display = 'none'
                    }
                    // this --> 某一个button
                    // console.log(this.n) 对应就是点击的button的索引
                    divs[this.n].style.display = 'block' */
                }
            }
        }
        Tab.prototype.hideDivs = function (n) {
            //  约定的规范: 原型上的方法中的this保证是 当前类的实例;
            // this -- > tab1
            for (var i = 0; i < this.divs.length; i++) {
                this.divs[i].style.display = 'none'
            }
            this.divs[n].style.display = 'block'
        }
        Tab.prototype.qqq = function (index) {
            // 让索引未 index的div显示,其他隐藏
            // this ---> tab1
            console.log(this.divs)
            this.hideDivs(index)
        }
        let tab1 = new Tab('box')
        tab1.qqq(1) // qqq执行的时候 里边的this 是 tab1;
        console.log(tab1)
        let tab2 = new Tab('box2')
                tab2.qqq(2)

二、new

  • 代码 :p = new Person()
  • 执行过程:
    先开一个作用域,然后开辟一个堆内存,把this指向 这个堆内存,
    形参赋值 &变量提升-->代码执行;
  • 返回值:不是引用的话,就默认返回this

call

  • 代码:fn.call(xxx,a,b,c)
  • 参数:
    xxx:表示把fn函数中的this指向xxx;
    a,b,c:从第二个参数以后的当作实参给fn的形参传递

【手写实现new】

 //基础版
 function myNew(C, name) {
    let obj = {};
    obj.__proto__ = C.prototype
    // Person中的this指向这个堆
    let res = C.call(obj, name) // 需要判断C->Person返回的是是类型
    // res是call执行的返回结果;call的返回结果就是C的返回结果
    // res 就是 C的执行结果
    if (typeof res == 'object') {
      return res
    } else {
      return obj
    }
  }

  function Person(name) {
    this.name = name
  }
  let p1 = new Person("小明")
  let p2 = myNew(Person, '小明')
  let p3 = myNew(Person, '小明')
  p1.__proto__ === Person.prototype
  p2.__proto__ === Person.prototype
 function Person(name, age, sex) {
    this.name = name
    this.age = age;
    this.sex = sex;
  }
  let p1 = new Person("小康", 12, 1)
  let p2 = myNew(Person, "小康", 12, 1)

  function myNew(C, ...args) {
    let obj = {};
    obj.__proto__ = C.prototype;
    let res = C.call(obj, ...args)
    let res = C.apply(obj, args)
    return typeof res == 'object' ? res : obj
  }

三、instanceof

  • 代码:A instanceof B
  • 作用:从A到基类的prototype这条原型链上有没有B.prototype存在
  • 返回值:ture 或者false
//原理讲解
var ary = [];
  // console.log(ary instanceof Array) // true
  // 从ary 到 Object类的prototype 这条链上 有没有Array.prototype
  function myInstance(a, b) {
    // 从 a到Object类的prototype 这条链上有没有b.prototype
    // a.__proto__....__proto__ === b.prototype
    // a.__proto__ 是否是b.prototype
    // 若不是  a.__proto__.__proto__ 是否是b.prototype
    // 若还不是a.__proto__.__proto__.__proto__ 是否是b.prototype
    // 若有一层__proto__ == b.prototype 应该返回true
    // 若a.__proto__.。。。.__proto__ === null; 应该false
    let temp = a.__proto__;
    while (temp && temp !== b.prototype) {
      // temp存在 并且 temp不是b.prototype
      // 证明temp不是null;而且temp不等于b的原型
      //  下一步就是 看看a.__proto__.__proto__
      // temp.__proto__
      temp = temp.__proto__
    }
    // return temp ? true : false
    // 什么情况能跳出while
    // temp === null;
    // temp === b.prototype
    if (temp == null) {
      return false
    } else {
      return true
    }
  }
  console.log(myInstance(ary, Array)) // true
  console.log(myInstance({}, Object)) // true
  console.log(myInstance(ary, Number)) // false

【手写实现instanceof】

function instance_of(L, R) {
    //L 表示左表达式,R 表示右表达式
    L = L.__proto__; // 取 L 的隐式原型
    while (true) {
      if (L === null) return false;
      if (R.prototype === L)
        // 这里重点:当 O 严格等于 L 时,返回 true
        return true;
      L = L.__proto__;
    }
  }

\