《你不知道的JavaScript》阅读笔记(2)
一、关于this
结论一:this并不会指向函数本身
结论二:this只有在函数执行时才是确定的,它取决于函数的调用位置
1、绑定规则:
1⃣️、默认绑定
独立函数调用,直接在全局声明一个函数并且调用,则this指向全局,如果严格模式则this会指向undefined
2⃣️、隐式绑定
当函数具有上下文的时候,this会指向上下文,如下面事例就是指向的obj
function foo(){
console.log(this.a)
}
const obj={
a:1,
foo:foo
}
obj.foo()
隐式丢失:如下代码,obj.foo其实本质上还是foo的引用给了bar,最后调用和独立调用没有什么区别,所有this遵守的是默认绑定
function foo(){
console.log(this.a)
}
const obj={
a:1,
foo:foo
}
var a=2;
const bar=obj.foo
bar() //2
3⃣️、显式绑定
call,apply,bind,其中call、apply只有参数的形式不同,bind则是返回一个具有绑定对象上下文的函数,不会立即执行,bind被称为硬绑定
4⃣️、new 绑定
使用new来调用函数后,或者按面对对象的依赖来说就是通过构造函数new了一个对象,会发生三个步骤:
1、创建一个全新的对象
2、这个对象会被执行原型链接
3、这个新对象会绑定到函数调用的this
4、如果函数没有返回其他对象则会把这个新对象返回
第二步我们这边不做过多讲述以后会讲到,如下代码我们就会发现obj就是生成的新对象,这个新对象会绑定到构造函数调用的this,所以obj.a=1
function Foo(){
this.a=1;
}
const obj=new Foo()
new可以修改已经被使用bind硬绑定的this,如下代码,使用new后新创建的对象会代替硬绑定的对象指向this
function foo(val){
this.a=val
}
const obj={}
const bar=foo.bind(obj)
bar(2);
obj.a //2
const baz=new bar(3)
baz.a=3;
上述情况来进行柯里化
function foo(p1,p2){
this.val=p1+p2
}
const baz=foo.bind(null,'p1');
const bar=new baz('p2')
bar.val //p1p2
2、硬绑定在某些情况下传入的值会被忽略,比如你传入了null或者是undefined
function foo(){
console.log(this.a)
}
var a=2;
foo.call(null) //2
3、间接引用,下面这种情况会遵循默认绑定
function foo(){
console.log(this.a)
}
var a=3;
const p={
a:4,
foo:foo
}
const q={
a:5,
foo:foo
}
(p.foo=q.foo)()//3
4、软绑定:软绑定出现的意义是如果我们进行了硬绑定则我们无法修改this的指向(new除外),大大降低了函数的灵活性,软绑定就是让默认绑定的this不绑定window或者undefined,而是绑定我们指定的对象,如下代码
Function.prototype.softBind=function(obj){
const fn=this
if(typeof this !=='function'){
throw new Error('this is not function')
}
const args=[].slice.call(arguments,1)
var bound=function(){
return fn.apply((!this||this===window)?obj:this,args.concat.apply(args,arguments))
}
bound.prototype=Object.create(fn.prototype)
return bound
}
function foo(){
console.log(this.a)
}
const obj={
a:1
}
const bar=foo.softBind(obj)
setTimeout(bar,1000) //1
const obj2={
a:3
}
bar.call(obj2)//3
5、箭头函数:保留上下文引用,如下
function foo(){
return ()=>{
console.log(this.a)
}
}
const obj1={
a:1
}
const obj2={
a:2
}
const bar=foo.call(obj1)
const baz=bar.call(obj2) //1
function zzz(){
setTimeout(()=>{
console.log(this.a)
},1000)
}
zzz.call(obj2) //2
上述代码箭头函数会保留foo()执行时的this指向,定时器里也会保留zzz()执行时的this指向,也就是说箭头函数会保留他的上下文,且箭头函数的this无法被显式修改的(new 也不行)