美文网首页
js中this总结

js中this总结

作者: 景阳冈大虫在此 | 来源:发表于2020-05-12 15:43 被阅读0次

此篇为《你不知道的JavaScript》读书笔记

函数的执行过程中调用位置决定this的绑定对象

绑定规则

默认绑定

  1. 在非严格模式中
function foo() { 
    console.log( this.a );
}
var a = 2;
foo(); // 2

在代码中,foo()是直接使用不带修饰的函数引用进行调用的,因此只能使用默认绑定,无法应用其他规则

  1. 如果使用严格模式(strict mode),那么全局对象将无法使用默认绑定,因此this会绑定到undefined
function foo() { 
    "use strict";

    console.log( this.a ); 
}

var a = 2;

foo(); // TypeError: this is undefined

隐式绑定

  1. 当函数引用有上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象。

case 1:
调用位置会使用obj上下文来引用函数,因此你可以说函数被调用时obj对象“拥有”或者“包含”它。

function foo() { 
    console.log( this.a );
}
var obj = { 
    a: 2,
    foo: foo 
};
obj.foo(); // 2

因为调用foo()时this被绑定到obj,因此this.a和obj.a是一样的。

case 2:
这解答了我的一个困惑,在读Axios源码的时候,有这样一段代码

Axios.prototype.request = function request(config) {
……
}

utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
  /*eslint func-names:0*/
  Axios.prototype[method] = function(url, data, config) {
    return this.request(utils.merge(config || {}, {
      method: method,
      url: url,
      data: data
    }));
  };
});

调用方式

import axios from 'axios';
Vue.prototype.axios = axios;

created(){
  this.axios.post('/api/posta');
}

这段代码很简单,如果调用方式为axios.post,则这个post方法其实是request方法的引用。
困惑就是,这个this.request为什么可以引用到挂在原型上的request方法。
解答:

看右边的this,为Axios
调用post时,this被绑定到了Axios,所以可以直接用this.request

case 3:
对象属性引用链中只有最顶层或者说最后一层会影响调用位置。

function foo() { 
    console.log( this.a );
}
var obj = { 
    a: 2,
    foo: foo 
};
var bar = obj.foo; // 函数别名!
var a = "oops, global"; // a是全局对象的属性
bar(); // "oops, global”
  1. 隐式丢失

case 1:

function foo() { 
    console.log( this.a );
}
var obj = { 
    a: 2,
    foo: foo 
};
var bar = obj.foo; // 函数别名!
var a = "oops, global"; // a是全局对象的属性
bar(); // "oops, global”

虽然bar是obj.foo的一个引用,但是实际上,它引用的是foo函数本身,因此此时的bar()其实是一个不带任何修饰的函数调用,因此应用了默认绑定。

case 2:

function foo() { 
    console.log( this.a );
}

var obj = { 
    a: 2,
    foo: foo 
};

var a = "oops, global"; // a是全局对象的属性
setTimeout( obj.foo, 100 ); // "oops, global”

为什么从setTimeout的调用结果看来,fn中的this是全局的呢?

参数传递其实就是一种隐式赋值,因此我们传入函数时也会被隐式赋值。
而JavaScript环境中内置的setTimeout()函数实现和下面的伪代码类似:

function setTimeout(fn,delay) {
    // 等待delay毫秒
    fn(); // <-- 调用位置!
} 

就像我们看到的那样,回调函数丢失this绑定是非常常见的。
除此之外,还有一种情况this的行为会出乎我们意料:调用回调函数的函数可能会修改this。在一些流行的JavaScript库中事件处理器常会把回调函数的this强制绑定到触发事件的DOM元素上。这在一些情况下可能很有用,但是有时它可能会让你感到非常郁闷。

显式绑定

可以使用call(..)和apply(..)方法来显示绑定this。

  1. 硬绑定
    硬绑定的典型应用场景就是创建一个包裹函数,传入所有的参数并返回接收到的所有值:
function foo(something) { 
    console.log( this.a, something ); 
    return this.a + something;
}

var obj = { 
    a:2
};

var bar = function() {
    return foo.apply( obj, arguments );
};

var b = bar( 3 ); // 2 3
console.log( b ); // 5

由于硬绑定是一种非常常用的模式,所以在ES5中提供了内置的方法Function.prototype.bind,它的用法如下:

