爱这故乡的热土,初心就是踏上家乡黑土的那一刻,回家了--致敬最可爱的人
本次文章借鉴部分引用《你不知道的JavaScript(上卷)》声明本人已够买纸质书籍。(尊重知识)
this真的懂吗??看着this是不是感觉既熟悉又陌生,这篇文章能够在帮助我学习也能带领你学习一些(记得千万不要相信几天速成,一篇文章就直接可以学会,用心去感受,带着学习的炽热之心,勇往直前)
我们为什么会说javaScript中最复杂的机制之一那??我个人认为就是它的指向是多重变换,与孙悟空的72变一样,你不知道哪个是孙悟空变得,所以还的练就一双火眼。
从本质看this就是能够让你的代码变得优雅,能够快速获取你想要的上下文。
this到底是什么??
this的绑定和函数的声明位置没有任何关系,只取决于函数的调用方式。(P80)
在了解this的绑定过程之前,先要了解一下调用位置,我们要多加分析调用栈、调用位置;
//函数的定义位置
function wenshu(){
//调用栈个人理解就是调用函数的顺序链接,就是有个先来后到。
//调用栈wenshu
//调用位置
light()
console.log('76wenhsu')
}
function light(){
//调用栈 wenshu -> light
console.log('心里有火眼里有光:‘武')
}
//调用位置就是函数的执行位置
//调用位置
wenshu()
默认绑定
函数调用类型“独立函数调用。 可以看成是无法应用其他规则的默认规则。
这是在this中最简单的一种绑定方式,上代码:
//const a = '76wenshu';
var a = '76wenshu';
function wenshu(){
debugger
//调用栈wenshu
var a = '心里有火,眼里有光'
//调用位置
light() // => this.light()
console.log(this)
//因为你打断点就会发现const和let声明的变量不在window中(有待进一步了解)
console.log(this.a)//当a是用const声明是就是undefined
//var声明时就是‘76wenshu’
debugger
}
function light(){
//调用栈 wenshu -> light
console.log(this.a)//与上方相同为什么不会调用wenshu函数的a,请看light函数的引用
}
//调用位置
wenshu()
全局对象window下找不到const、let声明的变量???
隐式绑定
function light(){
console.log(this.a)
}
var obj ={
a:2,
light:light
}
light();//undefined
obj.light()//2
个人认为:当你把函数赋给变量时,就相当于吧函数体赋给这个变量,让它能够使用
多层的调用:
function light(){
console.log(this.a)
}
//对象声明的先后顺序是有区别的,防止变量提升的问题出现。
var obj1 ={
a:76,
light:light
}
var obj ={
a:2,
obj1:obj1,
light:light
}
console.log(obj);
obj.light();//2
obj.obj1.light();//76
个人认为:近水楼台先得月(就近原则)
- 隐式丢失
function light(){
console.log(this.a)
}
var obj ={
a:2,
light:light
}
var a = '武统';
var bar = obj.light
bar();//武统
当你调用bar的时候是不是有点不一样的感觉为什么会出现这样的情况,怎么直接引用全局的变量a了那; 我认为函数是对象,只能进行引用,他们就是一个重新复制的过程
var bar = obj.light
=>
var bar = function(){
console.log(this.a)
}
更具有迷惑性的就是函数中用函数作为参数进行使用:
function light(){
console.log(this.a)
}
var obj ={
a:2,
// obj1:obj1,
light:light
}
function bar(fn){
console.log(fn);
fn()
}
var a = '武统';
bar(obj.light);//武统
//如果函数是箭头函数,就有区分别之后会有讲解
setTimeout(obj.light, 100);//武统
总结:隐式丢失的原因,函数调用符合使用规则,相当于用函数,将原函数替代。
显式绑定
显示绑定不得不说call/apply/bind三个函数
//call如何实现的
function call(content=window,...arguments){
//const content = content || window;
arguments=arguments?arguments:[];
//个人觉的没有必要像下边注释这样写
//const fn = Symbol()
content.fn=this;
const result =content.fn(...arguments)
delete content.fn
return result
}
在这里我说一下:
有很多人都是告诉你写call的源码,但是你真的记得住吗??
你理解了吗??多写写记得不要复制也不抄写代码,切记切记,不要死记硬背了解他,理解他,才能够看出为什么要写call函数。(切记不要假学习,自己感动自己,那样没有任何意义)
每个人都有一套自己的学习方法,及早的找到他领悟它,才能少走学习的弯路。我就是自学的前端走了很多弯路。所以我要极力构建前端的自学网络。
废话不多说,下面说一下它思路:
问:先抛出问题call 要解决的问题是什么??它是干什么的??
答:个人理解call就是用来改变函数中this指向,能够让你获取this指向你想要的对象。
记住这句话之后,就是call返回的 return obj(你想指向的那个对象).fn(你想使用的那个函数)(arguments)(你想传进来的参数)。
知道你想要的结果了那就看怎么去实现它那,那就是call中传进来两参数(content(你想指向的那个对象),...arguments(你想传进来的参数)),一点都不绕你的去了解它,
传进来两个参数之后,那你符合把你想使用的函数fn绑定到你想用的对象上那,obj.fn的方式就很好,那么我们就在函数的
const content = content
//func.call()
//这里的this指向的就是func
content.fn =this;
return content.fn();
函数体就写完了那么我们还的去完善它,因为他太简陋了,你考虑到现有的绑定情况; 突然感觉忘了传什么哈哈,忘了传参数了。。。
const content = content
//传参不能忘
cosnt argus = arguments
//func.call()
//这里的this指向的就是func
content.fn =this;
return content.fn(argus);
整体的思路如上 apply和这样一样就是,传参不一样而已。
call,apply的我觉得完全一样,如果非想说出他们的区别的话那就是参数的不同.参数数量少,参数的顺序是固定的就用call, 与其相反的就用apply。 如果参数是数组就直接只用apply
讲完了call和apply那就说一下bind,他们之间有什么区别吗? 总结一句就是call和apply返回调用函数的结果而bind是返回的函数,如果你需要,获取结果还还需要重新调用,
var a= fn.call(obj,...args)//直接获取到你想要的值
var b = fn.bind(obj,..args);//返回的不是结果是函数
b()//返回结果;
//根据这个区别我们就可已根据原有call和apply函数进行改造了
function bind(content=window,...arges){
const fn = this
arges=arges?arges:[];
return function newFn(...newArges){
//为什么会有这样的判断后边会有讲,mark一下
if(fn instanceof newFn){
return new fn(...arges,...newArges)
}
return fn.apply(content,[...arges,...newArges])
}
}
先给大家讲解一下call/apply/bind,下面在讲解显示绑定的例子:
function light(){
console.log(this.a)
}
var obj ={
a:2,
}
//强制吧light函数绑定到obj对象上
light.call(obj)//2
//显式绑定call/apply
function light(){
console.log(this.a)
}
var obj ={
a:2,
light:light
}
var bar =obj.ligh.call(obj)//直接返回值所以说显示绑定解决不了隐式丢失的问题
bar()//报错
//硬绑定bind(显式的强制绑定)柯里化
function light(){
//调用栈 wenshu -> light
console.log(this.a)
}
var obj ={
a:2,
light:light
}
//var bar =function(){
//light.call(obj)
//}
//硬绑定之后不能在修改他的this
var bind = obj.light.bind(obj);
bar()
API调用上下文例如forEach等,第二个参数数就是函数内的this指向。
new绑定
function light(a){
this.a = a
}
var bar = new light(2)
console.log(bae.a)//2
//会构造一个新对象并把它绑定到light();调用的this上,成为new 绑定
优先级
在调用位置可以使用多条规则的时候,总会有先后,权重高和低之分。
默认绑定绝对是这里权重最低的我们先看,隐式绑定和显式绑定, 上代码:
//隐式绑定和显示绑定的优先级
function dq(){
console.log(this.a)
}
var obj={
a:'76文书',
dq:dq
}
var obj1={
a:'心里有火,眼里有光',
dq:dq
}
obj.dq();
obj1.dq();
obj.dq.call(obj1);
obj1.dq.call(obj);
从上述代码来判断 显式绑定>隐式绑定
隐式绑定和new绑定谁的会更高一点那??
function dq(params){
this.a = params;
// console.log(this.a)
}
var obj={
a:'76文书',
dq:dq
}
obj.dq(3);
console.log(obj.a);
var bar = new obj.dq('心里有火,眼里有光')
console.log(obj.a)
//new 绑定 和隐式绑定的对象不产生任何关系
console.log(bar.a)
从上述代码来判断
new绑定>隐式绑定
记得new绑定>显示绑定 具体后期会讲
判断this
就可以根据new>显式>隐式>默认的优先级去可以判断this
bug无穷无尽的bug this被忽略。
function dq(){
console.log(this.a)
}
var obj={
a:'76文书',
dq:dq
}
var a = 2;
var ø = Object.create(null);
//使用null可能带来更多无穷无尽的bug,程序的稳定运行才是硬道理
dq.call(null);//默认将会忽略this
dq.call(obj);
//ø比null更空
dq.call(ø);//不会忽略this,只不过是指向的更空了
间接引用
我觉的这种方式使用的函数我还没有见到过,我觉得只是存在理论上;
function dq(){
console.log(this.a)
}
var a = 2;
var o = {a:3, foo:foo};
var p = {a:4};
o.dq();//3
//我觉的一个正常人谁会这么写。。。。,考验自己还是考验后来接手的同学,易读,易用才是好的,不是别人技术菜不菜的问题,是你写的代码干净利落,能读懂,写的代码除了自己别人看不懂那有什么实际的意义那。
(p.dq = o.dq)()//2
软绑定
就是为了能后让绑定的更加灵活,有够指向其他对象: 书上的软绑定的代码
if(!Function.prototype.softBind){
Function.prototype.softBind = function(obj){
const fn = this;
var curried = [].slice.call(arguments,1);
var bound = function(){
return fn.apply({
(!this || this ===(window || global))?obj:this,
curried.concat.apply(curried,arguments)
})
};
bound.prototype = Object.create(fn.prototype);
return bound;
}
}
```javaScript
function foo(){
console.log(`name:${this.name}`)
}
var obj = {name:'obj'},obj2={name:'obj2'},obj3={name:'obj3'};
var fooOBJ = foo.softBind(obj);
fooOBJ(); //name:obj
obj2.foo = foo.softBind(obj);
obj2.foo() //name:obj2
fooOBJ.call(obj3); //name:obj3
//有疑问可以看看隐式绑定
setTimeOut(obj2.foo, 10) //name:obj
***箭头函数 ***
先给出我的总结:
不管在什么情况下,箭头函数的this跟外层的function的this是一致的,外城function的this指向谁,如果外层的不是function则窒息那个window。
function foo(){
setTimeout(()=>{
//这里的this在词法上继承自foo()
console.log(this.a)
},100);
}
var obj = {
a:2
}
foo.call(obj); //2
self = this;
function foo(){
var self = this;
setTimeout(function(){
console.log(self.a);
},100)
}
var obj = {
a:2
}
foo.call(obj); //2
惊讶的问题:
箭头函数中的this是undefined,debugger看到的,不影响正常使用打印consoel.log(this);
出现原因就是chrome调试器优化造成的,如果未在函数内部引用局部变量(就是相当于块级作用域)这里是this,这个变量就不会存储在此函数上下文对象中。
总结的结果就是箭头函数内部的this
,存在的事件与函数的封闭局部变量相同,都是未显示引用输出就是undefined
{
console.log(a)
var a=2
}
this的解析基本上写完了,在这里致谢:
《你不知道的JavaScript(上卷)【美】KYLE SIMPOSON 著 赵望野/梁杰 译》