关键词:
之前一直对this的指向很模糊,找了一些别人的博客看,又重新看了一下《你不知道的JavaScript》,感觉基本上是弄懂了,挑一些重点的地方记录一下,有些地方对我来说书上解释写的不够多,所以自己做下补充以方便理解,有理解错的地方还望指出。
一.澄清误区
首先你需要知道:
1.this并不指向函数自身
2.this的作用域在任何情况下都不指向函数的词法作用域。
举个例子:
function foo() { var a = 2; this.bar(); } function bar() { console.log(this.a); } foo();//ReferenceError: a is not defined
第一次看这里的时候就掉坑里了,想当然的以为foo()中this指向window,而bar()又在全局中故能调用bar(),但实际上这是错的,不能使用this来引用一个词法作用域内部的东西。
this是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里调用。跟函数声明的位置没有任何关系,要区别于函数的作用域,函数的作用域是在它被定义的时候确定的。
要判断一个运行中函数的this绑定,需要找到这个函数的直接调用位置。
二.调用位置
调用位置就是函数在代码中被调用到的位置。
this有四条绑定规则:
1.默认绑定:非严格模式下this指向全局对象,严格模式绑定到undefined。 var bar = foo()
2.隐式绑定:this指向包含它的函数的对象,要注意隐式丢失的情况。 var bar = obj1.foo()
3.显示绑定:this指向call()、apply()和bind()方法指定的对象。 var bar = foo.call(obj2)
4.new绑定:this指向构造的新对象。 var bar = new foo()
优先级:new绑定 > 显示绑定 > 隐式绑定 > 默认绑定
具体看下面:
1.默认绑定
无法应用其他规则时默认使用默认绑定。如果函数独立调用,使用默认绑定。
function foo() {
//看函数体是否处于严格模式,是看这个位置("use strict")
console.log(this.a); } var a = 2; foo(); //2 仅foo()函数本身,为独立调用
在上面的代码中,foo()是使用不带任何修饰的函数引用进行调用的,所以会使用默认绑定,非严格模式下this指向全局对象,严格模式绑定到undefined。不是指调用位置是否处于严格模式,而是函数体是否处于严格模式。
2.隐式绑定
考虑函数的调用位置是否有上下文对象,或者说是否被某个对象拥有或者包含。
function foo() { console.log(this.a); } var obj = { a: 2, foo: foo //foo()被当作引用属性添加到obj中,此时它被obj对象包含,此时this指向obj } obj.foo(); //2
对象属性引用链中只有最后一层会影响调用位置。
function foo() { console.log(this.a); } var obj2 = { a: 42, foo: foo } var obj1 = { a: 2, obj2: obj2 } obj1.obj2.foo(); //42 虽然这里有obj1对象和obj2对象,但是obj2才是处于最后一层最接近foo(),所以会指向obj2
隐式丢失的情况
来看下面的代码:
function foo() { console.log(this.a); } var obj = { a: 2, foo: foo } var bar = obj.foo; //虽然bar是obj.foo的一个引用,但实际上bar引用的只是foo函数本身,可以看成bar() = foo(),此时foo()是独立调用故绑定到全局对象 var a = "oops, global"; bar(); //oops, global
还有一种常见的隐式丢失的情况是传入回调函数
function foo() { console.log(this.a); } function doFoo(fn) { fn(); } var obj = { a: 2, foo: foo } var a = "oops, global"; doFoo(obj.foo); //oops, global obj.foo作为参数传入实际上隐式赋值给了fn,可以看成fn = obj.foo,
//此时又回到了上一例代码的情况,引用的是foo函数本身,看成fn()= foo(),独立调用指向全局对象
3.显示绑定
使用函数的call()和apply()方法可以使用显示绑定,这两个方法的第一个参数都是一个对象,他们会把这个对象绑定到this,在调用函数时指定这个this,因为可以指定绑定对象故称做显示绑定。
function foo() { console.log(this.a); } var obj = { a: 2 } foo.call(obj);
但是这种显示绑定没有解决隐式丢失的问题,要解决这个问题可以使用硬绑定。
function foo() { console.log(this.a); } var obj = { a: 2 } var bar = function() { foo.call(obj); } bar(); //2 setTimeout(bar, 100) //2 bar.call(window); //2
创建了一个函数bar(),并在它的内部手动调用foo.call(obj),因此强制把foo的this绑定到了obj,之后无论如何调用函数bar,它总会手动在obj上调用foo,这叫做硬绑定。
硬绑定非常常用,ES5中提供了内置的方法Function.prototype.bind,bind()会返回一个硬编码的新函数,它会把参数设置为this的上下文并调用原始函数。
硬绑定的两个典型应用场景:
- 创建一个包裹函数,传入所有的参数并返回接收到的所有值
function foo(something) { console.log(this.a, something); return this.a + something; //返回接收到的所有this.a和something } var obj = { a: 2 } var bar = function() { return foo.apply(obj, arguments); //this指向obj,arguments为传入的参数3 } var b = bar(3); //2 3 console.log(b); //5
- 创建一个可以重复使用的辅助函数
function foo(something) { console.log(this.a, something); return this.a + something; } function bind(fn, obj) { return function() { return fn.apply(obj, arguments); }; } var obj = { a: 2 } var bar = bind(foo, obj); var b = bar(3); //2 3 console.log(b); //5
4.new绑定
使用new来调用函数,或者说发生构造函数调用时,会执行下面的操作:
①创建(构造)一个全新的对象。
②这个新对象会被执行[[原型]]连接。
③这个新对象会绑定到函数调用的this。
④如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象。
function foo(a) { this.a = a; } var bar = new foo(2);//使用new操作符调用foo(),构造了一个新对象bar并把它绑定到foo()的this上 console.log(bar.a); //2
三.绑定例外
有一些例外情况需要注意
1.把null或undefined作为this的绑定对象传入call、apply或bind。
null和undefined在调用时会被忽略,然后应用默认绑定。
function foo() { console.log(this.a); } var a = 2; foo.call(null);//2
有时你可能选择null作为一个占位值而选择null作为参数,但是总是用null来忽略this的绑定可能产生一些副作用。一种更安全的做法是传入一个空的非委托对象,把this绑定到这个对象不会对你的程序产生任何副作用。
2.无意间创建了一个函数的“间接引用”
同样会应用默认绑定,最容易在赋值时发生。
function foo() { console.log(this.a); } var a = 2; var o = { a: 3, foo: foo } var p = { a: 4 } o.foo();//3 (p.foo = o.foo);//2 p.foo = o.foo的返回值是目标函数的引用,故调用的位置是foo(),又会应用默认绑定
3.软绑定 softBind()
硬绑定会降低函数的灵活性,使用硬绑定后就无法使用隐式绑定或显示绑定来修改this。
软绑定:给默认绑定指定一个全局对象和undefined以外的值,可以实现和硬绑定相同的效果,同时保留隐式绑定或显示绑定修改this的能力。
除了软绑定外,softBind()的其他原理和bind()类似。首先检查调用时的this,如果this绑定到全局对象或者undefined,就把指定的对象绑定到this,否则不会修改this。
function foo() { console.log("name:" + this.name); } var obj = { name: "obj" } var obj2 = { name: "obj2" } var obj3 = { name: "obj3" } var fooOBJ = foo.softBind(obj); fooOBJ();//obj obj2.foo = foo.softBind(obj); obj2.foo();//obj2 obj2.foo()调用时,this绑定到obj2上,不是全局对象也不是undefined,所以不会调用指定的obj,而是使用原来的obj2。 fooOBJ.call(obj3);//硬绑定时绑定了obj3,后面不可再修改 setTimeout(obj2.foo, 10);//假设setTimeout(fn,10),fn实际上引用的只是foo(),可以看成fn = obj2.foo,类似前面的隐式绑定丢失,
//会使用默认绑定到全局,这时会发生软绑定,绑定到指定对象obj
4.箭头函数
箭头函数无法使用this中的四种绑定规则,而是根据外层(函数或全局)作用域来决定this。
function foo() { return (a) => { console.log(this.a); } } var obj1 = { a: 2 } var obj2 = { a: 3 } var bar = foo.call(obj1); bar.call(obj2);//2 箭头函数会捕获调用时foo()的this,foo()的this会显示绑定到obj1,
//bar(引用箭头函数)的this也会绑定到obj1,箭头函数的绑定无法被修改
箭头函数常用于回调函数中,例如事件处理器或者定时器
function foo() { setTimeout( () => { console.log(this.a);//箭头函数会继承外层函数调用的this绑定,这里箭头函数的外层函数是foo(),foo()的this绑定到obj,所以箭头函数的this也绑定到obj。 }, 100); } var obj = { a: 2 } foo.call(obj);//2
学习笔记---js基础类
JavaScript中的this!https://qiutc.me/post/this-this-this-in-javascript.html#call,_apply,_bind讲解了JS中this在特殊情况下的变化。以及如何保持this值。 查看详情
javascript高级编程笔记06(面相对象2)
1) 构造函数模式es中的构造函数可以用来创建特定类型的对象,像Object和Array这样的原生构造函数,在运行时会自动出现在执行环境中,此外,也可以创建自定义的构造函数,从而定义自定义对象类型的属性和方法,例如:functionPerson(na... 查看详情
javascript学习笔记——this原型javascript面向对象
一、this在JavaScript中this表示:谁调用它,this就是谁。JavaScript是由对象组成的,一切皆为对象,万物皆为对象。this是一个动态的对象,根据调用的对象不同而发生变化,当然也可以使用call、apply修改this指向的对象。它代表函数... 查看详情
javascript对象(复习笔记)
js对象对象构造器functionperson(firstname,lastname,age,eyecolor){ this.firstname=firstname; this.lastname=lastname; this.age=age;this.eyecolor=eyecolor;}myFather=newperson("John","Doe",50,"blue"); 或者person={f 查看详情
javascript面向对象学习笔记
面向对象:变量和属性一样的。属性:属于一个对象变量:属于任何一个人函数:方法:属于一个对象难点:this:当前的方法属于谁,this就是谁的例子window.show=function(){ alert(this);}//this是windowfunctionshow(){ alert(this);}Object:用来实现对... 查看详情
js日常笔记之this
在javascript中自己创建构造函数时可以利用this来指向新创建的对象上。这样就可以避免函数中的this指向全局了,如下 varx=2; functiontest(){ this.x=1; } varo=newtest(); alert(x);//2 反之,如果不使用new,只把上面... 查看详情
《javascript设计模式与开发》笔记2.this指针
1.作为对象方法调用 2.作为普通函数调用1.作为普通函数进行调用2.将函数赋值给一个对象3.callback内部的this指向了window4.解决callback内部的this问题 3.构造器调用1.普通构造器2.如果构造器显示地返回了一个object类型对象... 查看详情
jquery中的$(this)和javascript中的this
this是JavaScript中的关键字。 $(this)可以认为是用jQuery包装过JavaScript中的this,包装后$(this)就会继承jQuery的方法。本质就是JavaScript与jQuery对象的转换$(‘a‘).click(function(){//这里的this指向当前点击的DOM节点,也就是a。可以调... 查看详情
javascript中的this作用域
javaScript中的this作用域 javaScript中的this作用域java的区别是,java中的this是在编译中确定, javaScript中的this是在运行时确定的,不同的调用方式,决定js中的this指向不同的对象。 代码实现: //this作用域... 查看详情
javascript跟aaron大神学习jquery源码笔记
/*通过new操作符构建一个对象,一般经过四步:A.创建一个新对象B.将构造函数的作用域赋给新对象(所以this就指向了这个新对象)C.执行构造函数中的代码D.返回这个新对象最后一点就说明了,我们只要返回一个新对象即可。其... 查看详情
javascript面向对象的程序设计记录笔记4
组合使用构造函数模式和原型模式: functionPerson(name,age,job){this.name=name;this.age=age;this.job=job;this.friends=[‘Shelby‘,‘Court‘];}Person.prototype={connstructor:Person,sayName:function(){returnthis.name;}}varp 查看详情
javascript基础笔记总结(全部)(代码片段)
...函数16.5函数做为参数16.6函数作用域16.7函数的作用域链17.JavaScript对象18.this用法18.1指向对象中的this18.2指向全局对象的this18.3this的指向19.对象的遍历和删除20.包装对象21.数学对象(标准库对象,内置对象)22.日期对象2... 查看详情
详解javascript中的this
JavaScript中的this总是让人迷惑,应该是js众所周知的坑之一。个人也觉得js中的this不是一个好的设计,由于this晚绑定的特性,它可以是全局对象,当前对象,或者…有人甚至因为坑大而不用this。其实如果完全掌握了this的工作原... 查看详情
详解javascript中的this
JavaScript中的this总是让人迷惑,应该是js众所周知的坑之一。个人也觉得js中的this不是一个好的设计,由于this晚绑定的特性,它可以是全局对象,当前对象,或者…有人甚至因为坑大而不用this。其实如果完全掌握了this的工作原... 查看详情
javascript中的this
JavaScript中的this指向问题在JavaScript中,this是经常使用的一个关键字,按照规定,this指向调用者,即谁调用该对象,则this指向谁,但是this存在一个缺陷,当在一个函数中定义另一个函数时,内部函数的this不会指向外部函数,而... 查看详情
读书笔记《你不知道的javascript(上卷)》——第二部分this和对象原型(代码片段)
文章目录第6章行为委托6.1面向委托的设计6.1.1类理论6.1.2委托理论1.互相委托(禁止)2.调试6.1.3比较思维模型6.2类与对象6.2.1控件“类”ES6的class语法糖6.2.2委托控件对象6.3更简洁的设计反类6.4更好的语法反词法6... 查看详情
javascript中的this陷阱的最全收集
JavaScript来自一门健全的语言,所以你可能觉得JavaScript中的this和其他面向对象的语言如java的this一样,是指存储在实例属性中的值。事实并非如此,在JavaScript中,最好把this当成哈利波特中的博格特的背包,有着深不可测的魔力... 查看详情
javascript语言中的this
JavaScript语言中的this由于其运行期绑定的特性,JavaScript中的this含义要丰富得多,它可以是全局对象、当前对象或者任意对象,这完全取决于函数的调用方式。JavaScript中函数的调用有以下几种方式:作为对象方法调用,作为函数... 查看详情