zoukankan      html  css  js  c++  java
  • this指向及改变this指向的方法

    this的指向在函数定义时是确定不了的,只有在函数执行时才能确定,this最终指向调用它的对象。

    this在js中主要有四种用法:

    1、作为普通函数使用

    2、作为对象方法来使用

    3、call和apply

    4、作为构造函数来使用

    1、作为普通函数使用

    var name='window';
    function s(){
        var name='myself';
        console.log(this.name);//window
    }
    s();

    2、作为对象方法来使用

    var name='window';
    var obj={
        name:'obj',
        sayName:function(){
            console.log(this.name);
        }
    }
    obj.sayName();//obj;

    这个很简单,this指向自己,所以this.name就用hello;

    (在全局里面this指向window,在某个对象里面this指向该对象,在闭包里面this指向window)

    var user="the Window";
    var box={
      user:'the box',
      getThis:function(){
        return this.user;
      },
      getThis2:function(){
        return function (){
          return this.user;
        }
      }
    };
    alert(this.user);//the Window
    alert(box.getThis());//the box
    alert(box.getThis2()());//the Window (由于使用了闭包,这里的this指向window)
    alert(box.getThis2().call(box));//the box 对象冒充(这里的this指向box对象)

    3、call和apply

    所有函数对象都有两个方法:apply和call,这两个方法可以让我们构建一个参数数组传递给调用函数,也允许我们改变this值。

    使sayName中的this指向b,改变this的指向
    var name='window';
    var obj={
        name:'obj',
        sayName:function(){
            console.log(this.name);
        }
    }
    var b={name:'abcd'};
    //改变this指向
    var newobj=obj.sayName;
    newobj();//将this指向全局
    
    newobj.call(b);//abcd   将this指向b   改变this的指向并且执行调用函数

    4、作为构造函数来使用

    function test(){
        this.name=1;
    }
    var myobj=new test();
    console.log(myobj.name);

    new操作符具体干了什么呢?

    var Func=function(){  }; 

    var f=new Func ();  

    new共经过了4几个阶段:

    1:创建一个空对象 var obj=new Object(); 

    2:设置原型链,让空对象的__proto__指向函数的原型prototype。  obj.__proto__= Func.prototype;  

    3:让Func()中的this指向空对象obj,并执行Func()的函数体;var result =Func.call(obj);  

    4:判断Func()的返回值类型

      如果是值类型,返回obj。如果是引用类型,就返回这个引用类型的对象。

      使用new来创建对象时,如果return的是非对象(数字、字符串、布尔类型、null等)或没有返回值时会忽略该返回值,返回的是新创建的对象  ;如果return的是对象,则返回该对象。

    复制代码
    (1)返回的是非对象
    function Person(name){
        this.name=name;
        return name;
    }
    var p=new Person('tom');
    console.log(p);//Person {name: "tom"}
    (2)返回的是对象
    function Person(name){
        this.name=name;
        return {'name1':name};
    }
    var p=new Person('tom');
    console.log(p);//{name1: "tom"}    
    复制代码

    构造函数与普通函数的区别

     构造函数也是一个普通函数,创建方式和普通函数一样。

    (1)调用方式:构造函数在调用时使用new关键字   new Fun();普通函数调用则直接调用fun()

    (2)this指向:在构造函数内部,this指向的是构造出来的新对象;在普通函数内部this指向window全局对象

    (3)return返回值:构造函数会默认返回this,也就是新的实例对象;普通函数如果没有return,返回undefined,如果使用了return,那么返回值会根据return的类型而判断。

    练习题

    在执行person1.sayName()时,this指向person1对象

    在执行person2.sayName()时,sayName()方法并没有执行,而是将sayName()赋值给fun变量,fun()是普通函数调用模式,this指向window,所以输出全局name

     

    执行console.log(b.n)时,b对象有自己的属性n值

    执行console.log(c.n)时,c对象没有自己的属性n值,会向上查找,找的A对象中的属性n值

     

    var getColor=test.getColor相当于把方法函数赋值给全局变量,

    故getColor()中的this指向window

    二、改变this指向的方式

    以下属于函数的方法

    改变this的指向并且执行调用函数

    (1)通过一个对象的方法来定义函数,并用该对象调用方法。

    var name='window';
    var obj={
        name:'obj',
        sayName:function(){
            console.log(this.name);
        }
    }
    var b={};
    b.x='is o'
    b.fun=obj.sayName;
    b.fun();

    (2)call()和apply()

      call()方法存在于Function的原型上,Function.prototype.call(),因此每个函数都可以通过原型链继承下来。属于函数的方法,只有函数对象可以调用

    相同点:两个方法产生的作用是完全一样的,都用来改变当前函数调用的对象。

    不同点:调用的参数不同,比较精辟的总结:

        foo.call(this,arg1,arg2,arg3) == foo.apply(this, arguments)==this.foo(arg1, arg2, arg3)

    1.call的使用

      call 方法可以用来代替另一个对象调用一个方法。call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。如果没有提供 thisObj 参数,那么 Global 对象被用作 thisObj。

    window.color="red";
    var o={
      color:"blue"
    };
    function sayColor(){
      console.log(this.color);
    }
    sayColor();//指向window
    sayColor.call(this) ; //指向自己
    sayColor.call(window); //red
    sayColor.call(o); //blue

    解析:最后一个之所以输出的是blue,是因为call()方法的第一个参数指的是在其中运行函数的作用域,所以在sayColor.call(o)中,它把函数的作用域定在了对象o中,所以函数sayColor()中的this.color即为o.color,因此输出的是对象o的color的值bule. 

    2.apply()

      apply与call的功能几乎一样,第一个参数意义都一样,只是第二个参数有点不同apply传入的是一个参数数组,也就是将多个参数组合成为一个数组传入,call从第二个参数开始,依次传值给调用函数的参数 

     call、apply与bind的区别:前两个可以自动执行,bind只创建一个新的函数,不会自动执行,需要手动调用。

    call()会比apply()执行速度快,主要是因为在apply方法中,对数组参数有多次判断,执行步骤比call多。

    (3)bind()

    bind()会创建一个函数的实例,其this值会被绑定到传给bind()函数的值。如

    var color='red';
    var o1={
        color:'blue'
    };
    var o2={
        color:'yellow'
    };
    function sayColor(){
        console.log(this.color);
    }
    //call()自动执行
    sayColor.call(o1);//blue
    
    //bind()需手动执行
    var say=sayColor.bind(o2);
    say();//yellow

    bind()除了改变函数this指向的功能,还有柯里化的功能,即把一个函数拆成多个单元。

    柯里化就是把一个多参数的函数,转换为单参数函数。只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。

    function add(a,b,c){
        console.log(a+b+c);
    }
    
    var func=add.bind(undefined,100);//第一个参数为undefined、null指不需要改变this
    func(1,2);//103    绑定时传入一个参数100,在调用时传入第二个和第三个参数
    
    var func2=func.bind(undefined,200);
    func2(10);//310      之前绑定a=100,所以200绑定到b上

    为什么要使用柯里化呢?

    比如需要写一个获取一套配置的函数,不同页面下的配置可能是不同的,在同一模块下,其中有些参数是相同的,不同页面获取时只需要传入otherOptions即可,把一个函数拆分成子函数,使代码重用。

    bind与new

    function foo(){
        this.b=100;
        return this.a;
    }
    var func=foo.bind({a:1});
    console.log(func());//1
    console.log(new func());//{b:100}

    使用new 函数时,函数中的return除非是对象,否则会把this作为返回值,并且this会被初始化一个默认的空对象,对象的原型就是foo.prototype,所以new一个对象时,即使方法以bind了一个对象,this仍指向原函数。

    拓展:

    1、手写bind()函数 

    考虑到构造函数的情况:

    Function.prototype.bind2=function(context){
        var self=this;
        if(typeof this !='function'){//要求调用bind的必须是函数
            throw new Error("Function.prototype.bind")
        }
                        
        var oArgs=Array.prototype.slice.call(arguments,1);
                        
        var fNOP=function(){}
        var fBound=function(){
            var iArgs=Array.prototype.slice.call(arguments);
            var args=oArgs.concat(iArgs);
            //当作为普通函数时,this指向window,self指向绑定函数,此时结果为false;当结果为false时,令this指向context
            //如果bind后的新函数要做new操作,那么this指向新函数,所以this instanceof self==true
            return self.apply(this instanceof fBound?this:context,args);
        }
        fNOP.prototype=this.prototype;
        fBound.prototype=new fNOP();//用于构造函数时,使fBound是构造函数的实例
        return fBound;
    }
    
    function fun(name){
        this.name=name
        return this.name;
    }
    var obj={
        name:'obj'
    }
    var f=fun.bind2(obj,'an');
    console.log(new f());

     知识点:

    (1) Array.prototype.slice.call(arguments)能够将类数组对象转换为数组。

     (2)Array.prototype.slice.call(arguments,1);//截取,获取外部函数的第一个参数之后的所有参数,参数1表示被返回的数组包含从第二个参数开始的所有参数,即去除函数自己的方法名

    2、实现一个call()函数

    1.将函数对象设置为对象的属性
    2.执行这个函数
    3.删除这个函数
    Function.prototype.myCall=function(context){
        //判断是否传入指定的对象
        context=context||window;
        //把调用的函数添加到对象中
        context.fn=this;
        var arg=[...arguments].slice(1);
        //执行函数
        let res=context.fn(arg);
         //执行函数后从对象中删除函数
      delete context.fn;
        return res;
    }
    //执行过程类似:obj.f=fun;obj.f();

    3、实现一个apply()函数

    Function.prototype.myApply=function(context,arr){
        context=context||window;
        context.fn=this;
        if(arr&&!Array.isArray(arr)){
            throw new TypeError('not funciton');
            return;
        }
        let res=context.fn(arr);
            delete context.fn;
        return res;
    }

    4、模拟new的实现过程

    //把构造函数作为第一个参数传入
    function objectFactory(){
        var obj=new Object();
        //取出第一个参数,就是我们要传入的构造函数,因为shift会改变原数组,所以arguments会被去除第一个参数
        var fun=Array.prototype.shift.call(arguments);
        //将obj的原型指向构造函数,这样obj就可以访问到构造函数原型中的属性了
        obj.__proto__=fun.prototype;
        //改变构造函数this的指向
        var res=fun.apply(obj,arguments);
        return typeof res=='object'?res:obj;//判断函数的返回值,如果返回值为对象,则返回这个对象,否则返回新创建的对象
    }
                    
    var q=objectFactory(func,'an');

    5、函数柯里化

    var newArgs=[];
    function curry(fn,args){
        var length=fn.length;//指fn总共的参数个数
        var args=args||[];
        return function(){
                        newArgs=args.concat(Array.prototype.slice.call(arguments));
            if(newArgs.length<length){
                return curry.call(this,fn,newArgs);
            }
            else{
                return fn.apply(this,newArgs);
            }
        }
    }
            
    function multiFn(a,b,c){
        return a*b*c;
        
    var multi=curry(multiFn);
    console.log(multi(2,3)(4));
    console.log(multi(2)(3)(4));

    三、箭头函数中的this

    由于箭头函数不绑定this,他会捕获其所在(即定义位置)上下文的this值,作为自己的this。

    所以call()、apply()、bind()方法对于箭头函数来说只是传入参数,对他的this毫无影响。

  • 相关阅读:
    mysql用户密码修改
    Java List java.lang.UnsupportedOperationException
    python __dict__
    pytest.fixture
    Python __metaclass__ 解释
    Python __new__()方法,为对象分配内存 返回对象的引用
    git 常用操作
    boto3 dynamodb 一些简单操作
    conda, pip, virtualenv 区别
    list去重后不改变排序
  • 原文地址:https://www.cnblogs.com/xiaoan0705/p/8651879.html
Copyright © 2011-2022 走看看