最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 5分钟带你搞懂 Javascript 中的this(包含apply、call、bind)

    正文概述 掘金(alanyf)   2020-12-25   249

    当一个函数被调用时,会创建一个活动记录(有时候也称为执行上下文)。这个记录会包含函数在哪里被调用(调用栈)、函数的调用方法、传入的参数等信息。this 就是记录(上下文)的其中一个属性,会在函数执行的过程中用到。

    一、this的指向

    this 总是指向执行时当前对象

    JavaScript 的 this 总是指向一个对象,而具体指向哪个对象是在运行时基于函数的执行环境动态绑定的,而非函数被声明时的环境

    也就是说 this 的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式

    除了使用 with 和 eval 的情况,常用的可分为以下几种:

    • 作为对象的方法调用。
    • 作为普通函数调用。
    • 构造器调用。
    • Function.prototype.call 或 Function.prototype.apply 调用。

    1. 作为对象的方法调用

    对象的方法被调用时,this 指向该对象。

    var obj = {
        a: 1,
        getA() {
            alert ( this === obj ); // 输出:true alert ( this.a ); // 输出: 1
        }
    };
    obj.getA();
    

    2. 作为普通函数调用

    当函数不作为对象的属性被调用时,也就是我们常说的普通函数方式,此时的 this 总是指向全局对象。

    这里注意对象的方法被单独拷贝出来后执行,那么原来的this会丢失,变成指向全局对象

    //在浏览器的JavaScript 里,这个全局对象是window 对象。
    window.name = 'globalName';
    var getName = function(){
        return this.name;
    };
    console.log( getName() ); // 输出:globalName
    
    //或者:
    window.name = 'globalName';
    var myObject = {
        name: 'sven',
        getName: function(){
            return this.name;
        }
    };
    var getName = myObject.getName;
    console.log( getName() ); // globalName
    

    3. 作为对象的方法调用

    通常情况下,构造器里的 this 就指向返回的这个对象;

    • 如果构造器不显式地返回任何数据,或者是返回一个非对象类型的数据,this指向返回的这个对象;
    • 如果构造器显式地返回了一个对象,则实例化的结果会返回这个对象,而不是this;
    //构造器里的this 就指向返回的这个对象,见如下代码:
    var MyClass = function(){
        this.name = 'sven';
    };
    var obj = new MyClass();
    alert ( obj.name ); // 输出:sven
    
    
    var MyClass = function(){
        this.name = 'sven';
        return { // 显式地返回一个对象
            name: 'anne'
        }
    };
    var obj = new MyClass();
    alert ( obj.name ); // 输出:anne
    
    
    var MyClass = function(){
        this.name = 'sven'
        return 'anne'; // 返回string 类型
    };
    var obj = new MyClass();
    alert ( obj.name ); // 输出:sven
    

    4. call 或 apply 调用

    apply和call可以动态地改变传入函数的 this

    var obj1 = {
        name: 'sven',
        getName: function(){
            return this.name;
        }
    };
    var obj2 = {
        name: 'anne'
    };
    console.log( obj1.getName() ); // 输出: sven
    console.log( obj1.getName.call( obj2 ) ); // 输出:anne
    

    二、call, apply 和 bind

    简介及区别

    call 和 apply作用一模一样,区别仅在于传入参数形式的不同。

    apply

    apply 接受两个参数,第一个参数指定了函数体内 this 对象的指向,第二个参数为一个带下标的集合,这个集合可以为数组,也可以为类数组

    类数组:

    1. 对象本身要可以存取属性;
    2. 对象的 length 属性可读写。

    call

    call 传入的参数数量不固定,第一个参数也是代表函数体内的 this 指向,从第二个参数开始往后,每个参数被依次传入函数

    bind

    bind 参数类型和 call 相同,不过它不会执行函数,而是修改 this 后返回一个新的函数

    var func = function( a, b, c ){
        alert ( [ a, b, c ] );
    };
    func.apply( null, [ 1, 2, 3 ] );
    func.call( null, 1, 2, 3 );
    

    call 和 bind 是包装在 apply 上面的语法糖

    Function.prototype.call = function( context ){ 
        var argus = [];
        for (var i = 1; i < arguments.length; i++) {
            argus.push(arguments[i]);
        }
        this.apply(context, argus);
    };
    
    Function.prototype.bind = function( context ){ 
        var self = this; // 保存原函数
        // 返回一个新的函数
        return function(){ 
            // 执行新的函数的时候,会把之前传入的 context // 当作新函数体内的 this
            return self.apply( context, arguments );
        }
    };
    

    用途

    1. 改变 this 指向
    2. 借用其他对象的方法:操作类数组(比如 arguments) 的时候,可以找 Array.prototype 对象借用方法Array.prototype.push.call(a, 'first' )

    三、eval 和 with

    词法作用域由写代码期间函数所声明的位置来定义,而 eval 和 with 可以在运行时修改词法作用域。

    eval(..) 和 with 会在运行时修改或创建新的作用域,以此来欺骗其他在书写时定义的词 法作用域。 看起来它们能实现更复杂的功能,并且代码更具有扩展性。

    但这些功能已经过时,性能也较差,使用不当容易导致难以预料的问题,已经不被提倡,严格模式下会被限制和禁止,所以不要轻易使用它们

    eval

    eval(..) 函数可以接受一个字符串为参数,并将其中的内容视为好像在书写时就存在于程序中这个位置的代码。换句话说,可以在你写的代码中用程序生成代码并运行,就好像代码是写在那个位置的一样。

    function foo(str, a) {
        eval( str );
        console.log( a, b );
    }
    var b = 2;
    foo( "var b = 3;", 1 ); // 1, 3
    
    // 相当于
    function foo(str, a) {
        var b = 3;
        console.log( a, b );
    }
    

    字符串的内容可以是一段动态生成的函数代码

    JavaScript中 还有其他一些功能效果和eval(..)很相似。setTimeoutsetInterval 的第一个参数可以是字符串。

    with

    with 可以改变作用域,通常被当作重复引用同一个对象中的多个属性的快捷方式,可以不需要重复引用对象本身。

    var obj = {
        a: 1,
        b: 2,
        c: 3
    };
    // 单调乏味的重复 "obj" 
    obj.a = 2;
    obj.b = 3;
    obj.c = 4;
    // 简单的快捷方式 
    with (obj) {
        a = 3;
        b = 4;
        c = 5;
    }
    

    性能

    eval 和 with性能差

    JavaScript 引擎会在编译阶段进行数项的性能优化。其中有些优化依赖于能够根据代码的词法进行静态分析,并预先确定所有变量和函数的定义位置,才能在执行过程中快速找到标识符。

    但如果引擎在代码中发现了 eval(..) 或 with,就无法在词法分析阶段明确知道 eval(..) 会接收到什么代码,这些代码会如何对作用域进行修改,也无法知道传递给 with 用来创建新词法作用域的对象的内容到底是什么。

    因此如果大量使用会导致性能变差。

    参考

    • JavaScript设计模式与开发实践
    • 你不知道的JavaScript

    起源地下载网 » 5分钟带你搞懂 Javascript 中的this(包含apply、call、bind)

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元