zoukankan      html  css  js  c++  java
  • module1-04-实现 new、apply、call、bind 的底层逻辑

    实现 new、apply、call、bind 的底层逻辑

    • apply、call、bind在前端代码开发中非常重要,在很多时候,可以通过这些来节约内存。这些包括new都与this的指向密切相关。所以必须要了解这些方法

    • 思考题:

      • ① 用什么样的思路可以new关键词?

      • ② apply、call、bind这三个方法之间有什么区别?

      • ③ 怎么样实现一个apply或者call的方法?

    一、new

    • 这个的关键词主要作用是执行一个构造函数,

    • 这个结果比较简单相信大家都知道,接下来用一段代码来过

    function Person() {
      this.name = 'Jack';
    }
    var p = new Person();
    console.log(p.name)  // Jack

    (1)new在生成实例过程中分几个步骤呢?

    • ① 创建一个新对象

    • ② 将构造函数的作用于赋予新对象(this指向新对象)

    • ③ 执行构造函数中的代码(为这个新对象添加属性)

    • ④ 返回新对象

    (2)如果去掉new会怎么样的?

    • 去掉new就像我们普通使用的函数一看,返回值返回什么就是什么,而且this指向的是window

    • 如:

    function Person(){
     this.name = 'Jack';
    }
    var p = Person();
    console.log(p) // undefined
    console.log(name) // Jack
    console.log(p.name) // 'name' of undefined
    • 可以看出不使用new的话就会把this指向window,并且在window添加属性

    (3)当函数有返回值的话呢?

    function Person(){
      this.name = 'Jack';
      return {age: 18}
    }
    var p = new Person();
    console.log(p)  // {age: 18}
    console.log(p.name) // undefined
    console.log(p.age) // 18
    • 用一段代码感受一下

    • 当return出来的是一个和this无关的对象的时候,new会直接返回这个新对象,而不是它通过new执行步骤生成的this对象

    • 这里要求构造函数必须返回的是一个对象如果不是对象,还是会按照new的实现步骤,返回新生成的对象

    function Person(){
      this.name = 'Jack';
      return 'tom';
    }
    var p = new Person();
    console.log(p)  // {name: 'Jack'}
    console.log(p.name) // Jack
    • 即使有了return,也还是执行new的操作

    (4)自己实现的new

    function _new(ctor, ...args) {
     if (typeof ctor !== 'function') throw 'ctor must be a function';; // 必须为一个函数
       
     let obj = {}; // 创建一个空对象
     Reflect.setPrototypeOf(obj, ctor.prototype); // 绑定原型
     let res = ctor.apply(obj, args); // 这里做了两件事, 1. 获取ctor的返回值 2. 将ctor的this指向obj并执行里面的代码,包括往this添加属性

     return typeof res === 'function' || (typeof res === 'object' && typeof res !== 'null') ? res : obj;
    } // 判断是否为一个对象

    二、apply & call & bind 原理介绍

    • 先来了解一下这三个方法的基本情况,call、apply、bind是挂在Function对象上的三个方法,调用这三个方法的必须是一个函数

    • 这三种方法的基本语法

    func.call(thisArg, param1, param2, ...)
    func.apply(thisArg, [param1,param2,...])
    func.bind(thisArg, param1, param2, ...)
    • 这三个方法共有的作用就是,都可以改变func的this指向,call和apply的区别在于传入的参数不同

      • apply需要的是数组

      • call传入的是参数列表

    • 而bind的区别就是在于它不会立刻执行函数

      • 结合一段代码感受一下

    let a = {
     name: 'jack',
     getName: function(msg) {
       return msg + this.name;
    }
    }
    let b = {
     name: 'lily'
    }
    console.log(a.getName('hello~'));  // hello~jack
    console.log(a.getName.call(b, 'hi~'));  // hi~lily
    console.log(a.getName.apply(b, ['hi~']))  // hi~lily
    let name = a.getName.bind(b, 'hello~');
    console.log(name());  // hello~lily

    (1)应用场景

    • 比如判断数据类型的时候,借用Object.prototyoe的toString方法

    • 类数组项进行数组操作的时候借用数组的方法

    • 获取最大最小值(Math的方法)

    • 继承中改变this执行

    (2)apply和call的实现

    • 因为两个基本相像所以放在一起,主要通过eval来实现的

    Function.prototype.myCall = function (content, ...args) {
     var content = content || window; // 没有制定content则默认window,使用var是因为要绑定到全局中
     content.fn = this; // 使要指向的对象添加一个fn方法
     let result = eval('content.fn(...args)'); // 使用eval调用该方法
     delete content.fn; // 完成之后删除引用
     return result;
    }
    Function.prototype.myApply = function (content, args) {
     var content = content || window;
     content.fn = this;
     let result = eval('content.fn(args)');
     delete content.fn;
     return result;
    }
    // 同上

    (3)bind的实现

    • 基本思路与apply和call一样,但是这里最后返回的结果不同

    Function.prototype.myBind = function (content, ...args) {
      if (typeof this !== 'function') throw 'this must be a function'; // 必须是一个函数
      const self = this; // 将这个函数的this指向保存在self
      const fbind = function () {
        self.apply(this instanceof self ? this : content, Array.prototype.concat.apply(args, arguments));
      } // 定义的这个新函数里面执行的就是一个apply, self中的函数执行,然后在这过程中改变this指向为content
      if (this.prototype) Reflect.setPrototypeOf(fbind, this.prototype); // 将原型对象赋予给fbind
      return fbind;
    }
    

    (4)总结

    • 用一张图来表示

  • 相关阅读:
    Dash panel 里面添加启动项
    Ubuntu安装chrome
    多核CPU服务器 tomcat配置
    Iptux 信使 自动缩小问题 ubuntu12.04
    Html5 上传文件
    ubuntu 12.04 字体设置
    Ubuntu12.04 Eclipse 背景颜色 修改
    一些需要禁用的PHP危险函数
    Oracle 修改带数据的字段类型
    oracle 中同一个字段1 ,字段追加,字段部分数据删除
  • 原文地址:https://www.cnblogs.com/lezaizhu/p/14455034.html
Copyright © 2011-2022 走看看