JS基础之继承和This

继 承

原型继承

  • 让类B的原型指向类A的实例,那么以后类B的实例既可以调取类A实例的私有属性,也可以调取类A实例的公有属性,那这种继承方式就是原型继承
    • 原型继承:继承私有和公有
      • 通过改变prototype的指向,使其指向其他实例
       function A(){
           this.getX = function(){console.log('恭喜发财')}
       };
       A.prototype.getY = function(){
           console.log('真好')
       };
       function B(){}
       B.prototype = new A;
       let f = new B;

image.png

中间类继承

  • arguments类数组,不是一个数组;虽然不是Array的实例,但是我们可以手动把arguments的__proto__指向Array的原型,那这样arguments就可以使用Array原型上的方法了,这就是中间类继承
    • 只能继承公有属性
      • 通过自己的__proto__指向类的prototype(原型)
        function fn(){
           console.log(arguments instanceof Array)
           arguments.__proto__ = Array.prototype;
           console.log(arguments.push(23))
            console.log(arguments) 
        }
        fn(1,2,3,4,5)

call继承

  • call继承:私有属性
    • 改变类里的this指向
    • 在类B中,调用了类A,并且通过call改变了类A中的this指向,使其指向B的实例;这样类B创建的实例就具有类A的私有属性;这种继承就是call继承;
    • call方法在Function的原型上
        /* 
        原型继承:继承私有和公有
        中间类继承:公有
        call继承:私有属性
        */
        function A(){
            this.x =10 
        }
        A.prototype.getX = function(){
            console.log('万事如意')
        }

        function B(){
            // this->当前实例
            /* 
            类B当做构造函数执行时,此时的this是当前实例,
            */
            this.a = 20
            A.call(this)  // 把函数A当做普通函数执行,并且把A的this指向了类B的实例

        }
 //call继承让类B继承了类A的私有属性,但是不能使用类A的公有属性
// 类B的所有实例都可以使用类A的私有属性
        let f = new B;
        console.log(f)
 		f.getX()

寄生组合继承

  • 继承公有和私有
    • 创建一个空对象,让空对象的__proto__指向(传第一个参)类A的原型,在把这个空对象赋值给类B的原型
    • 使用call继承继承了私有属性,Object.create继承了公有属性,这种继承方式就是寄生组合继承;
    • 为了防止修改B的原型时,修改了A的原型,所以使用Object.create的方法
        // Object.create(context): // 创建一个空对象,让对象的__proto__指向你传的第一个参数
        let obj = {name:3,getX:function(){console.log(11)}}
        let o = Object.create(obj)
        console.log(o)
  • 实例
        // Object.create(context): // 创建一个空对象,让对象的__proto__指向你传的第一个参数
        // let obj = {
        //     name: 3,
        //     getX: function () {
        //         console.log(11)
        //     }
        // }
        // let o = Object.create(obj)
        // console.log(o);
        // console.log(o.__proto__ === obj) // true
        // 创建一个空对象,让空对象的__proto__指向你传递的第一个参数(obj)


        function A(){
            this.a = 10
        }
        A.prototype.getX = function(){
            console.log('恭喜发财')
        }

        function B(){
            /* 
            函数B以构造函数的身份运行
            那类B中的this指向当前实例
            */
            this.x =20;
            A.call(this) // 让函数A以普通函数身份运行,而且把函数A中的this指向了类B的实例
          // 继承私有属性
        }
        B.prototype = Object.create(A.prototype);// 继承公有属性;
        // 创建一个空对象,让空对象的__proto__指向类A的原型,在把这个空对象赋值给类B的原型
        let f = new B;
        f.__proto__.getY = function(){
            console.log(333)
        };
        let m = new A;
        // m.getY()
        console.log(f)
        //call继承让类B继承了类A的私有属性,然是不能继承类A的公有属性
        // console.log(f)
        // f.getX() // 报错
        // f.a // 可以取到

在这里插入图片描述

Class继承

  // ES6中class创造出来的类不能当做普通函数执行
        class A {
            constructor(q) {
                this.x = q;
            }
            getX() {
                console.log(this.x)
            }
        }
        
        // ES6中的继承
        class B extends A {
            constructor(name) {
                // 子类继承父类,可以不写constructor,但是你要是一旦写了,
              那在constructor里第一句话就要写super()
                // 你要是不写constructor,那浏览器会默认创建一个constructor(...arg){
                //     super(...arg)
                // }
                super(200) // A.call(this, 200) 把父类当做普通函数执行,给方法传递参数,
              让方法中的this是子类的实例

                this.y = 100;
            }
            getX() {
                console.log(this.y)
            }
        }
        
        B.prototype = Object.create(A.prototype); // class定义的类不能改原型重定向
        let f = new B(100);
        console.log(f)
  • B.prototype = Object.create(A.prototype); QQ图片20191223154557.png

This

