摘要:绑定使用来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。
1 this的概念
this 在任何情况下都不指向函数的词法作用域。作用域“对象”无法通过 JavaScript代码访问,它存在于 JavaScript 引擎内部。
this 是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件。this 的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。
当一个函数被调用时,会创建一个活动记录(有时候也称为执行上下文)。这个记录会包含函数在哪里被调用(调用栈)、函数的调用方法、传入的参数等信息。this 就是记录的其中一个属性,会在函数执行的过程中用到。
2 this的绑定函数的执行过程中调用位置如何决定 this 的绑定对象,绑定规则:
1 默认绑定
function foo(){ console.log(a); } var a=2; foo();
foo() 是直接使用不带任何修饰的函数引用进行调用的,因此只能使用默认绑定,无法应用其他规则。如果使用严格模式(strict mode),那么全局对象将无法使用默认绑定,因此 this 会绑定
到 undefined。
2 隐式绑定
调用位置是否有上下文对象,或者说是否被某个对象拥有或者包含。当 foo() 被调用时,它的落脚点确实指向 obj 对象。当函数引用有上下文对象时,隐式绑定规则会把函数调用中的 this 绑定到这个上下文对象。因为调用 foo() 时 this 被绑定到 obj,因此 this.a 和 obj.a 是一样的。对象属性引用链中只有最顶层或者说最后一层会影响调用位置。
function fooo(){ conosle.log(this.a); } var obj={ a:2, foo:foo }; var obj2={ a:22, obj: obj } obj.foo(); // 2 obj2.obj.foo(); // 2
隐式丢失
虽然 bar 是 obj.foo 的一个引用,但是实际上,它引用的是 foo 函数本身,因此此时的bar() 其实是一个不带任何修饰的函数调用,因此应用了默认绑定。
当函数被作为参数传递时(如:回调函数),会丢失this,因为参数传递其实就是一种隐式赋值
function foo() { console.log( this.a ); } var obj = { a: 2, foo: foo }; var bar = obj.foo; // 函数别名! var a = "oops, global"; // a 是全局对象的属性 bar(); // "oops, global"
3 显式绑定
JavaScript 提供的绝大多数函数以及你自己创建的所有函数都可以使用 call(..) 和 apply(..) 方法。这两个方法是如何工作的呢?它们的第一个参数是一个对象,它们会把这个对象绑定到this,接着在调用函数时指定这个 this。因为你可以直接指定 this 的绑定对象,因此我们称之为显式绑定。
function foo(){ console.log(this.a); } var obj={a:2}; foo.call(obj); // 2
如果你传入了一个原始值(字符串类型、布尔类型或者数字类型)来当作 this 的绑定对象,这个原始值会被转换成它的对象形式(也就是new String(..)、new Boolean(..)或者new Number(..))。这通常被称为“装箱”。
4 new 绑定
使用 new 来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。
创建(或者说构造)一个全新的对象。
这个新对象会被执行[[原型]]连接。
这个新对象会绑定到函数调用的this。
如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象。
function foo(a) { this.a = a; } var bar = new foo(2); console.log( bar.a ); // 2
使用 new 来调用 foo(..) 时,我们会构造一个新对象并把它绑定到 foo(..) 调用中的 this上。new 是最后一种可以影响函数调用时 this 绑定行为的方法,我们称之为 new 绑定。
3 判断this绑定
注意:如果你把 null 或者 undefined 作为 this 的绑定对象传入 call、apply 或者 bind,这些值在调用时会被忽略,实际应用的是默认绑定规则。
那么什么情况下你会传入 null 呢?一种非常常见的做法是使用 apply(..) 来“展开”一个数组,并当作参数传入一个函数。类似地,bind(..) 可以对参数进行柯里化(预先设置一些参数),这种方法有时非常有用:
function foo(a,b) { console.log( "a:" + a + ", b:" + b ); } // 把数组“展开”成参数 foo.apply( null, [2, 3] ); // a:2, b:3 // 使用 bind(..) 进行柯里化 var bar = foo.bind( null, 2 ); bar( 3 ); // a:2, b:3
软绑定会对指定的函数进行封装,首先检查调用时的 this,如果 this 绑定到全局对象或者 undefined,那就把指定的默认对象 obj 绑定到 this,否则不会修改 this。
if (!Function.prototype.softBind) { Function.prototype.softBind = function(obj) { var fn = this; // 捕获所有 curried 参数 var curried = [].slice.call( arguments, 1 ); var bound = function() { return fn.apply( (!this || this === (window || global)) ? obj : this curried.concat.apply( curried, arguments ) ); }; bound.prototype = Object.create( fn.prototype ); return bound; }; }4 箭头函数
箭头函数并不是使用 function 关键字定义的,而是使用被称为“胖箭头”的操作符 => 定义的。箭头函数不使用 this 的四种标准规则,而是根据外层(函数或者全局)作用域来决定 this。
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 也不行!)
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/104376.html
下一篇:《你不知道的javascript》笔记_对象&原型 写在前面 上一篇博客我们知道词法作用域是由变量书写的位置决定的,那this又是在哪里确定的呢?如何能够精准的判断this的指向?这篇博客会逐条阐述 书中有这样几句话: this是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式当一个函数被调用时...
摘要:本书属于基础类书籍,会有比较多的基础知识,所以这里仅记录平常不怎么容易注意到的知识点,不会全记,供大家和自己翻阅不错,下册的知识点就这么少,非常不推介看下册上中下三本的读书笔记你不知道的上读书笔记你不知道的中读书笔记你不知道的下读书笔记第三 本书属于基础类书籍,会有比较多的基础知识,所以这里仅记录平常不怎么容易注意到的知识点,不会全记,供大家和自己翻阅; 不错,下册的知识点就这么少,非...
摘要:注此读书笔记只记录本人原先不太理解的内容经过阅读你不知道的后的理解。作用域及闭包基础,代码运行的幕后工作者引擎及编译器。 注:此读书笔记只记录本人原先不太理解的内容经过阅读《你不知道的JS》后的理解。 作用域及闭包基础,JS代码运行的幕后工作者:引擎及编译器。引擎负责JS程序的编译及执行,编译器负责词法分析和代码生成。那么作用域就像一个容器,引擎及编译器都从这里提取东西。 ...
摘要:有种内置类型,分别是除对象之外,其他统称为基本类型。另一个需要注意的是数组确切地说,数组也是的一个子类型我们可以通过下面的方法检查变量是不是数组处理未声明的变量时,会返回这是因为有一个特殊的安全防范机制。 js有7种内置类型,分别是undefined null boolean string number symbol object除对象之 Object 外,其他统称为基本类型。符号 ...
摘要:闭包在循环中的应用延迟函数的回调会在循环结束时才执行事实上,当定时器运行时即使没给迭代中执行的是多有的回调函数依然是在循环结束后才会被执行,因此会每次输出一个出来。 闭包在循环中的应用 延迟函数的回调会在循环结束时才执行;事实上,当定时器运行时即使没给迭代中执行的是 setTime(..., 0),多有的回调函数依然是在循环结束后才会被执行,因此会每次输出一个6出来。 for(var...
阅读 1313·2021-11-22 14:44
阅读 2448·2021-09-30 09:47
阅读 1222·2021-09-09 11:56
阅读 2079·2021-09-08 09:45
阅读 3957·2021-08-31 09:40
阅读 1253·2019-08-30 15:52
阅读 2045·2019-08-30 14:09
阅读 1581·2019-08-26 17:04