this
this到底是什么?
this是在运行时绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件。this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。
当一个函数被调用时,会创建一个记录(有时候也称为执行上下文)。这个记录会包含函数函数在哪里被调用(调用栈)、函数的调用方法、传入的参数等信息。this就是记录的其中一个属性,会在函数执行的过程中用到。
如果不使用this,那就需要显示传入一个上下文对象,this提供了一种更优雅的方式来隐式“传递”一个对象的引用。
仅仅是因为无法猜对this的用法,就放弃学习this而去使用词法作用域,就不能算是一种很好的解决办法。
声明在全局作用域中的变量就是全局对象的一个同名属性。它们本质上就是同一个东西,并不是通过复制得到的。
this的绑定规则完全取决于调用位置。注:只有在non-strict mode下时,默认绑定才能绑定到全局对象。
隐式丢失
一个最常见的this绑定问题就是被隐式绑定的函数会丢失绑定对象,也就是说它会应用默认绑定,从而把this绑定到全局对象或者undefined上,取决于是否是严格模式。
显示绑定
call(...)和apply(...)方法,它们的第一个参数是一个对象,它们会把这个对象绑定到this,接着在调用函数时指定这个this。
如果你传入了一个原始值(字符串类型、布尔类型或者数字类型)来当做this的绑定对象,这个原始值会被转换成它的对象形式(也就是new String()...)。这通常被称为装箱。
由于硬绑定是一种非常常用的模式,所以在es5中提供了内置的方法Function.prototype.bind。
function foo(sth){
return this.a + sth
}
function bind(fn,obj){
return function(){
return fn.apply(obj,arguments)
}
}
var obj = {
a:'zjy'
}
var bar = bind(foo,obj)
var b = bar(' work hard')
console.log(b)
bind(...)会返回一个硬编码的新函数,它会把参数设置为this的上下文并调用原始函数。
判断this
- 函数是否在new中调用?如果是的话,this绑定的是新创建的对象。
- 判断函数是否通过call、apply或者硬绑定调用?如果是的话,this绑定的是指定的对象。
- 函数是否在某个上下文对象中调用?如果是的话,this绑定的是那个上下文对象。
- 如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到undefined,否则绑定到全局对象。
被忽略的this
如果把null或者undefined作为this的绑定对象传入call、apply或者bind,这些值在调用时会被忽略,实际应用的是默认绑定规则。
软绑定
硬绑定这种方式可以把this强制绑定到指定的对象(除了使用new时),防止函数调用应用默认绑定规则。问题在于,硬绑定会大大降低函数的灵活性,使用硬绑定之后就无法使用隐式绑定或者显示绑定来修改this。
如果可以给默认绑定指定一个全局对象和undefined以外的值,那就可以实现和硬绑定相同的效果,同时保留隐式绑定或者显示绑定修改this的能力。
意思就是说:使用硬绑定之后就无法使用隐式绑定或者显示绑定来修改this,那么就不使用硬绑定,实现一个软绑定(softBind),其功能就是给默认绑定指定一个全局对象和undefined以外的值,那就可以实现和硬绑定相同的效果,同时保留隐式绑定或者显示绑定修改this的能力。
Function.prototype.softBind = Function.prototype.softBind || function(obj,...rest){
let fn = this
let bound = function(...args){
return fn.apply(
(!this || this === (window || global)) ? obj : this,
[...rest,...args]
)
}
bound.prototype = Object.create(fn.prototype)
return bound
}
箭头函数并不是使用function关键字定义的,而是使用被称为”胖箭头“的操作符 => 定义的。箭头函数不适用this的四种标准规则,而是根据外层(函数或者全局)作用域来决定this。
- 箭头函数的外层如果有普通函数,那么箭头函数的 this 就是外层普通函数的this
- 箭头函数的外层如果没有普通函数,那么箭头函数的 this 就是全局变量
- 当对箭头函数使用 call 或 apply 方法时,只会传入参数并调用函数,并不会改变箭头函数中 this 的指向;
- 当对箭头函数使用 bind 方法时,只会返回一个预设参数的新函数,并不会绑定新函数的 this 指向。
经典箭头函数面试题
function foo(n) {
var f = () => arguments[0] + n;
return f();
}
let res = foo(2);
console.log(res); // 4
function A() {
this.foo = 1
}
A.prototype.bar = () => {
console.log(this.foo)
}
let a = new A()
a.bar() //undefined
let res = (function pt() {
return (() => this.x).bind({ x: 'inner' })();
}).call({ x: 'outer' });
console.log(res) // 'outer'
window.name = 'window_name';
let obj1 = {
name:'obj1_name',
print:() => console.log(this.name)
}
let obj2 = {name:'obj2_name'}
obj1.print() // 'window_name'
obj1.print.call(obj2) // 'window_name'
let obj1 = {
name:'obj1_name',
print:function(){
return () => {
console.log(this.name)
}
}
}
let obj2 = {name:'obj2_name'}
obj1.print()() // 'obj1_name
obj1.print().call(obj2) // 'obj1_name'
obj1.print.call(obj2)() // 'obj2_name'

浙公网安备 33010602011771号