function foo(something) { 
    console.log( this.a, something ); 
    return this.a + something;
}

var obj = { 
    a:2
};
“var bar = foo.bind( obj );

var b = bar( 3 ); // 2 3 
console.log( b ); // 5

new绑定

使用new来调用函数,即这种函数调用被称为构造函数调用
在new的过程中会发生以下事情:

  1. 创建一个全新的对象
  2. 这个对象会被执行[[原型]]连接
  3. 这个新对象会绑定到函数调用的this
  4. 如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个对象

具体的在笔者的https://www.jianshu.com/p/90ec658aa0e0里面有写。

思考以下例子

function foo(a) { 
    this.a = a;
} 

var bar = new foo(2);

console.log( bar.a ); // 2

使用new来调用foo(..)时,我们会构造一个新对象并把它绑定到foo(..)调用中的this上。new是最后一种可以影响函数调用时this绑定行为的方法,我们称之为new绑定。

优先级

上述的这些绑定的优先级为:new绑定,显示绑定,隐式绑定,默认绑定。

判断this

现在我们可以根据优先级来判断函数在某个调用位置应用的是哪条规则。可以按照下面的顺序来进行判断:

  1. 函数是否在new中调用(new绑定)?如果是的话this绑定的是新创建的对象。
var bar = new foo()
  1. 函数是否通过call、apply(显式绑定)或者硬绑定调用?如果是的话,this绑定的是指定的对象。
var bar = foo.call(obj2)
  1. 函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this绑定的是那个上下文对象。
var bar = obj1.foo()
  1. 如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到undefined,否则绑定到全局对象。
var bar = foo()

就是这样。对于正常的函数调用来说,理解了这些知识你就可以明白this的绑定原理了。不过……凡事总有例外。

绑定例外

如果你把null或者undefined作为this的绑定对象传入call、apply或者bind,这些值在调用时会被忽略,实际应用的是默认绑定规则:

function foo() { 
    console.log( this.a );
}

var a = 2;

foo.call( null ); // 2”

箭头函数

function foo() {
    // 返回一个箭头函数 
    return (a) => {
        //this继承自foo()
        console.log( this.a ); 
    };
}

var obj1 = { 
    a:2
};

var obj2 = { 
    a:3
};

var bar = foo.call( obj1 );
bar.call( obj2 ); // 2, 不是3!

foo()内部创建的箭头函数会捕获调用时foo()的this。由于foo()的this绑定到obj1,bar(引用箭头函数)的this 也会绑定到obj1,箭头函数的绑定无法被修改。(new也不行!)

如果我们把箭头函数换成普通的function,则结果为3。

相关文章

  • JS中的逻辑运算符&&、||

    1、JS中的||符号: 运算方法: 总结:真前假后 2、JS中的&&符号: 运算方法: 总结:假前真后

  • JSPatch下发笔记10

    OC代码: JS代码: 总结:NSForegroundColorAttributeName 在js中要写成"NSC...

  • Android中webview与js的交互(一)

    webview中调用js html中调用java android中配置 html中使用 总结 webview与js...

  • js中this总结

    此篇为《你不知道的JavaScript》读书笔记 函数的执行过程中调用位置决定this的绑定对象 绑定规则 默认绑...

  • js中如何访问对象和数组

    目录 一、总结 一句话总结:js访问对象点和中括号,访问数组的话就是中括号 1、js访问对象的两种方式? 2、js...

  • [Android 与 js 简单互调

    Android 与 js 简单互调 总结: Android 调用 js: 在 Android 中创建通往 jav...

  • js中history总结

    在这里我们把history简化成一个链表来讨论 , 以下红色数字为 url 当前位置 引起history变化的动作...

  • js中this问题总结

    1、元素绑定事件,方法中的this是当前操作的元素2、方法名前面是否有点,如果有点,那么点前面是谁,this就是谁...

  • ||与&&的区别

    1、JS中的||符号: 运算方法: 相当于or 总结:真前假后 2、JS中的&&符号: 运算方法: 相当于and ...

  • iOS 与 JS 交互开发知识总结

    iOS 与 JS 交互开发知识总结 iOS 与 JS 交互开发知识总结

网友评论

      本文标题:js中this总结

      本文链接:https://www.haomeiwen.com/subject/euzzwhtx.html