海兴网站建设公司,园林设计,正规的网站建设企业网站制作,WordPress底部栏插件JS中的this很多时候会让人捉摸不透#xff0c;不知道这个this到底指向的是什么。现在根据自己的理解写下这篇文章做一个总结。
我们知道this指向谁一般情况下是在运行时决定的#xff0c;并且运行时决定this指向的因素又有很多#xff0c;例如是不是被bind了#xff0c;或…JS中的this很多时候会让人捉摸不透不知道这个this到底指向的是什么。现在根据自己的理解写下这篇文章做一个总结。
我们知道this指向谁一般情况下是在运行时决定的并且运行时决定this指向的因素又有很多例如是不是被bind了或者调用的时候使用了apply和call这类方法还有是不是通过new来调用这个函数如果没有以上显示绑定那么是obj.fn()这样调用的吗或者直接fn()如果直接fn()调用那么fn的函数体是严格模式吗最后这个函数是ES6中的箭头函数吗
默认绑定和隐式绑定
首先看最常见的调用方式通过对象调用这个函数或者叫方法this隐式绑定到了调用函数的对象上。
var a 2
var obj {a: 1,fn: function () {console.log(this.a)}
}obj.fn()我们通过运行知道打印了1也就是说这时fn中的this指向了调用他的obj但是是否表示任何情况下fn中的this都是指向fn定义时的对象obj呢显然不是的。在某些情况下这种隐式绑定的this会丢失如下
var fn1 obj.fn
fn1()上面打印了2是的出乎意料并没有打印1所以关于this的指向和函数的定义没有什么关系看似函数fn属于对象obj其实并不是。这时fn中this默认指向的是window对象。上面通过赋值就弄丢了原本的隐式绑定没有了隐式绑定只能使用默认绑定。
现在我们切换到严格模式
use strict
var a 2
var obj {a: 1,fn: function () {console.log(this.a)}
}var fn1 obj.fn
fn1()我们发现执行fn1的时候报了一个错误Uncaught TypeError: Cannot read property a of undefined这是因为在严格模式下fn1中的this指向的undefined获取undefined的属性当然会报错因为undefined不是一个对象也不能隐式转换成一个对象。
注上面通过将对象的方法赋值给一个变量导致函数的方法中默认绑定this丢失这种情况会出现在很多其他意想不到的地方例如函数的传参这也是一种隐式的赋值。
显示调用call和apply的绑定
上面无论是通过对象还是直接通过函数名调用函数其中的this指向谁好像编译器心里有数是一种默契。那么我们能不能不要这个默契我们自己来指定函数调用的时候this指向谁。我们通过call方法和apply方法就可以轻易做到。
var a 2
var obj {a: 1,fn: function () {console.log(this.a)}
}
var fn1 obj.fn
fn1.call(obj)我们可以看到又打印出了1不负所望。调用fn1的时候我们通过结果可以知道函数体内的this被绑定到了obj上。apply做的事情和call是一样的区别就在于传入函数中参数的形式call必须要和调用函数一样一个一个传入参数但是apply允许我们通过一个数组将需要的参数一起传入函数中。这个神奇的功能就像是ES6中的 … 操作符。
bind的绑定
bind的也可以绑定函数中的this但是和上面的call和apply有明显的不同call和apply是直接就执行了函数但是bind不是bind会返回一个函数这个特性就让这个bind不仅仅可以绑定this还可以进行函数柯里化。
var a 2
var obj {a: 1,fn: function () {console.log(this.a)}
}
var fn1 obj.fn
var fn2 fn1.bind(obj)
fn2()不出意料的也打印了1。
bind的简单Polyfill
if (!Function.prototype.bind) {Function.prototype.bind function (obj) {// 这一句检测this是不是函数我本以为是多余但是bind是可以被call的这时候this就很有可能不指向functionif (typeof this ! function) {throw new TypeError(不是函数的数据尝试调用bind方法)}// obj 就是函数要绑定的this而函数就是现在函数体中的this因为bind函数是在Function.prototype上的// 这是在获取在bind的时候就传过来的参数var args [].slice.call(arguments, 1)// 存一下需要bind的函数var fn this // 处理fn函数的prototype属性var _fn function () {}// 这个函数将被返回var bindFn function () {// 处理一下被bind的函数使用new调用的时候return fn.apply(this instanceof bindFn ? this : obj, args.concat([].slice.call(arguments)))} // 处理fn.prototypeif (fn.prototype) {_fn.prototype fn.prototype}bindFn.prototype new _fn()return bindFn}
}上面的兼容是类似mozilla的写法不仅仅绑定了this还考虑到了参数的拼接还有函数的prototype属性的处理还包括被bind的函数作为构造函数调用的时候其中this的指向。
new的this绑定
可以绑定this的不仅仅是上面的callapply和bindnew也可以的。我们知道通过new调用一个函数的时候会有下面几个步骤
新建一个对象将对象的原型关联到函数的原型属性将this指向这个对象执行函数如果函数没有返回值则返回这个对象
看上面第二步就将函数中的this关联到了新建的对象上了。那么对于一个bind的函数我们使用new来调用函数中的this到底是指向了new新建出来的对象还是bind时候的对象呢
其实上面bind方法的Polyfill已经给出了答案是会指向new新建出来的对象。fn.apply(this instanceof bindFn ? this : obj, args.concat([].slice.call(arguments)))这里通过this instanceof bindFn判断是不是通过new调用的该方法如果是那么就指向当前的this也就是新建的对象如果不是才指向传进来的obj。
##绑定的优先级
通过上面bind的Polyfill我们知道new绑定的this优先级要大于显示绑定的bind并且bind的绑定优先级要高于call和apply方法。
隐藏是绑定优先级要高于默认绑定并且低于显示绑定的call和apply方法。
所以整理出来的优先级如下
new bind (apply call) 隐式绑定 默认绑定
关于箭头函数
ES6的箭头函数和上面说的情况都不一样箭头函数中的this指向并不是在调用的时候确定的而是在定义的时候和定义的时候的词法作用域有关并且后期并不能通过上面显示绑定的方法修改this的指向。也就是说箭头函数定义的时候拿到当前上下文的this然后就不会再改变了。
var a 2
var obj {a: 1,fn: function () {console.log(this.a)var b () {console.log(this)}b()}
}
obj.fn()上面打印出了1 和 obj 对象
var a 2
var obj {a: 1,fn: function () {console.log(this.a)var b () {console.log(this)}b.call(window)}
}
obj.fn()虽然使用了call指定this绑定但是还是打印了1和obj对象而不是window。call方法并没能修改箭头函数的this指向。
var a 2
var obj {a: 1,fn: function () {console.log(this.a)var b () {console.log(this)}var c b.bind(window)c()}
}
obj.fn()结果和call的绑定一致没有改变箭头函数中的this。
那么能使用new呢箭头函数不能使用new调用会报错的。
参考
你不知道的javascript上卷