this详解

  • 跟函数执行有关系
  • 他是js中的关键字,有特殊的特殊意义
  • 他就是函数的执行体,谁执行函数this就是谁
  • 不能给this直接赋值
  • this传的是指针,空间地址
  • this是个关键字;在特殊的情景下,this有特殊的意义;this不能用等号对其直接修改

this的几种情况

1.在全局作用于下,this就是window
2.在函数执行时,看执行函数前有没有".",如果有点,那点前面是谁,this就是谁,如果没有点,那this就是window
3.自执行函数里的this是window
4.给元素事件行为绑定方法,方法里的this指向被绑定的元素本身
5.回调函数里的this一般指向window
6.实例的私有属性或者公有属性里的this一般指向当前实例
7.构造函数里的this是当前实例
8.箭头函数没有this,要是在箭头函数里使用this,就看他上一级作用域的this,不能用call更改,不能被new
9.call、apply、bind可以改变this的指向

        //2给元素的事件绑定的函数中的this,指向了当前被点击的那个元素
        box.onclick = function () {
          // this : 对象
        console.dir(this === box); //空间地址相同
        box.style.color="red";
        this.style.color = "red";
        this=100;//this 不能放在等号左边
        }
        box1.onclick = function () {
            //this : 对象
            console.dir(this);
        }

		//5 回调函数中的this 一般指向window
        setTimeout(function () {
            console.log(this);

        }, 1000)

        var ary = [1, 2, 3, 4];
        ary.map(function () {
            console.log(this);

        }) 
		//回调函数的特殊
		//回调函数但是this更改了
        function A(){
            console.log(this);
        }
        function B(a){
            // a();
            var obj ={a:a};
            obj.a();
        }
        B(A);


/*     //  var let = 3;
            //  console.log(let)

                // console.log(this)
                // console.log(this === window)
                // window.a = 12;
                // console.log(this.a) */

        /*   // function fn(){
          //     console.log(this)
          // }
          // fn()


          // var age = 15;
          // var obj = {
          //     age: 13,
          //     name: function(){
          //         console.log(this.age)
          //     }
          // }
          // obj.name() // this是obj
          // var f = obj.name;
          // f()  // this是window*/

        // (function(){
        //     console.log(this) // window
        // })()

        // box.onclick = function(){
        //     console.log(this) 
        // }
        // var ary = [1, 2];
        // ary.map((a,b)=>{
        //     console.log(this) // window
        // })
        // ary.sort((a,b)=>{
        //     console.log(this); // window
        //     return a-b
        // })

        // function fn(a){
        //     a()
        // }
        // fn(function(){
        //     console.log(this)
        // })

        // setTimeout(()=>{
        //     console.log(this) // window
        // }, 2000)


        /*         var num = 100;
                var obj = {
                    num: 2,
                    fn: function () {
                        var num = 1;
                        console.log(this) //window
                        (function (num) { // 100
                        // this-> window
                            console.log(this.num + num);//200
                        })(this.num)
                        // this->window
                    }
                }
                // obj.fn(); 
                var f = obj.fn;
                f() */


        var num = 1; // 1 2
        var obj = {
            num: 0,
            fn: function () {
                num = 1;
                // this=>obj
                (function (num) {  // 0 1
                // this=>window
                    ++this.num;
                    num++;
                    console.log(num)
                })(this.num) 
            }
        }
        obj.fn(); 
        console.log(window.num, obj.num);

改变this指向的方法

每一个函数都是Function的实例,所以每一个函数都可以调取Function原型上的方法,call,apply,bind,他们三个都可以改变函数里的this指向

call

  • call继承:私有属性
  • fn通过__proto__属性找到当前所属类的原型(Function的原型)上的call方法
  • 所有函数可以获取到call
  • 让call方法执行,并且给call传递实参
  • 在call方法执行的同时,也让fn执行,并且把fn的this指向了第一个参数
  • 注意事项
    • 在严格模式下,如果call不传参或者传undefined,那fn的this就是undefined,如果传null,那fn的this就是null
    • 在非严格模式下,如果call不传参或者传undefined或者传null,那fn的this都是window
    • call的第一个参数是fn的this指向,从第二个开始,就是fn的正常参数了
  • fn通过__proto__先找到Function原型中的call方法,让call方法执行,call运行时,改变了call的this的this指向,fn中的this指向call的第一个参数,并且让call中this执行;
        function fn1(){
            console.log(100);
            console.log(this);
        }
        function fn2(){
            console.log(200);
        }
        //fn.call(1)
        fn1.call.call.call.call(fn2);
        // 1. fn1.call.call.call -->this--> fn2;
        // 2. fn1.call.call.call()

        // 1.fn2--> this 没有变
        // 2.fn2();

        // 1.先执行后面的call方法(这个call方法中的this是fn1.call);
