We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
全文约 2000 字,读完大约需要 5 分钟
今天我们就来啃下这个你可能害怕但又不得不去吃的瓜,this !
this
首先, this 在大多数情况下是一个对象,也有可能是 undefined 或其他值。
什么情况下,this 是 undefined ?函数运行在严格模式下,应用默认绑定规则的时候:
undefined
var a = 1; function foo() { "use strict"; console.log(this.a); }; foo(); // Uncaught TypeError: Cannot read property 'a' of undefined
原理其实很简单,因为规范定义了严格模式下,不能将全局对象 Window 用于默认绑定。而大多数情况下,我们说的 this,其实就是一个对象,所以确定 this 的指向,本质上就是要找到这个对象。
Window
所以接下来我就来教大家如何 “找对象” 🤣 。
找对象最重要的是什么?是不是得先通过各种途径(社交,搭讪,相亲...)去认识对象,途径越多,我们找到对象的几率就越大,对吧,这里也是一样,所以我们需要尽可能的了解 this 的绑定规则。
ECMAScript 5规范 定义的 this 的绑定规则,有 4 种。
教科书会告诉我们,几乎所有的规则都会有一个默认的情况,this 绑定也不例外,默认绑定的规则为:
非严格模式下,this 指向全局对象,严格模式下,this 会绑定到 undefined。
var a = 1; function foo() { console.log(this.a); }; function bar() { "use strict"; console.log(this.a); }; foo(); // 1,非严格模式下,this 指向全局对象 Window,这里相当于 Window.a bar(); // Uncaught TypeError: Cannot read property 'a' of undefined,严格模式下,this 会绑定到 undefined,尝试从 undefined 读取属性会报错
如果函数在调用位置有上下文对象,this 就会隐式地绑定到这个对象上,
说起来有点晦涩,直接看例子:
var a = 1; function foo() { console.log(this.a); }; var obj = { a: 2, foo: foo, // <-- foo 的调用位置 }; obj.foo(); // 2,foo 在调用位置有上下文对象 obj,this 会隐式地绑定到 obj,this.a 相当于 obj.a
这个规则可能会让你想起关于 this 经常听到的一句话,this 依赖于调用函数前的对象。
需要注意的是,隐式绑定在某些情况下可能会导致绑定丢失,具体来说有两种情况,
第一种是使用函数别名调用时:
var a = 1; function foo() { console.log(this.a); }; var obj = { a: 2, foo: foo, }; var bar = obj.foo; bar(); // 1,赋值并不会改变引用本身,使用函数别名调用时,bar 虽然是 obj.foo 的一个引用,但是实际上引用的还是 foo 函数本身,所以这里隐式绑定并没有生效, this 应用的是默认绑定
第二种是函数作为参数传递时:
function foo() { console.log(this.a); }; function bar(fn) { fn(); // <-- 调用位置 }; var a = 1; var obj = { a: 2, foo: foo, }; bar(obj.foo); // 1, 参数传递也是一种隐式赋值,即使传入的是函数,这里相当于 fn = obj.foo,所以 fn 实际上引用的还是 foo 函数本身,this 应用默认绑定
我们知道 call,apply,bind 等方法可以改变 this 的指向,通过传入参数就可以指定 this 的绑定值,够不够显式 ?这种明目张胆的绑定 this 的规则就叫显式绑定。
call
apply
bind
call 和 apply 的区别只是接受的参数格式不同,call 接受一个参数列表,apply 接受一个参数数组,但两者的第一个参数都是相同的,都是 绑定的 this 值:
function foo() { console.log(this.a); }; var a = 1; var obj = { a: 2 }; foo.call(obj); // 2,调用时显式地将 foo 的 this 绑定为 obj 对象,所以这里的 this.a 相当于 obj.a foo.apply(obj); // 2,同理
前文我们提到隐式绑定可能会导致绑定丢失,显式绑定也不例外,
思考一下,如何才能解决绑定丢失的问题?
答案其实很简单,只需要在调用函数的内部使用显式绑定,强制地将 this 绑定到对象:
function foo() { console.log(this.a); }; var obj = { a: 2, foo: foo, }; function bar(fn) { fn.call(obj); }; var a = 1; bar(obj.foo); // 2,
这其实就是 bind 的实现原理,与 call,apply 不同,bind 调用后不会执行,而是会返回一个硬绑定的函数,所以通过 bind 可以解决绑定丢失的问题。bind 也是显式绑定,我们来回顾下 bind 的用法:
function foo() { console.log(this.a); }; var obj = { a: 2 }; var a = 1; var bar = foo.bind(obj); bar(); // 2,bar 是通过 bind 返回后的一个硬绑定函数,其内部应用了显式绑定
此外,需要注意的是,将 null,undefined 作为第一个参数传入 call,apply,bind ,调用时会被忽略,实际应用的是默认绑定规则,即严格模式下,this 为 undefined,非严格模式下为全局对象。
null
先来回顾下 new 的实现原理,
function _new() { let obj = new Object(); // 1. 创建一个空对象 let Con = [].shift.call(arguments); // 2. 获得构造函数 obj.__proto__ = Con.prototype; // 3. 链接到原型 let result = Con.apply(obj, arguments); // 4. 绑定 this,执行构造函数 return typeof result === 'object' ? result : obj; // 5. 返回 new 出来的对象 }
了解了原理,我们不难发现,在使用 new 来调用函数时,会创建一个链接到函数原型的对象,并把它绑定到函数调用的 this,所以应用了 new 绑定规则后,不会被任何方式修改 this 指向:
new
function foo(a) { this.a = a; }; var bar = new foo(2); bar.a; // 2,new 会返回一个对象,这个对象绑定到构造函数的 this
ES6 中新增了一种函数类型,箭头函数,箭头函数中 this 不会应用上述规则,而是根据最外层的词法作用域来确定 this,简单来说,箭头函数的 this 就是它外面第一个不是箭头函数的函数的 this:
function foo() { return () => { return () => { console.log(this.a); }; }; }; foo()(); // undefined,箭头函数调用时,this 取决于最外层的第一个不是箭头函数的函数,这里就是 foo 函数,非严格模式下,默认绑定全局对象 Window,this.a 相当于 Window.a,输出 undefined
new绑定 > 显式绑定 > 隐式绑定 > 默认绑定
根据绑定规则和优先级,我们可以总结出 this 判断的通用模式,
本文首发于我的 博客,才疏学浅,难免有错误,文章有误之处还望不吝指正!
如果有疑问或者发现错误,可以在评论区进行提问和勘误,
如果喜欢或者有所启发,欢迎 star,对作者也是一种鼓励。
The text was updated successfully, but these errors were encountered:
醍醐灌顶!!!
Sorry, something went wrong.
No branches or pull requests
可能是最好的 this 解析了...
今天我们就来啃下这个你可能害怕但又不得不去吃的瓜,
this
!找对象
首先, this 在大多数情况下是一个对象,也有可能是 undefined 或其他值。
什么情况下,
this
是undefined
?函数运行在严格模式下,应用默认绑定规则的时候:原理其实很简单,因为规范定义了严格模式下,不能将全局对象
Window
用于默认绑定。而大多数情况下,我们说的this
,其实就是一个对象,所以确定this
的指向,本质上就是要找到这个对象。所以接下来我就来教大家如何 “找对象” 🤣 。
绑定规则
找对象最重要的是什么?是不是得先通过各种途径(社交,搭讪,相亲...)去认识对象,途径越多,我们找到对象的几率就越大,对吧,这里也是一样,所以我们需要尽可能的了解 this 的绑定规则。
ECMAScript 5规范 定义的
this
的绑定规则,有 4 种。默认绑定
教科书会告诉我们,几乎所有的规则都会有一个默认的情况,
this
绑定也不例外,默认绑定的规则为:非严格模式下,this 指向全局对象,严格模式下,this 会绑定到 undefined。
隐式绑定
如果函数在调用位置有上下文对象,this 就会隐式地绑定到这个对象上,
说起来有点晦涩,直接看例子:
这个规则可能会让你想起关于
this
经常听到的一句话,this 依赖于调用函数前的对象。需要注意的是,隐式绑定在某些情况下可能会导致绑定丢失,具体来说有两种情况,
第一种是使用函数别名调用时:
第二种是函数作为参数传递时:
显式绑定
我们知道
call
,apply
,bind
等方法可以改变this
的指向,通过传入参数就可以指定this
的绑定值,够不够显式 ?这种明目张胆的绑定this
的规则就叫显式绑定。call
和apply
的区别只是接受的参数格式不同,call
接受一个参数列表,apply
接受一个参数数组,但两者的第一个参数都是相同的,都是 绑定的 this 值:前文我们提到隐式绑定可能会导致绑定丢失,显式绑定也不例外,
思考一下,如何才能解决绑定丢失的问题?
答案其实很简单,只需要在调用函数的内部使用显式绑定,强制地将
this
绑定到对象:这其实就是 bind 的实现原理,与
call
,apply
不同,bind
调用后不会执行,而是会返回一个硬绑定的函数,所以通过bind
可以解决绑定丢失的问题。bind
也是显式绑定,我们来回顾下bind
的用法:此外,需要注意的是,将
null
,undefined
作为第一个参数传入call
,apply
,bind
,调用时会被忽略,实际应用的是默认绑定规则,即严格模式下,this
为undefined
,非严格模式下为全局对象。new绑定
先来回顾下 new 的实现原理,
了解了原理,我们不难发现,在使用
new
来调用函数时,会创建一个链接到函数原型的对象,并把它绑定到函数调用的this
,所以应用了new
绑定规则后,不会被任何方式修改this
指向:【特殊】箭头函数中的this
ES6 中新增了一种函数类型,箭头函数,箭头函数中
this
不会应用上述规则,而是根据最外层的词法作用域来确定 this,简单来说,箭头函数的this
就是它外面第一个不是箭头函数的函数的 this:优先级
new绑定 > 显式绑定 > 隐式绑定 > 默认绑定
判断模式
根据绑定规则和优先级,我们可以总结出
this
判断的通用模式,总结
写在最后
本文首发于我的 博客,才疏学浅,难免有错误,文章有误之处还望不吝指正!
如果有疑问或者发现错误,可以在评论区进行提问和勘误,
如果喜欢或者有所启发,欢迎 star,对作者也是一种鼓励。
The text was updated successfully, but these errors were encountered: