zoukankan      html  css  js  c++  java
  • (七)-前端-原型链

    7.原型及原型链

    原型

    - 函数都带有一个prototype 属性,这是属性是指向构造函数的原型对象,这个对象包含所有实例共享的属性和方法。

    - 原型对象都有一个constructor 属性,这个属性指向所关联的构造函数。

    - 每个对象都有一个__proto__ 属性[非标准的方法],这个属性指向构造函数的原型 prototype

    原型链

    - 当访问实例对象的某个属性时,会先在这个对象本身的属性上查找,如果没有找到,则会 通过 proto 属性去原型上查找,如果还没有 找到则会在构造函数的原型的__ proto__中查 找, 这样一层层向上查找就会形成一个作用域链,称为原型链

    原型相关习题

    ![img](file:///C:/Users/admin/AppData/Local/Temp/msohtmlclip1/01/clip_image006.png)

    ![img](file:///C:/Users/admin/AppData/Local/Temp/msohtmlclip1/01/clip_image007.png)

    Object.create****的作用:

    let obj = {a:123};
    let o = Object.create(obj);
    //该函数返回了一个新的空对象,但是该空对象的__proto__是指向了obj这个参数
    // 手写Object.create
    function create(proto) {
      function F() {}
      F.prototype = proto;
     
      return new F();
    }
    

    new****的执行过程是怎么回事?

    new操作符做了这些事:

    · 它创建了一个全新的对象

    · 它会被执行[[Prototype]](也就是__proto__)链接

    · 它使this指向新创建的对象

    · 通过new创建的每个对象将最终被[[Prototype]]链接到这个函数的prototype对象上

    · 如果函数没有返回对象类型Object(包含Functoin, Array, Date, RegExg, Error),那么new表达式中的函数调用将返回该对象引用

    //模拟new

    function objectFactory() {
    
     const obj = new Object();
    
     const Constructor = [].shift.call(arguments);
    
     
    
     obj.__proto__ = Constructor.prototype;
    
     
    
     const ret = Constructor.apply(obj, arguments);
    
     
    
     return typeof ret === "object" ? ret : obj;
    
    }
    
     
    

    call,apply,bind****三者的区别?

    **
    ** apply() 方法调用一个函数, 其具有一个指定的this值,以及作为一个数组(或类似数组的对象)提供的参数fun.apply(thisArg, [argsArray])

    apply 和 call 基本类似,他们的区别只是传入的参数不同。

    apply 和 call 的区别是 call 方法接受的是若干个参数列表,而 apply 接收的是一个包含多个参数的数组。

    bind()方法创建一个新的函数, 当被调用时,将其this关键字设置为提供的值,在调用新函数时,在任何提供之前提供一个给定的参数序列。

    call做了什么:

    将函数设为对象的属性

    执行&删除这个函数

    指定this到函数并传入给定参数执行函数

    如果不传入参数,默认指向为 window

    //实现一个call方法:
    
    Function.prototype.myCall = function(context) {
    
     //此处没有考虑context非object情况
    
     context.fn = this;
    
     let args = [];
    
     for (let i = 1, len = arguments.length; i < len; i++) {
    
      args.push(arguments[i]);
    
     }
    
     context.fn(...args);
    
     let result = context.fn(...args);
    
     delete context.fn;
    
     return result;
    
    };
    
    / 模拟 apply
    Function.prototype.myapply = function(context, arr) {
      var context = Object(context) || window;
      context.fn = this;
     
      var result;
      if (!arr) {
        result = context.fn();
      } else {
        var args = [];
        for (var i = 0, len = arr.length; i < len; i++) {
          args.push("arr[" + i + "]");
        }
        result = eval("context.fn(" + args + ")");
      }
     
      delete context.fn;
      return result;
    };
     
     
    实现bind要做什么
    返回一个函数,绑定this,传递预置参数
    bind返回的函数可以作为构造函数使用。故作为构造函数时应使得this失效,但是传入的参数依然有效
     
    // mdn的实现
    if (!Function.prototype.bind) {
      Function.prototype.bind = function(oThis) {
        if (typeof this !== 'function') {
          // closest thing possible to the ECMAScript 5
          // internal IsCallable function
          throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
        }
     
        var aArgs   = Array.prototype.slice.call(arguments, 1),
            fToBind = this,
            fNOP    = function() {},
            fBound  = function() {
              // this instanceof fBound === true时,说明返回的fBound被当做new的构造函数调用
              return fToBind.apply(this instanceof fBound
                     ? this
                     : oThis,
                     // 获取调用时(fBound)的传参.bind 返回的函数入参往往是这么传递的
                     aArgs.concat(Array.prototype.slice.call(arguments)));
            };
     
        // 维护原型关系
        if (this.prototype) {
          // Function.prototype doesn't have a prototype property
          fNOP.prototype = this.prototype; 
        }
        // 下行的代码使fBound.prototype是fNOP的实例,因此
        // 返回的fBound若作为new的构造函数,new生成的新对象作为this传入fBound,新对象的__proto__就是fNOP的实例
        fBound.prototype = new fNOP();
     
        return fBound;
      };
    }
     
     
    // 简单版
        Function.prototype.myBind = function(context,...arg){
            // this --->  fn
            var _this = this;
            return function(...ary){
                // _this(...arg)
                return _this.apply(context,arg.concat(ary))
            }
        }
    

    实现类的继承

    类的继承在几年前是重点内容,有n种继承方式各有优劣,es6普及后越来越不重要,那么多种写法有点『回字有四样写法』的意思,如果还想深入理解的去看红宝书即可,我们目前只实现一种最理想的继承方式。

    function Parent(name) {
        this.parent = name
    }
    Parent.prototype.say = function() {
        console.log(`${this.parent}: 你打篮球的样子像kunkun`)
    }
    function Child(name, parent) {
        // 将父类的构造函数绑定在子类上
        Parent.call(this, parent)
        this.child = name
    }
    /** 
     1. 这一步不用Child.prototype =Parent.prototype的原因是怕共享内存,修改父类原型对象就会影响子类
     2. 不用Child.prototype = new Parent()的原因是会调用2次父类的构造方法(另一次是call),会存在一份多余的父类实例属性
    3. Object.create是创建了父类原型的副本,与父类原型完全隔离
    */
    Child.prototype = Object.create(Parent.prototype);
    Child.prototype.say = function() {
        console.log(`${this.parent}好,我是练习时长两年半的${this.child}`);
    }
    // 注意记得把子类的构造指向子类本身
    Child.prototype.constructor = Child;
    var parent = new Parent('father');
    parent.say() // father: 你打篮球的样子像kunkun
    var child = new Child('cxk', 'father');
    child.say() // father好,我是练习时长两年半的cxk
    

    谈谈你对this指向的理解

    this 的指向,始终坚持一个原理:this 永远指向最后调用它的那个对象

    改变 this 的指向我总结有以下几种方法:

    · 使用 ES6 的箭头函数

    · 在函数内部使用 _this = this

    · 使用 apply、call、bind

    · new 实例化一个对象

    全局作用域下的this指向window

    如果给元素的事件行为绑定函数,那么函数中的this指向当前被绑定的那个元素

    函数中的this,要看函数执行前有没有 . , 有 . 的话,点前面是谁,this就指向谁,如果没有点,指向window

    自执行函数中的this永远指向window

    定时器中函数的this指向window

    构造函数中的this指向当前的实例

    call、apply、bind可以改变函数的this指向

    箭头函数中没有this,如果输出this,就会输出箭头函数定义时所在的作用域中的this

    ---------------------------
    作者:HelloBytes
    关于作者: JavaEE小新人,请多多赐教!
    本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。
  • 相关阅读:
    测试思想-流程规范 关于预发布环境的一些看法
    Jenkins 开启用户注册机制及用户权限设置
    Jenkins 利用Dashboard View插件管理任务视图
    Loadrunner 脚本开发-从文件读取数据并参数化
    SVN SVN合并(Merge)与拉取分支(Branch/tag)操作简介
    测试思想-流程规范 SVN代码管理与版本控制
    Python 关于Python函数参数传递方式的一点探索
    接口自动化 基于python+Testlink+Jenkins实现的接口自动化测试框架[V2.0改进版]
    Python 解决Python安装包时提示Unable to find vcvarsall.bat的问题
    lintcode :链表插入排序
  • 原文地址:https://www.cnblogs.com/HelloBytes/p/14177961.html
Copyright © 2011-2022 走看看