是改变fn1.call中的this指向fn2;并且让fn1.call运行;
        // 2. 当fn1.call运行时,改变fn.call中的this的this指向没有发生改变,
继续让fn1.call中this执行,也就是让fn2运行;
-------------------------------------------------------
    function B(){

    } 
    console.log(B.name);// 对象

    function A(){

    }
    A.call()
    function B(){

    }
    A.call.call.call(B);// B执行,并且B中的this一定指向window
    最后一个call执行时, 把A.call.call中的this改成了函数B;并且让A.call.call执行;A.call.call执行时,把函数Bthis改成window,并且让B执行;
      //  "use strict"   
      function fn(n,m){
          console.log(this)
      }
      let obj = {
          name:3
      }
    //   fn(12,13)
    // 实现把函数里的this改为obj
    //   obj.fn = fn;
    //   obj.fn();
    //   delete obj.fn;
      fn.call(undefined)

call方法封装

        function myCall(context, ...arg) { // 收缩运算符
          // arg接收的是传递的从第二个开始的实参
            // this->fn    context->obj
            let res = null; // 初始化一个实例的返回值
            context = context || window 
          // 处理传参的特殊情况,如果传的是空、null和undefined,context的值就是window
            context.$fn = this // 把当前实例放到对象里
            res = context.$fn(...arg); // 让this执行(让当前实例执行)
            delete context.$fn // 在对象里删除那个实例
            return res;// 把this执行之后的返回值return 出去
        }
        Function.prototype.myCall = myCall;
        function fn() {
            console.log(this)
            return 1
        }
        let obj = {
            name: 3
        }
        //    console.log(fn.call(1)) 
        console.log(fn.myCall(obj, 12, 23))
        console.log(obj)

连call面试题

  • 如果有两个及以上call,那最后就是执行传入的参数
<script>
        function fn1() {console.log(1)}
        function fn2() {console.log(2)}
        // fn1.call(fn2); //1
        // fn1.call.call(fn2); //2 不管前边有多少call,他执行的是最后一个call方法
        // Function.prototype.call(fn1); // 不输出
        // Function.prototype.call.call(fn1); // 1
        Function.prototype.call.call.call.call(fn1);
        function myCall(context){
            // 如果有两个及以上call,那最后就是执行传入的参数,传入的参数的this指向window
        
            
            /* 
            fn1.call(fn2);
            context->fn2  this->fn1
            context.$fn = this  ->fn2.$fn = fn1
            context.$fn()  ->fn1()
            */

            /* 
            fn1.call.call(fn2)
            context ->fn2  this-> fn1.call ->call
            context.$fn = this ->fn2.$fn = call
            context.$fn()  ->fn2.$fn()  ->call()
            第二次执行
            context->window  this->fn2
            context.$fn = this ->window.$fn = fn2;
            context.$fn()  ->window.$fn()  ->fn2()
            */

            /* 
            Function.prototype.call(fn1)
            context ->fn1   this->Function.prototype【原型】
            context.$fn = this ->fn1.$fn = 【原型】
            context.$fn() ->fn1.$fn() ->【原型】()
            */

           /* 
           Function.prototype.call.call(fn1)
           context ->fn1  this -> Function.prototype.call ->call
           context.$fn = this  ->fn1.$fn = call
           context.$fn() ->fn1.$fn()  ->call()
           第二次执行
           context->windwo  this->fn1
           context.$fn = this ->window.$fn = fn1;
           context.$fn()  ->window.$fn  ->fn1()
           */
            context.$fn = this;
            context.$fn()
        }
    </script>

apply

  • 改变this指向:他和call方法一样,只不过传参不同,第二个参数必须是数组或者类数组
  • 传入数组,但是Fn实际接收的仍然是一个一个接收;
        function fn(a,b){
            console.log(a,b);
            console.log(this);   
        }
        fn.apply(null,[100,200])
----------------------------------------
				function fn(n,m){
            console.log(this, n, m)
        }
        fn.apply(1, [20,30])

bind

  • 预处理this
  • 这个方法也是改变this指向的,但他会提前改变实例函数的this指向,并不会让实例函数执行,他的返回值是改变this之后的新函数
  • 在bind函数中将fn进行了包装和处理,改变了fn里面的this指向,并且返回一个改变this之后的新函数
  • bind在IE8以下不兼容,bind正常传参
        function fn(a,b){
            console.log(a,b);
            console.log(this);   
        }
        var f = fn.bind([1,2]);
        f();
        var  f =fn.bind([1,2]);
        f(100,200);
        fn();	
--------------------------------------------------------
			<div id="box">1111</div>
            // let box = document.getElementById('box');
            let fn = function(){
                console.log(this)
            }
            let obj = {}
            // box.onclick  = fn.bind(obj)
          	fn = fn.bind(obj)//不改变原有函数,需要重新赋值
          	fn()

猜你喜欢

转载自blog.csdn.net/Sheng_zhenzhen/article/details/106797831