最近看你不知道的js书里头聊到this的时候说的很有意思,“任何足够先进的技术都和魔法无疑”。
以我的水平当然不能说this这个机制是不是足够先进,但是还是想试着分析分析他,因为它的坑实在太深了。
首先简明扼要说两点前提:1:this并不代表指向自身 2:this并不指向任何函数的词法作用域
对第二点稍微展开一下,因为词法作用域特性听起来虽然和对象类似,但是其实它是存在于JavaScript内部引擎中,无法通过代码访问。
然后接下来是很重要的一句话:this并不是根据编写代码时的位置来绑定,它是在函数被调用的时候发生的绑定,它指向什么完全取决于函数是在哪里被调用。
寻找调用位置并不是简单分析代码这么简单,因为有的时候可能会有编程模式会隐藏调用。
我们重点是要分析调用栈,然后找到栈的第二个元素,这个就是真正的调用位置。
找到函数调用位置之后,我们就要说到js的四种this绑定规则:默认绑定,显式绑定,隐式绑定,new绑定。
默认绑定可以理解为当无法使用其他三种规则的使用规则(兜底),关于默认绑定主要要记住的就是,首先使用严格模式下,全局对象是不能用于默认绑定的,这个时候this会绑定到undefined,
且,严格模式下函数调用是可以的,不影响,你可以理解为严格模式这个意义不会“穿透”
function foo(){
"use strict"
console.log(this.a)
};
var a = 2;
foo()// this is undefined
function foo(){
console.log(this.a)
}
var a = 2
(function(){
"use strict"
foo() //2
}) ()
看到这里请注意理解哦再往下哦。
隐式绑定,我们说简单一点的话,就是看调用位置,有没有上下文对象,有的话就会倍绑定到这个对象上,且注意对象属性引用链只有上一层或者说最后一层在调用位置中起作用。
但是隐式绑定是很容易造成隐式丢失的,即丢失了绑定对象然后应用默认绑定。我们在这里比较精简的说法就是,在函数别名以及参数传递(隐式赋值)上都会体现隐式丢失,这个看后期有没有必要展开说。
到显式绑定,这个大家在日常开发或者学习中应该遇到过。即call,apply,bind方法,这些方法对js中绝大多数函数以及你自己创建的函数都是开放使用的。call和apply改变了函数的this上下文后便执行该函数,而bind则是返回改变了上下文后的一个函数。 就是说bind绑定后不会立即执行。
而call和apply的区别在于:参数的区别,call和aplly的第一个参数都是要改变上下文的对象,call()和apply()方法都是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。apply和call方法的第一个参数都是特定的作用域第二个参数不同。
而call从第二个参数开始以参数列表的形式展现,需要逐个列出需要传递的参数。
apply则是把除了改变上下文对象的参数放在一个数组里面作为它的第二个参数。可以是Array的实例,也可以是arguments对象。
最后一种绑定是new绑定,这里其实多说一句,js中的new操作符和别的面向类语言是不一样的,
严格上来说js中没有“构造函数”,只有对于函数的"构造调用",这个后期我也会再出一篇来专门讲js中的“类”.
四种绑定规则的优先级是:new>显>隐>默认。(这里建议自己多去试一试)
再说一个很有意思的事可以积累一下,有些时候我们会特意的去忽略绑定的this,比如在参数展开和函数柯里化的时候,我们甚至会指定this为null或者undefined,这样可以刻意帮我们回避默认绑定,当然,更安全的一种做法是使用一个DMZ对象,用Object.create(null)创建,即可保护全局对象.